Redis从入门到放弃系列(三) List
Redis从入门到放弃系列(三) List
本文例子基于:5.0.4 List是Redis中一种比较常见的数据结构,其实现为quicklist,quicklist是一个ziplist的双向链表
首先让我们来看一下该如何在redis里面使用List类型
//设置key的列表为value lpush key value [value...]
代码示例:
//栈的用法,rpush rpop一样~ 通过rpush,lpop相当于堆的用法 > lpush books java python c (integer) 3 > lpop books "c" > lpop books "python" > lpop books "java" ---------------------------------- //返回列表key指定区间的元素,区间偏移量start跟stop指定 //start跟stop的下表都是以0为底 > lrange books 0 2 1) "c" 2) "python" 3) "java" ---------------------------------- //ltrim可以作为一个定长的list,每次都可以获取到最新的2条数据 > lpush books java python c c++ (integer) 4 > ltrim books 0 1 OK > lrange books 0 -1 1) "c++" 2) "c" ---------------------------------- //当给定列表内没有任何元素可供弹出的时候,连接将被blpop ,brpop命令阻塞,直到等待超时或发现可弹出元素为止。 //设置超时 1秒 > BLPOP books 1 1) "books" 2) "c++" > BLPOP books 1 1) "books" 2) "c" > BLPOP books 1 (nil) (1.05s) ----------------------------------
至此,redis list的用法先告一段落.
源码解析
本文开头的时候讲list实现为quicklist,quicklist是一个ziplist的双向链表,那么其内部结构是怎样的呢?
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist. * 'count' is the number of total entries. * 'len' is the number of quicklist nodes. * 'compress' is: -1 if compression disabled, otherwise it's the number * of quicklistNodes to leave uncompressed at ends of quicklist. * 'fill' is the user-requested (or default) fill factor. */ typedef struct quicklist { quicklistNode *head; quicklistNode *tail; unsigned long count; /* total count of all entries in all ziplists */ unsigned long len; /* number of quicklistNodes */ int fill : 16; /* fill factor for individual nodes */ unsigned int compress : 16; /* depth of end nodes not to compress;0=off */ } quicklist; /* quicklistNode is a 32 byte struct describing a ziplist for a quicklist. * We use bit fields keep the quicklistNode at 32 bytes. * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k). * encoding: 2 bits, RAW=1, LZF=2. * container: 2 bits, NONE=1, ZIPLIST=2. * recompress: 1 bit, bool, true if node is temporarry decompressed for usage. * attempted_compress: 1 bit, boolean, used for verifying during testing. * extra: 10 bits, free for future use; pads out the remainder of 32 bits */ typedef struct quicklistNode { struct quicklistNode *prev; struct quicklistNode *next; unsigned char *zl; unsigned int sz; /* ziplist size in bytes */ unsigned int count : 16; /* count of items in ziplist */ unsigned int encoding : 2; /* RAW==1 or LZF==2 */ unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */ unsigned int recompress : 1; /* was this node previous compressed? */ unsigned int attempted_compress : 1; /* node can't compress; too small */ unsigned int extra : 10; /* more bits to steal for future usage */ } quicklistNode; /* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'. * 'sz' is byte length of 'compressed' field. * 'compressed' is LZF data with total (compressed) length 'sz' * NOTE: uncompressed length is stored in quicklistNode->sz. * When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */ typedef struct quicklistLZF { unsigned int sz; /* LZF size in bytes*/ char compressed[]; } quicklistLZF;
从上面我们可以知道,quicklist是一个的双向链表,所以当我们使用lpush,rpop等操作是O(1)了。
ziplist本身也是一个能够维持数据先后顺序的列表(按照插入位置),而且是一个内存紧凑的列表。 当我们要表示list拥有12个数据项,这时候就会有可能有多种选择了,例如3个节点的quicklist,每个节点ziplist又包含4个数据项.或者2个节点的quicklist,每个节点ziplist又包含6个数据项 那么redis是如何选择的呢?我们可以再redis.conf找到蛛丝马迹~
# Lists are also encoded in a special way to save a lot of space. # The number of entries allowed per internal list node can be specified # as a fixed maximum size or a maximum number of elements. # For a fixed maximum size, use -5 through -1, meaning: # -5: max size: 64 Kb <-- not recommended for normal workloads # -4: max size: 32 Kb <-- not recommended # -3: max size: 16 Kb <-- probably not recommended # -2: max size: 8 Kb <-- good # -1: max size: 4 Kb <-- good # Positive numbers mean store up to _exactly_ that number of elements # per list node. # The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), # but if your use case is unique, adjust the settings as necessary. list-max-ziplist-size -2 # Lists may also be compressed. # Compress depth is the number of quicklist ziplist nodes from *each* side of # the list to *exclude* from compression. The head and tail of the list # are always uncompressed for fast push/pop operations. Settings are: # 0: disable all list compression # 1: depth 1 means "don't start compressing until after 1 node into the list, # going from either the head or tail" # So: [head]->node->node->...->node->[tail] # [head], [tail] will always be uncompressed; inner nodes will compress. # 2: [head]->[next]->node->node->...->node->[prev]->[tail] # 2 here means: don't compress head or head->next or tail->prev or tail, # but compress all nodes between them. # 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] # etc. list-compress-depth 0
list-max-ziplist-size
当设置为正数意味着最多只能储存该数量的元素,redis的作者建议设置为-1 or -2,设置每个quicklist节点上的ziplist能储存元素的大小~ 当列表很长的时候,中间的数据被访问的频率就有可能很低,那么在这种情况下,list提供了一个参数能够将中间的数据压缩~
list-compress-depth 0
这个参数表示quicklist两端不被压缩的节点数.head节点跟tail节点总是不压缩的,方便在list的两端进行快速存取
- 0: 是个特殊值,表示都不压缩。这是Redis的默认值。
- 1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。
- 2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。
- 3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。
- 依此类推…
quickList结构图如下图所示:
图中对应的ziplist的配置大小和节点压缩深度配置如下:
list-max-ziplist-size 3 list-compress-depth 1
在这里例子中我们可以看到,quickList两端各有一个节点没有被压缩,它们的数据指针指向真正的ziplist(即zl的指向).中间的其他节点是被压缩过的,它们的数据指针指向quicklistLZF
应用场景
1.消息队列(无ack机制)
//生产者使用lpush将消息放入list中,消费者就可以通过rpop取出该消息,并且可以保证消息的有序性。 >lpush message "ces" (integer) 1 >rpop message "ces"
2.时间轴
//一种场景就是当用户发送一条微博,通过lpush将它存放到list中,然后通过lrange就可以取出最近的最新的微博信息了 > lpush weibo "xiaoxi1" (integer) 1 > lpush weibo "xiaoxi2" (integer) 2 > lpush weibo "xiaoxi3" (integer) 3 > lrange weibo 0 9 1) "xiaoxi3" 2) "xiaoxi2" 3) "xiaoxi1"
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
浅谈Java【代理设计模式】——看这篇文章就懂了
写在前面:设计模式源于生活,而又高于生活! 什么是代理模式 为其他对象提供一种代理以控制对这个对象的访问。 为什么使用代理模式 中介隔离:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。 代理模式实现原理 代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy) 抽象主题角色(Subje...
- 下一篇
linux内核协议栈TCP time_wait原理、配置、副作用
0. 手把手教你做中间件、高性能服务器、分布式存储技术交流群 手把手教你做中间件、高性能服务器、分布式存储等(redis、memcache、nginx、大容量redis pika、rocksdb、mongodb、wiredtiger存储引擎、高性能代理中间件),git地址如下: git地址:https://github.com/y123456yz/middleware_development_learning 1. time_wait状态产生条件 只有在正常四次挥手关闭连接的情况下,在主动关闭连接的一方会出现一段时间的time_wait。如果启用了快速回收功能,回收时间和网络延迟状况有关,正常情况下小于1s,如果没有开启time_wait快速回收功能,则time_wait回收时间默认60s。 三次挥手过程(FIN+ACK, FIN+ACK,ACK)的情况,例如杀掉一段进程,第一个发送FIN+ACK的一端也会产生time_wait。 2. Time_wait状态相关参数说明 TCP中有和time_wait状态相关的参数有以下四个: tcp_tw_recycle 表示开启TCP连接中ti...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8编译安装MySQL8.0.19
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- 设置Eclipse缩进为4个空格,增强代码规范