Redis List列表

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

Redis List列表

收录于专栏[redis]
本专栏旨在分享学习Redis的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

概述

常用命令

LPUSH

LPUSHX

RPUSH

RPUSHX

LRANGE

LPOP

RPOP

LINDEX

LINSERT

LLEN

阻塞命令版本

BLPOP

BRPOP

命令小结

​内部编码

使用场景

消息队列

​分频道的消息队列

[微博 Timeline](#微博 Timeline)


概述

列表类型是用来存储多个有序的字符串,如下图所示,a、b、c、d、e 五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素(element),一个列表最多可以存储 2^32 - 1 个元素。在 Redis 中,可以对列表两端插入(push)和弹出(Pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等(如下图所示)。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。

列表类型的特点:

第一、列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表,例如获取上图的第五个元素,可以执行 lindex user:1:message 4 或者倒数第一个元素,lindex user:1:message -1 就可以得到元素 e。

第二、区分获取和删除的区别,例如 lrem 1 b 是从列表中把从左数遇到的前 1 个 b 元素删除,这个操作会导致列表的长度从 5 变成 4;但是执行 lindex 4 只会获取元素,但列表的长度是不会变化的。

第三、列表中的元素是允许重复的,如图列表是包含了两个 a 元素的。

常用命令

LPUSH

将一个或者多个元素从左侧放入(头插)到 list 中。

语法:

bash 复制代码
LPUSH key element [element ...]

命令有效版本: 1.0.0 之后
时间复杂度: 只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
返回值: 插入后 list 的长度。

示例:

LPUSHX

在 key 存在时,将一个或者多个元素从左侧放入(头插)到 list 中。不存在,直接返回。

语法:

bash 复制代码
LPUSHX key element [element ...]

命令有效版本: 2.0.0 之后
时间复杂度: 只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
返回值: 插入后 list 的长度。

示例:

RPUSH

将一个或者多个元素从右侧放入(尾插)到 list 中。

语法:

bash 复制代码
RPUSH key element [element ...]

命令有效版本: 1.0.0 之后
时间复杂度: 只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
返回值: 插入后 list 的长度。

示例:

RPUSHX

在 key 存在时,将一个或者多个元素从右侧放入(尾插)到 list 中。

语法:

cpp 复制代码
RPUSHX key element [element ...]

命令有效版本: 2.0.0 之后
时间复杂度: 只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
返回值: 插入后 list 的长度。

示例:

LRANGE

获取从 start 到 end 区间的所有元素,左闭右闭。

语法:

bash 复制代码
LRANGE key start stop

命令有效版本: 1.0.0 之后
时间复杂度: O(N)
返回值: 指定区间的元素。

示例:

LPOP

从 list 左侧取出元素(即头删)。

语法:

bash 复制代码
LPOP key

命令有效版本: 1.0.0 之后
时间复杂度: O(1)
返回值: 取出的元素或者 nil。

示例:

RPOP

从 list 右侧取出元素(即尾删)。

语法:

bash 复制代码
RPOP key

命令有效版本: 1.0.0 之后
时间复杂度: O(1)
返回值: 取出的元素或者 nil。

示例:

LINDEX

获取从左数第 index 位置的元素。

语法:

cpp 复制代码
LINDEX key index

命令有效版本: 1.0.0 之后
时间复杂度: O(N)
返回值: 取出的元素或者 nil。

示例:

LINSERT

在特定位置插入元素。

语法:

bash 复制代码
LINSERT key <BEFORE | AFTER> pivot element

命令有效版本: 2.2.0 之后
时间复杂度:O(N)
返回值:
插入后的 list 长度。

示例:

LLEN

获取 list 长度。

语法:

bash 复制代码
LLEN key

命令有效版本: 1.0.0 之后
时间复杂度: O(1)
返回值: list 的长度。

示例:

阻塞命令版本

blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,和对应非阻塞版本的作用基本一致,除了:

  • 在列表中有元素的情况下,阻塞和非阻塞表现是一致的。但如果列表中没有元素,非阻塞版本会理解返回 nil,但阻塞版本会根据 timeout,阻塞一段时间,期间 Redis 可以执行其他命令,但要求执行该命令的客户端会表现为阻塞状态。
  • 命令中如果设置了多个键,那么会从左向右进行遍历,一旦有一个键对应的列表中可以弹出元素,命令立即返回。
  • 如果多个客户端同时都一个键执行 POP,则最先执行命令的客户端会得到弹出的元素。

BLPOP

LPOP 的阻塞版本。

语法:

cpp 复制代码
BLPOP key [key ...] timeout 

命令有效版本: 1.0.0 之后
时间复杂度: O(1)
返回值: 取出的元素或者 nil。

示例:

Redis 会尝试从 list1 和 list2 中一次弹出一个元素:

  1. 如果 list1 非空,则从 list1 弹出一个元素。

  2. 如果 list2 为空,则继续尝试从 list2 中弹出一个元素。

  3. 如果两个列表都为空,客户端会阻塞并等待,直到某个列表有元素可弹出为止(由于设置了超时为0,它会一直等待)。

BRPOP

RPOP 的阻塞版本。

语法:

bash 复制代码
BRPOP key [key ...] timeout 

命令有效版本: 1.0.0 之后
时间复杂度: O(1)
返回值: 取出的元素或者 nil。

示例:

命令小结

有关列表的命令已经介绍完毕,下表是这些命令的作用和时间复杂度,开发人员可以参考。

内部编码

列表类型的内部编码有两种:

ziplist(压缩列表):当列表的元素个数小于 list-max-ziplist-entries 配置(默认 512 个),同时列表中每个元素的长度都小于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选用 ziplist 来作为列表的内部编码实现来减少内存消耗。

**linkedlist(链表):**当列表类型无法满足 ziplist 的条件时,Redis 会使用 linkedlist 作为列表的内部实现。

但是现在版本的 Redis 已经不再使用这两种内部编码,使用了 quicklist 代替:

使用场景

消息队列

如下图所示,Redis 可以使用 lpush + brpop 命令组合实现经典的阻塞式生产者-消费者模型队列,生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式地从队列中"争抢" 队首元素。通过多个客户端来保证消费的负载均衡和高可用性。

分频道的消息队列

如下图所示,Redis 同样使用 lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费

者可以通过 brpop 不同的键值,实现订阅不同频道的理念。

微博 Timeline

每个用户都有属于自己的 Timeline(微博列表),现需要分页展示文章列表。此时可以考虑使用

列表,因为列表不但是有序的,同时支持按照索引范围获取元素。

1)每篇微博使用哈希结构存储,例如微博中 3 个属性:title、timestamp、content:

bash 复制代码
hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx

2)向用户 Timeline 添加微博,user:<uid>:mblogs 作为微博的键:

bash 复制代码
lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9

3)分页获取用户的 Timeline,例如获取用户 1 的前 10 篇微博:

bash 复制代码
keylist = lrange user:1:mblogs 0 9
for key in keylist {
hgetall key
}

此方案在实际中可能存在两个问题:

1. 1 + n 问题。 即如果每次分页获取的微博个数较多,需要执行多次 hgetall 操作,此时可以考虑使用 pipeline(流水线)模式批量提交命令,或者微博不采用哈希类型,而是使用序列化的字符串类型,使用 mget 获取。
2. 分裂获取文章时,lrange 在列表两端表现较好,获取列表中间的元素表现较差,此时可以考虑将列表做拆分。
选择列表类型时,请参考:
同侧存取(lpush + lpop 或者 rpush + rpop)为栈
异侧存取(lpush + rpop 或者 rpush + lpop)为队列

相关推荐
小吴编程之路8 小时前
MySQL 索引核心特性深度解析:从底层原理到实操应用
数据库·mysql
~莫子8 小时前
MySQL集群技术
数据库·mysql
凤山老林8 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
就不掉头发8 小时前
Linux与数据库进阶
数据库
与衫8 小时前
Gudu SQL Omni 技术深度解析
数据库·sql
咖啡の猫9 小时前
Redis桌面客户端
数据库·redis·缓存
oradh9 小时前
Oracle 11g数据库软件和数据库静默安装
数据库·oracle
what丶k9 小时前
如何保证 Redis 与 MySQL 数据一致性?后端必备实践指南
数据库·redis·mysql
_半夏曲9 小时前
PostgreSQL 13、14、15 区别
数据库·postgresql
把你毕设抢过来9 小时前
基于Spring Boot的社区智慧养老监护管理平台(源码+文档)
数据库·spring boot·后端