Redis简介REmoteDIctionaryServer(Redis)是由SalvatoreSanfilippo编写的键值存储系统。Redis是开源的,用ANSIC语言编写,遵守BSD协议,支持网络,可以是基于内存的也可以是持久化的,日志型,Key-Value数据库,提供多种语言的API。它常被称为数据结构服务器,因为值(value)可以是字符串(String)、散列(Map)、列表(list)、集合(sets)和有序集合(sortedsets)等类型。Redis的特点Redis完全开源免费,遵循BSD协议,是一个高性能的key-value数据库。Redis和其他key-value缓存产品有以下三个特点:Redis支持数据持久化,可以将内存中的数据保存在磁盘上,重启时可以再次加载使用。Redis不仅支持简单的key-value类型数据,还提供list、set、zset、hash等数据结构的存储。Redis支持数据备份,即主从模式的数据备份。Redis优势高性能——Redis的读速度可达11万次/s,写速度可达81000次/s。丰富的数据类型——Redis支持Strings、Lists、Hashes、Sets和OrderedSets数据类型操作。原子性——Redis的所有操作都是原子的,Redis也支持所有操作合并后原子执行。丰富的特性——Redis还支持发布/订阅、队列、key过期等特性。Redis对象类型简介Redis是一个键/值数据库,其中每个键和值都由一个对象表示。例如,我们执行如下代码:redis>SETmessage"helloredis"其中key为message,是一个包含字符串“message”的对象。value是一个包含“helloredis”的对象。Redis中有五种类型的对象,分别是:类型常量对象的名称REDIS_STRING字符串对象REDIS_LIST列表对象REDIS_HASH哈希对象REDIS_SET集合对象REDIS_ZSET有序集合对象Redis中对象的结构表示为如下:typedefstructredisObject{//Typeunsignedtype:4;//编码方式unsignedencoding:4;//引用计数intrefcount;//指向对象的值void*ptr;}robj;type表示对象的对象类型,即是以上五种之一。但是,为了提高存储效率和程序执行效率,每个对象的底层数据结构实现可能不止一种。encoding指示底层对象使用的编码。Redis对象底层数据结构编码常量编码所对应的底层数据结构REDIS_ENCODING_INTlong类型的整数REDIS_ENCODING_EMBSTRembstr编码的简单动态字符串REDIS_ENCODING_RAW简单动态字符串REDIS_ENCODING_HT字典REDIS_ENCODING_LINKEDLIST双端链表REDIS_ENCODING_ZIPLIST压缩列表REDIS_ENCODING_INTSET整数集合REDIS_ENCODING_SKIPLIST跳跃表和字典字符StringobjectTheencodingofastringobjectcanbeint,raworembstr.Ifthecontentofastringcanbeconvertedtolong,thenthestringwillbeconvertedtolongtype,andtheptroftheobjectwillpointtothelong,andtheobjecttypewillalsobeRepresentedbyinttype.Therearetwocommonstrings,embstrandraw.embstrshouldbeanewdatastructureinRedis3.0,whichisnotavailablein2.8.Ifthelengthofthestringobjectislessthan39bytes,usetheembstrobject.Otherwiseatraditionalrawobjectisused.#defineREDIS_ENCODING_EMBSTR_SIZE_LIMIT44robj*createStringObject(char*ptr,size_tlen){if(len<=REDIS_ENCODING_EMBSTR_SIZE_LIMIT)returncreateEmbeddedStringObject(ptr,len);elsereturncreateRawStringObject(ptr,len);}embstr的好处如下:对于sds分配对象,另一个对于objet分配对象,embstr第一次保存)。相应的,释放内存的次数也从2次变成了1次。把embstr的objet和sds放在一起,可以更好的利用cache带来的优势。raw和embstr的区别如下两张图所示:Listobjectslist对象的编码可以是ziplist或linkedlist。Ziplist是一个压缩链表。它的好处是可以节省内存空间,因为它存储的内容都在一块连续的内存区域。当列表对象元素不大,每个元素也不大时,使用ziplist来存储,但是当数据量太大时,ziplist就不那么好用了。因为为了保证其存储内容在内存中的连续性,插入的复杂度为O(N),即realloc每次插入都会重新分配。如下图所示,对象结构中ptr指向的是一个ziplist。整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。linkedlist是一个双向链表。它的结构比较简单。节点中存储了pre和next两个指针,以及节点相关的信息。每增加一个节点,都需要重新分配一块内存。哈希对象哈希对象的底层实现可以是ziplist或者hashtable。ziplist中的hash对象按照key1、value1、key2、value2的顺序存储。当对象数量少,内容不大时,这种方法非常有效。Hashtable是通过dict的结构实现的,dict是一个字典,指针dichtht[2]指向两个哈希表typedefstructdict{dictType*type;void*privdata;dictththt[2];longrehashidx;/*rehashingnotinprogressifrehashidx==-1*/initerators;/*numberofiteratorscurrentlyrunning*/}dict;typedefstructdict{dictEntry**table;unsignedlongsize;unsignedlongsizemask;unsignedlongused;}dictht;dicht[0]是用来实际存储数据的,dicht[1]一般是在??里面用的当哈希表中的元素太多需要重新哈希时传输数据。dicttht中的tableterm实际存储的是元素,每一个key/value对用一个dictEntry表示,放在dictEntry数组中。集合对象的编码可以是intset或hashtable。intset是一个整数集合,存储相同类型的整数,支持以下三种长度的整数:orderedset,查找元素的复杂度是O(logN),但是插入的时候不一定是O(logN),因为可能涉及升级操作。例如,当集合中全是int16_t类型的整数时,此时需要插入一个int32_t。为了保持集合中数据类型的一致性,所有数据都会被转换为int32_t类型,这涉及到内存的重新分配。此时插入的复杂度为O(N)。intset不支持降级操作。有序集合对象的有序集合有两种可能的编码,一种是ziplist,另一种是skiplist和dict的结合。Ziplist与集合和哈希对象相同,成员和分数是顺序存储的。将skiplist按照score从小到大的顺序排列是skiplist的一种,实现了在有序集合中的快速搜索,在大多数情况下其速度可以与平衡树相近。但它的实现比较简单,可以作为平衡树的替代品。它的结构比较特殊。下面是跳跃列表skiplist及其内部节点skiplistNode的结构:/**skiplist*/typedefstructzskiplist{//头节点,尾节点structzskiplistNode*header,*tail;//节点数unsignedlonglength;//在currenttable***layernumberintlevelofthenode;}zskiplist;/*ZSETsuseaspecializedversionofSkiplists*//**skiplistnode*/typedefstructzskiplistNode{//memberobjectrobj*obj;//scoredoublescore;//向后指针structzskiplistNode*backward;//layerstructzskiplistLevel{//前向指针structzskiplistNode*forward;//本层跨越的节点数unsignedintspan;}level[];}zskiplistNode;head和tail分别指向头节点和尾节点,然后每个skiplistNode内部的结构是Hierarchical(即层级数组)用图来表示的,大致如下:总结上面简单介绍了介绍,特点,以及Redis的五种对象类型和五种对象类型的底层实现。事实上,Redis的高效和灵活是由于同一个对象类型使用不同的底层结构,并在必要时将两者进行转换,以及各种底层结构对内存的合理使用。
