NoSQL数据库(二)03-Redis数据类型——列表类型之介绍、命令-向列表两端增加元素、从列表两端弹出元素、获取列表中元素的个数、删除列表中指定的值

NoSQL数据库(二)03-Redis数据类型——列表类型之介绍、命令-向列表两端增加元素、从列表两端弹出元素、获取列表中元素的个数、删除列表中指定的值

列表类型

介绍

列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。

列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的(和从只有20个元素的列表中获取头部或尾部的10条记录的速度是一样的)。不过使用链表的代价是通过索引访问元素比较慢。

和js的数组有什么区别?

  • 在js里面数组其实也是对象,字典
  • redis的列表却是链表

命令

  1. 向列表两端增加元素

LPUSH key value [value …]

RPUSH key value [value …]

redis> LPUSH numbers 1 
(integer) 1
  1. 从列表两端弹出元素

LPOP key

RPOP key

  1. 获取列表中元素的个数

LLEN key

  1. 获取列表片段

LRANGE key start stop

LRANGE 0 -1 可以获取列表中所有的元素

  1. 删除列表中指定的值

LREM key count value

LREM命令会删除列表中前count个值为value的元素,返回值是实际删除的元素个数。根据count值的不同,LREM命令的执行方式会略有差异。

(1)当 count > 0时 LREM 命令会从列表左边开始删除前 count 个值为 value的元素。

(2)当 count < 0时 LREM 命令会从列表右边开始删除前|count|个值为 value 的元素。

(3)当 count = 0是 LREM命令会删除所有值为 value的元素。例如:

ex:储存文章id列表

新增需求: 我们存储的文章需要展示许多条,如何做分页功能。

为了解决这个问题,我们使用列表类型键posts:list记录文章ID列表。当发布新文章时使用LPUSH命令把新文章的ID加入这个列表中,另外删除文章时也要记得把列表中的文章ID 删除,就像这样:LREM posts:list 1 要删除的文章 ID

有了文章 ID列表,就可以使用 LRANGE命令来实现文章的分页显示了。伪代码如下:

var redis = require('redis');
var client = new redis({
    
    
    // 配置
});

var currentPage = 1;

var listLength = 10;
var start = (currentPage - 1) * listLength;
var end = currentPage * listLength - 1;
var postIdArr = client.lrange(`post:list ${
      
      start} ${
      
      end}`);

postIdArr.forEach(id => {
    
    
    client.hgetall(`post:${
      
      id}`, (data) => {
    
    
        console.log(data)
    });
});

美中不足的一点是散列类型没有类似字符串类型的 MGET命令那样可以通过一条命令同时获得多个键的键值的版本,所以对于每个文章ID都需要请求一次数据库,也就都会产生一次往返时延(round-trip delay time)[11] ,之后我们会介绍使用管道和脚本来优化这个问题。

另外使用列表类型键存储文章ID列表有以下两个问题。

(1)文章的发布时间不易修改:修改文章的发布时间不仅要修改post:文章ID中的time字段,还需要按照实际的发布时间重新排列posts:list中的元素顺序,而这一操作相对比较繁琐。

(2)当文章数量较多时访问中间的页面性能较差:前面已经介绍过,列表类型是通过链表实现的,所以当列表元素非常多时访问中间的元素效率并不高。

ex: 存储评论列表

在博客中还可以使用列表类型键存储文章的评论。由于博客不允许访客修改自己发表的评论,而且考虑到读取评论时需要获得评论的全部数据(评论者姓名,联系方式,评论时间和评论内容),不像文章一样有时只需要文章标题而不需要文章正文。所以适合将一条评论的各个元素序列化成字符串后作为列表类型键中的元素来存储。

var commentsStr = JSON.stringify({
    
    
    author: 'xxx',
    time: 'xxx',
    content: 'xxx',
})

client.lpush('post:id:comment', commentsStr);

命令补充

  1. 获得/设置指定索引的元素值

    LINDEX key index

    LSET key index value

  2. 只保留列表指定片段

    LTRIM 命令可以删除指定索引范围之外的所有元素,其指定列表范围的方法和LRANGE命令相同。就像这样:

    redis> LRANGE numbers 0 -1 
    1) "1" 
    2) "2" 
    3) "7" 
    4) "3" 
    redis> LTRIM numbers 1 2 
    OK 
    redis> LRANGE numbers 0 1 
    1) "2"
    2) "7"
    

    LTRIM命令常和LPUSH命令一起使用来限制列表中元素的数量,比如记录日志时我们希望只保留最近的100条日志,则每次加入新元素时调用一次LTRIM命令即可:

    LPUSH logs $newLog 
    LTRIM logs 0 99
    
  3. 向列表中插入元素

    LINSERT key BEFORE|AFTER pivot value

    LINSERT 命令首先会在列表中从左到右查找值为 pivot 的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。

    LINSERT命令的返回值是插入后列表的元素个数。示例如下:

    redis> LRANGE numbers 0 -1 
    1) "2" 
    2) "7"
    3) "0" 
    redis> LINSERT numbers AFTER 7 3 
    (integer) 4 
    redis> LRANGE numbers 0 -1 
    1) "2" 
    2) "7" 
    3) "3" 
    4) "0" 
    redis> LINSERT numbers BEFORE 2 1 
    (integer) 5 
    redis> LRANGE numbers 0 -1 
    1) "1" 
    2) "2" 
    3) "7" 
    4) "3" 
    5) "0"
    
  4. 将元素从一个列表转到另一个列表

    RPOPLPUSH source destination

    RPOPLPUSH是个很有意思的命令,从名字就可以看出它的功能:先执行RPOP命令再执行LPUSH命令。RPOPLPUSH命令会先从source列表类型键的右边弹出一个元素,然后将其加入到destination列表类型键的左边,并返回这个元素的值,整个过程是原子的。

    当把列表类型作为队列使用时,RPOPLPUSH 命令可以很直观地在多个队列中传递数据。当source和destination相同时,RPOPLPUSH命令会不断地将队尾的元素移到队首,借助这个特性我们可以实现一个网站监控系统:使用一个队列存储需要监控的网址,然后监控程序不断地使用 RPOPLPUSH 命令循环取出一个网址来测试可用性。这里使用RPOPLPUSH命令的好处在于在程序执行过程中仍然可以不断地向网址列表中加入新网址,而且整个系统容易扩展,允许多个客户端同时处理队列。

实例

list_demo.js

var redis = require('redis');
var client = new redis({
    
    
    // 配置
});

// post:list已经有值  通过lpush

var currentPage = 1;  // 当前页面为1
var listLength = 10;

var start = (currentPage - 1) * listLength;
var end = currentPage * listLength - 1; // 0-9

var postIdArrHash = client.lrange(`post:list ${
      
      start} ${
      
      end}`);  // postIdArr此时还是一个列表 里面 存储的 散列

postIdArrHash.forEach((id) => {
    
    
    client.hgetall(`post:${
      
      id}`, (data) => {
    
    
        console.log(data);
    })
});

// 评论只需要用字符串存储 
var commentStr = JSON.stringify({
    
    
    author: 'xxx',
    time: 'xxx',
    content: 'xxx',
});

// 评论要存到列表中,因为一篇文章会有多个评论
client.lpush(`post:${
      
      id}:comments ${
      
      commentStr}`);

猜你喜欢

转载自blog.csdn.net/weixin_44867717/article/details/131650902
今日推荐