【Redis】8.List列表

文章目录

  • [2. Redis 常见数据类型](#2. Redis 常见数据类型)
    • [2.4 List 列表](#2.4 List 列表)
      • [2.4.1 命令](#2.4.1 命令)
        • [2.4.1.1 LPUSH:头插](#2.4.1.1 LPUSH:头插)
        • [2.4.1.2 LPUSHX:key存在时头插;不存在返回](#2.4.1.2 LPUSHX:key存在时头插;不存在返回)
        • [2.4.1.3 RPUSH:尾插](#2.4.1.3 RPUSH:尾插)
        • [2.4.1.4 RPUSHX:key存在时尾插](#2.4.1.4 RPUSHX:key存在时尾插)
        • [2.4.1.5 LRANGE:获取闭区间元素](#2.4.1.5 LRANGE:获取闭区间元素)
        • [2.4.1.6 LPOP:头删](#2.4.1.6 LPOP:头删)
        • [2.4.1.7 RPOP:尾删](#2.4.1.7 RPOP:尾删)
        • [2.4.1.8 LINDEX:获取左数第index元素](#2.4.1.8 LINDEX:获取左数第index元素)
        • [2.4.1.9 LINSERT:特定位置插入元素](#2.4.1.9 LINSERT:特定位置插入元素)
        • [2.4.1.10 LLEN:获取list长度](#2.4.1.10 LLEN:获取list长度)
        • [2.4.1.11 LREM:移除前count个与element相等的元素](#2.4.1.11 LREM:移除前count个与element相等的元素)
        • [2.4.1.12 LTRIM:只保留范围内元素](#2.4.1.12 LTRIM:只保留范围内元素)
        • [2.4.1.13 LSET:修改指定位置元素](#2.4.1.13 LSET:修改指定位置元素)
      • [2.4.2 阻塞版本命令](#2.4.2 阻塞版本命令)
        • [2.4.2.1 BLPOP:阻塞版头删](#2.4.2.1 BLPOP:阻塞版头删)
          • [2.4.2.1.1 针对非空列表操作](#2.4.2.1.1 针对非空列表操作)
          • [2.4.2.1.1 针对空列表操作](#2.4.2.1.1 针对空列表操作)
          • [2.4.2.3 针对多个key操作](#2.4.2.3 针对多个key操作)
        • [2.4.2.2 BRPOP:阻塞版尾删](#2.4.2.2 BRPOP:阻塞版尾删)
      • [2.4.3 命令小结](#2.4.3 命令小结)
      • [2.4.4 内部编码](#2.4.4 内部编码)
      • [2.4.5 使用场景](#2.4.5 使用场景)
        • [2.4.5.1 消息队列](#2.4.5.1 消息队列)
        • [2.4.5.2 分频道的消息队列](#2.4.5.2 分频道的消息队列)
        • [2.4.5.3 微博 Timeline](#2.4.5.3 微博 Timeline)

2. Redis 常见数据类型

2.4 List 列表

列表类型是用来存储多个有序的字符串,如下图所示,a、b、c、d、e 五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素(element),一个列表最多可以存储 个元素。

在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。

列表两端插入和弹出操作:

头插:lpush

头删:lpop

尾插:rpush

尾删:rpop
注意:

list内部的结构并非是一个简单的数组,而是接近于"双端队列"deque。

列表的获取、删除等操作

列表类型的特点:

  1. 列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表。

    例如:要获取上图的第 5 个元素,可以执行 lindex user:1:messages 4 或者倒数第 1 个元素,lindex user:1:messages -1 就可以得到元素 e。

  2. 区分获取和删除的区别。

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

  3. 列表中的元素是允许重复的。

    例如下图中的列表中是包含了两个 a 元素的。

列表中允许有重复元素


2.4.1 命令

2.4.1.1 LPUSH:头插

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

语法:

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

命令有效版本:1.0.0 之后

时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.

返回值:插入后 list 的长度。

示例:

mysql 复制代码
127.0.0.1:6379> lpush mylist "world"
(integer) 1
127.0.0.1:6379> lpush mylist "hello"
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1 # lrange在后面
1) "hello"
2) "world"
127.0.0.1:6379> 

2.4.1.2 LPUSHX:key存在时头插;不存在返回

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

语法:

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

命令有效版本:2.0.0 之后

时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数。

返回值:插入后 list 的长度。

示例:

mysql 复制代码
127.0.0.1:6379> lpush mylist8 1 2 3 4
(integer) 4
127.0.0.1:6379> lpushx mylist8 5
(integer) 5
127.0.0.1:6379> lrange mylist8 0 100
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lpushx mylist9 5
(integer) 0
127.0.0.1:6379> lrange mylist9 0 100
(empty array)
127.0.0.1:6379> 

2.4.1.3 RPUSH:尾插

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

语法:

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

命令有效版本:1.0.0 之后

时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.

返回值:插入后 list 的长度。

示例:

mysql 复制代码
127.0.0.1:6379> rpush mylist6 1 2 3
(integer) 3
127.0.0.1:6379> lrange mylist6 0 0
1) "1"
127.0.0.1:6379> lrange mylist6 -3 2
1) "1"
2) "2"
3) "3"
127.0.0.1:6379>

2.4.1.4 RPUSHX:key存在时尾插

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

语法:

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

命令有效版本:2.0.0 之后

时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数。

返回值:插入后 list 的长度。

示例:

mysql 复制代码
127.0.0.1:6379> rpushx mylist6 4
(integer) 4
127.0.0.1:6379> lrange mylist6 0 3
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> rpushx mylist4 5
(integer) 0
127.0.0.1:6379> lrange mylist4 0 0
(empty array)
127.0.0.1:6379> 

2.4.1.5 LRANGE:获取闭区间元素

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

语法:

mysql 复制代码
LRANGE key start stop

命令有效版本:1.0.0 之后

时间复杂度:O(N)

返回值:指定区间的元素。

示例:

mysql 复制代码
127.0.0.1:6379> rpush mylist6 1 2 3
(integer) 3
127.0.0.1:6379> lrange mylist6 0 0
1) "1"
127.0.0.1:6379> lrange mylist6 -3 2
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> lrange mylist6 -100 100
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> lrange mylist6 5 10
(empty array)
127.0.0.1:6379> 

需要注意:此处搜出来的1) 2) 3)这种的是专门给结果集使用的序号,和list下标无关。

如果是对哈希操作,也会得到这种带有序号的结果,此处的序号仅仅是标识返回元素顺序,和下标没有一点关系,hash类型就没有下标这个概念。

另外,没有rrange,因为lrange的l不是left,而是list。


2.4.1.6 LPOP:头删

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

语法:

mysql 复制代码
LPOP key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:取出的元素或者 nil。

示例:

mysql 复制代码
127.0.0.1:6379> lrange mylist6 0 3
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> lpop mylist6
"1"
127.0.0.1:6379> lrange mylist6 0 3
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> lpush mylist9 1
(integer) 1
127.0.0.1:6379> lrange mylist9 0 100
1) "1"
127.0.0.1:6379> lpop mylist9
"1"
127.0.0.1:6379> lpop mylist9
(nil)
127.0.0.1:6379> lrange mylist9 0 100
(empty array)
127.0.0.1:6379> 

2.4.1.7 RPOP:尾删

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

语法:

mysql 复制代码
RPOP key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:取出的元素或者 nil。

示例:

mysql 复制代码
127.0.0.1:6379> lrange mylist6 0 3
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> rpop mylist6
"4"
127.0.0.1:6379> lrange mylist6 0 3
1) "2"
2) "3"
127.0.0.1:6379> 

2.4.1.8 LINDEX:获取左数第index元素

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

语法:

mysql 复制代码
LINDEX key index

命令有效版本:1.0.0 之后

时间复杂度:O(N)

返回值:取出的元素或者 nil。

示例:

mysql 复制代码
127.0.0.1:6379> lrange mylist6 0 3
1) "2"
2) "3"
127.0.0.1:6379> lindex mylist6 0
"2"
127.0.0.1:6379> lindex mylist6 1
"3"
127.0.0.1:6379> lindex mylist6 2
(nil)
127.0.0.1:6379> 

2.4.1.9 LINSERT:特定位置插入元素

在特定位置插入元素。

语法:

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

命令有效版本:2.2.0 之后

时间复杂度:O(N)

返回值:插入后的 list 长度。

示例:

mysql 复制代码
127.0.0.1:6379> lrange mylist6 0 3
1) "2"
2) "3"
127.0.0.1:6379> linsert mylist6 before "2" "1"
(integer) 3
127.0.0.1:6379> linsert mylist6 after "3" "4"
(integer) 4
127.0.0.1:6379> lrange mylist6 0 3
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> 
mysql 复制代码
127.0.0.1:6379> lpush mylist10 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lpush mylist10 1
(integer) 6
127.0.0.1:6379> lrange mylist10 0 100
1) "1"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> linsert mylist10 before "1" "100" # 两个1插入哪个前面呢?
(integer) 7
127.0.0.1:6379> lrange mylist10 0 100
1) "100"
2) "1"
3) "5"
4) "4"
5) "3"
6) "2"
7) "1"
127.0.0.1:6379> 

linsert进行插入的时候,会根据基准值,从左往右找到第一个基准值的位置,然后进行插入。


2.4.1.10 LLEN:获取list长度

获取 list 长度。

语法:

mysql 复制代码
LLEN key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:list 的长度。

示例:

mysql 复制代码
127.0.0.1:6379> lrange mylist6 0 3
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> llen mylist6
(integer) 4
127.0.0.1:6379> llen mylist7
(integer) 0
127.0.0.1:6379> 

2.4.1.11 LREM:移除前count个与element相等的元素

从键 key 存储的列表中移除前 count 个与 element 相等的元素。参数 count 对操作的影响如下:

  • count > 0 :从表头向表尾方向(从左到右)移除等于 element 的元素。
  • count < 0 :从表尾向表头方向(从右到左)移除等于 element 的元素。
  • count = 0 :移除所有等于 element 的元素。

语法:

mysql 复制代码
LREM key count element
	count:要删除的个数
	element:要删除的值

命令有效版本:1.0.0 之后

时间复杂度:O(N + M),其中 N 是列表的长度,M 是被移除的元素数量。

返回值:移除的元素数量。

示例:

mysql 复制代码
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 8
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 12
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 16
127.0.0.1:6379> lrange key 0 -1
 1) "1"
 2) "2"
 3) "3"
 4) "4"
 5) "1"
 6) "2"
 7) "3"
 8) "4"
 9) "1"
10) "2"
11) "3"
12) "4"
13) "1"
14) "2"
15) "3"
16) "4"
127.0.0.1:6379> lrem key 2 1
(integer) 2
127.0.0.1:6379> lrange key 0 -1
 1) "2"
 2) "3"
 3) "4"
 4) "2"
 5) "3"
 6) "4"
 7) "1"
 8) "2"
 9) "3"
10) "4"
11) "1"
12) "2"
13) "3"
14) "4"
127.0.0.1:6379> 
mysql 复制代码
127.0.0.1:6379> lrem key -2 1
(integer) 2
127.0.0.1:6379> lrange key 0 -1
 1) "2"
 2) "3"
 3) "4"
 4) "2"
 5) "3"
 6) "4"
 7) "2"
 8) "3"
 9) "4"
10) "2"
11) "3"
12) "4"
127.0.0.1:6379> lrem key 0 2
(integer) 4
127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "4"
3) "3"
4) "4"
5) "3"
6) "4"
7) "3"
8) "4"
127.0.0.1:6379> 

2.4.1.12 LTRIM:只保留范围内元素

对一个已存在的列表进行修剪,让列表只保留指定范围内的元素。

start 和 stop 都是基于 0 的索引,其中 0 表示列表的第一个元素(表头),1 表示下一个元素,以此类推。

语法:

mysql 复制代码
LTRIM key start stop

命令有效版本:1.0.0 之后

时间复杂度:O(N),其中 N 是操作要移除的元素数量。

返回值:成功返回OK。

示例:

mysql 复制代码
127.0.0.1:6379> lpush key1 1 2 3 4
(integer) 4
127.0.0.1:6379> lrange key1 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> ltrim key1 1 2
OK
127.0.0.1:6379> lrange key1 0 -1
1) "3"
2) "2"
127.0.0.1:6379> ltrim key1 3 4
OK
127.0.0.1:6379> lrange key1 0 -1
(empty array)
127.0.0.1:6379> 

2.4.1.13 LSET:修改指定位置元素

将列表中指定索引 index 处的元素设置为 element。

如果索引超出范围,将返回错误。

语法:

mysql 复制代码
LSET key index element

命令有效版本:1.0.0 之后

时间复杂度:O(N),其中 N 是列表的长度。设置列表的第一个或最后一个元素是 O(1)。

返回值:成功返回OK。

示例:

mysql 复制代码
127.0.0.1:6379> lpush key1 1 2 3 4
(integer) 4
127.0.0.1:6379> lrange key1 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lset key1 0 element
OK
127.0.0.1:6379> lrange key1 0 -1
1) "element"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lset key1 1 100
OK
127.0.0.1:6379> lrange key1 0 -1
1) "element"
2) "100"
3) "2"
4) "1"
127.0.0.1:6379> lset key1 10 100
(error) ERR index out of range
127.0.0.1:6379> lrange key1 0 -1
1) "element"
2) "100"
3) "2"
4) "1"
127.0.0.1:6379> 

2.4.2 阻塞版本命令

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

  • 在列表中有元素的情况下,阻塞和非阻塞表现是一致的。但如果列表中没有元素,非阻塞版本会理解返回 nil,但阻塞版本会根据 timeout,阻塞一段时间,期间 Redis 可以执行其他命令,但要求执行该命令的客户端会表现为阻塞状态(如下图)。

  • 命令中如果设置了多个键,那么会从左向右进行遍历键,一旦有一个键对应的列表中可以弹出元素,命令立即返回。

    blpop和brpop都是可以同时去尝试获取多个key的列表的元素的。

    多个key对应多个list,这多个list哪个有元素了,就会返回哪个元素。

    一个人约多个女神,谁先来就和谁约会。(海王)

  • 如果多个客户端同时对一个键执行 pop,则最先执行命令的客户端会得到弹出的元素。

    多个人约同一个女神,谁先约到谁先和女神约会。(舔狗)

阻塞版本的 blpop 和 非阻塞版本 lpop 的区别:


2.4.2.1 BLPOP:阻塞版头删

LPOP 的阻塞版本。

BLPOP 是一个阻塞式列表弹出原语。它是 LPOP 的阻塞版本,因为当给定列表中没有任何元素可弹出时,它会阻塞连接。元素会从第一个非空列表的头部弹出,给定的键会按照指定的顺序依次进行检查。

语法:

mysql 复制代码
BLPOP key [key ...] timeout
key [key ...]
要操作的一个或多个列表键。
timeout
	最大阻塞时间,以秒为单位。超时时间为 0 表示无限期阻塞。
	在Redis6以后,超时时间允许设定成小数。不过Redis5中,超时时间只能是整数。

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:取出的元素或者 nil。

示例:

2.4.2.1.1 针对非空列表操作
mysql 复制代码
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> blpop key 0 # 返回的结果相当于一个pair(二元组),一方面是告诉我们当前的数据来自于哪个key,另一方面告诉我们取到的数据是什么。
1) "key" # 当前的数据来自于哪个key
2) "1"   # 当前取到的数据是什么,blpop也是pop
127.0.0.1:6379> 

2.4.2.1.1 针对空列表操作
mysql 复制代码
# A客户端
127.0.0.1:6379> del key
(integer) 1
127.0.0.1:6379> keys *
1) "backup3"
2) "backup1"
3) "backup2"
4) "backup4"
127.0.0.1:6379> 
# 100s内key为空就卡这里,阻塞了

# B客户端
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> 

# A客户端
127.0.0.1:6379> blpop key 100
1) "key"
2) "1"
(98.06s)
127.0.0.1:6379> 

# B客户端
127.0.0.1:6379> lrange key 0 -1
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> 

2.4.2.3 针对多个key操作
mysql 复制代码
# A客户端
127.0.0.1:6379> blpop key key1 key2 key3 key4 500
# 阻塞住,因为这几个都是空的

# B客户端
127.0.0.1:6379> rpush key2 1
(integer) 1
127.0.0.1:6379> 

# A客户端
127.0.0.1:6379> blpop key key1 key2 key3 key4 500
1) "key2"
2) "1"
(43.20s)
127.0.0.1:6379> 

2.4.2.2 BRPOP:阻塞版尾删

RPOP 的阻塞版本。

效果和blpop差不多,就不赘述了。

语法:

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

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:取出的元素或者 nil。

示例:

mysql 复制代码
redis> DEL list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BRPOP list1 list2 0
1) "list1"
2) "c"

此处的这两个阻塞命令,用途主要就是用来作为消息队列。

当前这两个命令,虽然可以一定程度的满足"消息队列"这样的需求,但是整体来说,这两个命令的功能还是比较有限的。所以实际开发中,要根据实际情况来搞。


2.4.3 命令小结

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

列表命令


2.4.4 内部编码

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

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

    ( list-max-ziplist-entries 和list-max-ziplist-value 现在已经不使用了,因为现在里面的结构变了)

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

mysql 复制代码
root@yudukai:~# cd /etc/redis
root@yudukai:/etc/redis# cat redis.conf

我们可以发现:list-max-ziplist-entries 和list-max-ziplist-value这两个确实没了,不是我胡说八道。

不过出现了一个新的东西:list-max-ziplist-size -2

mysql 复制代码
# 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)也采用特殊的编码方式以节省大量空间。
每个内部列表节点允许的条目数量可以指定为固定的最大大小或最大元素数量。
对于固定的最大大小,使用 -5 到 -1 的值,其含义如下:
-5:最大大小:64 Kb  <-- 不建议用于常规工作负载
-4:最大大小:32 Kb  <-- 不建议
-3:最大大小:16 Kb  <-- 可能不建议
-2:最大大小:8 Kb   <-- 不错
-1:最大大小:4 Kb   <-- 不错
正数表示每个列表节点最多存储_精确_达到该数值的元素个数。
性能最高的选项通常是 -2(8 Kb 大小)或 -1(4 Kb 大小),
但如果您的使用场景比较特殊,请根据需要调整设置。
list-max-ziplist-size -2

ziplist(压缩列表):把数据按照更紧凑的压缩形式进行表示,节省空间。但是当元素个数多了,会影响操作效率。

linkedlist(链表)

quicklist(快速链表):相当于链表和压缩链表的结合,整体还是一个链表,链表的每个节点是一个压缩列表。每个压缩列表都不会很大,同时再把多个压缩列表通过链式结构连接起来。

mysql 复制代码
127.0.0.1:6379> rpush key 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding key
"quicklist"
127.0.0.1:6379> 

2.4.5 使用场景

2.4.5.1 消息队列

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

Redis 阻塞消息队列模型


2.4.5.2 分频道的消息队列

如下图所示,Redis 同样使用 lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费者可以通过 brpop 不同的键值,实现订阅不同频道的理念。

Redis 分频道阻塞消息队列模型

搞成多个频道,就可以在某种数据发生问题的时候,不会对其他数据造成影响。(解耦合)


2.4.5.3 微博 Timeline

每个用户都有属于自己的 Timeline(微博列表),现需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。

  1. 每篇微博使用哈希结构存储,例如微博中 3 个属性:title、timestamp、content:
mysql 复制代码
hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx
  1. 向用户 Timeline 添加微博,user:<uid>:mblogs 作为微博的键:
mysql 复制代码
lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9
  1. 分页获取用户的 Timeline,例如获取用户 1 的前 10 篇微博:
c 复制代码
keylist = lrange user:1:mblogs 0 9
for key in keylist {
	hgetall key
}

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

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

虽然有多个redis命令,但是把这些命令合并到一个网络请求中进行通信。这样就可以减少频繁网络请求带来的开销。
选择列表类型时,请参考:

同侧存取(lpush + lpop 或者 rpush + rpop)为栈

异侧存取(lpush + rpop 或者 rpush + lpop)为队列

相关推荐
IT大白鼠5 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz5 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
lbb 小魔仙5 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
夕除5 天前
redis--018
redis
两锭子5 天前
Redis基础
redis
烟沙九洲5 天前
Redis 缓存穿透、缓存击穿、缓存雪崩
数据库·redis
字节探索5 天前
Redis 只会当缓存用?这 10 大实战场景,让你的系统快到飞起
redis·后端
于樱花森上飞舞5 天前
【Redis】哨兵详解
java·开发语言·数据库·redis
Rain的Java大神之路6 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
Wang's Blog6 天前
Java 接入Redis: 通用命令与键管理
java·服务器·redis