Redis常用数据类型有字符串String、字典dict、列表List、集合Set、有序集合SortedSet,本文将简单介绍各数据类型及其使用场景,并重点剖析有序集合SortedSet的实现。
List的底层实现是类似Linked List双端链表的结构,而不是数组,插入速度快,不需要节点的移动,但不支持随机访问,需要顺序遍历到索引所在节点。List有两个主要的使用场景:
- 记住用户最新发表的博文,每次用户发表了文章,将文章id使用LPUSH加入到列表中,用户访问自己的主页时,使用LRANGE 0 9获取最新10条博文(使用LTRIM 0 9可以取出最新10条文章的同时,删除旧的文章),而不用使用order by sql语句去后端数据库取数据。
- 生产者/消费者模式,生产者往List中加入数据,消费者从List中取数据。当List为空时,消费者rpop返回为NULL,这是会进行轮询,等待一段时间继续去取。轮询模式有如下缺点:
- 客户端和redis耗费cpu和网络带宽等资源执行无效命令。
- 取回NULL后,sleep会使有新数据时,客户端消费不够及时。
为了解决轮询的问题,Redis提供了brpop和blpop实现Blocking读,当List为空时,等待一段时间再返回,当有数据时,按请求顺序返回给各客户端。(当List为空时,可以将请求Blocking读命令的客户端加入此List的Blocking读列表中,有数据时按列表序返回)
集合Set的底层实现是类似Hash,不过value全为NULL,set有求并、交、差集及随机取的功能。使用场景如下:
- 表示对象之间的联系,比如求拥有标签1、2、10的新闻,使用sinter tag:1:news tag:2:news tag:10:news。
- 随机发牌,使用spop,spop随机返回集合中的元素,比如给每位玩家发五张牌,每位玩家调用五次spop即可,为了下次发牌不需要再将牌加入set,可以在这次发牌前调用sunionstore将牌复制。
有序集合SortedSet(t_zset.c),集合中的每个值都带有分数,按分数排序,底层实现较为复杂,用到了ziplist、skiplist和dict数据结构,后文将进行详细介绍。使用场景如下:
- 排行榜问题,比如游戏排行榜,按用户分数排序,并取top N个用户。
在redis中,所有数据类型都被封装在一个redisObject结构中,用于提供统一的接口,结构如下表1:
表1 redisObject
redisObject源码(server.h) |
typedef struct redisObject { unsigned type:4;//对象类型,用于分辨字符串、列表、
|
有序列表有压缩列表ziplist和跳表skiplist两种实现方式,通过encoding识别,当数据项数目小于zset_max_ziplist_entries(默认为128),且保存的所有元素长度不超过zset_max_ziplist_value(默认为64)时,则用ziplist实现有序集合,否则使用zset结构,zset底层使用skiplist跳表和dict字典。创建有序集合的关键代码如下表2:
表2 创建有序集合
zaddGenericCommand函数 |
if (server.zset_max_ziplist_entries == 0 || server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr)) { zobj = createZsetObject(); //创建zset } else { zobj = createZsetZiplistObject();//创建ziplist }
|
ziplist是一个内存连续的特殊双向链表LinkList,减少了内存碎片和指针的占用,用于节省内存,但对ziplist进行操作会导致内存的重新分配,影响性能,故在元素较少时用ziplist。ziplist内存布局如下:
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>
表3 ziplist在内存中各字节含义
Field |
含义 |
zlbytes(uint32_t) |
ziplist占用的内存字节数,包括zlbytes本身 |
zltail(uint32_t) |
最后一个entry的offset偏移值 |
zllen(uint16_t) |
数据项entry的个数 |
entry(变长) |
数据项 |
zlend(uint8_t) |
标识ziplist的结束,值为255 |
数据项entry的内存结构如下:<prevlen> <encoding> <entry-data>,当保存的是小整型数据时,entry没有entry-data域, encoding本身包含了整型元素值。Entry各字节含义如下表4:
表4 entry各Field含义
Field |
含义 |
prevlen |
上一个数据项entry的长度。当长度小于254字节,则prevlen占1字节,当长度大于或等于254字节,则prevlen占5字节,首字节值为254,剩下4字节表示上一entry长度。 |
encoding |
encoding的值依赖于数据entry-data。首字节的前两个bit为00、01、10,标识entry-data为字符串,同时表示encoding的长度分别为1、2、5字节,除前两个bit,剩下的bit表示字符串长度;前两个bit为11,表示entry-data为整型,接下来的2 bit表示整数类型。entry-data不同类型及encoding如下: 1) |00pppppp| - 1 byte,字符串且长度小于等于63字节(6bit) |
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5 | |
【大 中 小】【打印】 【繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部】 | |
上一篇:高性能索引策略二 | 下一篇:MySQL----navicat for mysql(破.. |