redis 六. list应用场景及底层分析

一. 简单命令示例

1.首先简单说明: List是一个双端链表的结构,内容是2的32次方减1个元素,大概40多亿,主要功能有push/pop等,一般用在栈,队列,消息队列等场景
2.简单命令

//1.向列表左边添加元素
LPUSH key value[value...]
//2.向列表右边添加元素
RPUSH key value[value...]
//3.查看列表
LRANGE key start stop
//4.获取列表中的元素个数
LLEN key

二. java 操作示例

  1. 操作 List 列表类型,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),通过list进行指定下标的查询,可以做到简单分页
	@Test
    public void test03(){
        //1.创建操作List类型数据的对象
        ListOperations<String ,String> listOperations= stringRedisTemplate.opsForList();

        //2.左上添加,第一个添加的会被后一个添加的挤压到下面
        listOperations.leftPush("student","liuliuliu");
        //3.右下追加,第一个添加的会被后一个添加的挤到上面
        listOperations.rightPush("students", "Zhao Liu");

        //4.查询,根据key与下标获取指定位置的数据,左闭右闭,两边包含,返回一个List
        List<String> students = listOperations.range("student", 0,2);

        //5.根据key与指定下标获取单个数据
        String stu = listOperations.index("student", 1);

        //6.获取当前List类型数据的长度
        Long total = listOperations.size("student");

        //7.删除List类型数据,key为student的,value值为"Li",在List中第二次出现的,
        listOperations.remove("student", 2, "Li");

        // 删除多条,有左删除一条,右删除一条等
        stringRedisTemplate.delete("student");
        //8.从左边开始获取并删除
        String srt1 = listOperations.leftPop("student");
        //9.右边开始获取并删除
        String str2= listOperations.rightPop("student");
    }

三. 使用场景

  1. 公众号订阅: 假设用户a订阅了某个公众号sss,当sss公众号发布新文章,就会push到a用户的list中
//发布文章命令
LPUSH (like文章模块+a用户id) sss公众号文章id, bbb公众号文章id
//分页
LRANGE (like文章模块+a用户id) 0 9
  1. 商品评论列表: 一个商品会被不同用户评论,保存评论是按照先后顺序排序,查询商品时按照时间逆序排序,list存储时,key是商品id,value是评论信息:商品编号
LPUSH (品id)key 评论者id:评论信息

四. 底层分析

  1. list底层有linkedList、zipList和quickList三种存储方式
  2. 当列表对象保存的所有字符串元素的长度都小于 list-max-ziplist-value 默认 64字节, 并且保存的元素数量小于list-max-ziplist-entries默认512个时采用zipList, 否则采用linkedlist
  3. linkedlist: redis中自建了listNode对象, 通过内部的prev 和 next 指针组成的一个双端链表,
  4. 在redis 在 3.2 版本时,考虑到redis的空间存储效率和时间效率,引入了quicklist快速列表作为 list 的底层实现可以看成ziplist+linkedList实现的一个双端链表,链表中的每一个节点都以压缩列表ziplist的结构保存着数据,而ziplist有多个entry节点,保存着数据。相当于一个quicklist节点保存的是一片数据,而不再是一个数据
  5. redis中封装了quicklist 结构体变量, 内部通过quicklistNode 保存每一个节点数据,
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 : QL_FILL_BITS;              /* fill factor for individual nodes */
    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
    unsigned int bookmark_count: QL_BM_BITS;
    quicklistBookmark bookmarks[];
} quicklist;
typedef struct quicklistNode {
	//上一个node节点
    struct quicklistNode *prev; 
    //下一个node
    struct quicklistNode *next; 
    //保存的数据 压缩前ziplist 压缩后压缩的数据
    unsigned char *zl;   
    //表示zl执行的ziplist的总大小,注意如果ziplist被压缩了,这个sz的值仍然时压缩前的大小         
    unsigned int sz;            
    //表示ziplist包含的数据个数,16bit
    unsigned int count : 16;     
    //表示ziplist是否压缩,1表示没有,2表示压缩了
    unsigned int encoding : 2;   
    //预留字段,当前是一个固定值2,表示使用zplist作为数据容器
    unsigned int container : 2;  
   //解压标记1, 通过该标记可以再次压缩
    unsigned int recompress : 1;
    unsigned int attempted_compress : 1; 
    unsigned int extra : 10; 
} quicklistNode;
  1. 使用quicklist 插入数据时可以头部插入,或者尾部插入,
  1. 如果头节点(或尾节点) 上ziplist大小没有超过限制(即_quicklistModeAlLowEInsert 返回1),那么新数据被直接插入到ziplist中(调明ziplisteush ) 。
  2. 如果头节点(或尾节点)上ziplist太大了,那么新创建一个quicklistlode节点(对应地也会新创建一个ziplist),然后把这个新创建的节点插入到quicklist双向链表中
  1. quicklist 在任意指定位置插入数据时
  1. 当插入位置所在的ziplist大小没有超过限制时,直接插入到ziplist中
  2. 当插入位置所在的ziplist大小超过了限制,但插入的位置位于ziplist两端,并且相邻的quicklist链表节点的ziplist大小没有超过限制,那么就转而插入到相邻的那个quicklist链表节点的ziplist中;
  3. 当插入位置所在的ziplist大小超过了限制,但插入的位置位于ziplist两端,并且相邻的Squicklist链表节点的ziplist大小也超过限制,这时需要新创建一个quicklist链表节点插入。
  4. 对于插入位置所在的ziplist大小超过了限制的其它情况(主要对应于在ziplist中间插入数据的情况),则需要把当前ziplist分裂为两个节点,然后再其中一个节点上插入数据
  1. quicklist 查找: quicklist的节点是由一个一个的ziplist构成的每个ziplist都有大小,所以先根据每个node个数,找到对应的ziplist,调用ziplist的index就能成功找到。
  2. quicklist 删除: 在区间删除时,会先找到start 所在的 quicklistlode,计算删除的元素是否小于要删除的count,如果不满足删除的个数,则会移动至下一个quicklistNode 继续删除,依次循环直到删除完成为止
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值