一: List 列表
列表类型用于存储多个有序的字符串,a、b、c、d、e 五个元素从左到右组成一个有序列表,其中每个字符串称为元素。列表最多可以存储 2³² - 1 个元素。在 Redis 中,可以对列表的两端进行插入(push)和弹出(pop),还可以获取指定范围的元素列表或指定索引的元素。作为一种灵活的数据结构,列表既可以充当栈,也可以充当队列,在实际开发中有着广泛的应用场景,List 的特点如下:
特点 | 描述 |
---|---|
元素有序 | 列表中的元素是有序的,可以通过索引下标获取指定元素或范围内的元素。例如,lindex user:1:messages 4 获取第 5 个元素,lindex user:1:messages -1 获取倒数第 1 个元素。 |
区分获取和删除的操作 | 获取和删除操作是区分的。例如,lrem 1 b 会从列表中删除从左数第 1 个 b,导致列表长度从 5 变为 4;而 lindex 4 只会获取第 5 个元素,不会改变列表长度。 |
元素允许重复 | 列表中的元素可以重复,例如图 2-21 所示,列表中包含两个相同的元素 a。 |
1.1 常用命令
1.1.1 LPUSH
LPUSH 命令用于将一个或多个元素从左侧插入到列表中(头插)。返回值为插入后列表的长度。
LPUSH key element [element ...]
redis> LPUSH mylist "world"
(integer) 1
redis> LPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "hello"
2) "world"
1.1.2 LPUSHX
LPUSHX 命令用于在指定的 key 存在时,将一个或多个元素从左侧插入到列表中(头插)。如果 key 不存在,则直接返回。返回值为插入后列表的长度。
LPUSHX key element [element ...]
redis> LPUSH mylist "World"
(integer) 1
redis> LPUSHX mylist "Hello"
(integer) 2
redis> LPUSHX myotherlist "Hello"
(integer) 0
redis> LRANGE mylist 0 -1
1) "Hello"
2) "World"
redis> LRANGE myotherlist 0 -1
(empty array)
1.1.3 RPUSH
RPUSH 命令用于将一个或多个元素从右侧插入到列表中(尾插)。返回值为插入后列表的长度。
RPUSH key element [element ...]
redis> RPUSH mylist "world"
(integer) 1
redis> RPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "world"
2) "hello"
1.1.4 RPUSHX
RPUSHX 命令用于在指定的 key 存在时,将一个或多个元素从右侧插入到列表中(尾插)。返回值为插入后列表的长度。
RPUSHX key element [element ...]
redis> RPUSH mylist "World"
(integer) 1
redis> RPUSHX mylist "Hello"
(integer) 2
redis> RPUSHX myotherlist "Hello"
(integer) 0
redis> LRANGE mylist 0 -1
1) "World"
2) "Hello"
redis> LRANGE myotherlist 0 -1
(empty array)
1.1.5 LRANGE
LRANGE 命令用于获取列表中从 start 到 end 区间(左闭右闭)的所有元素。返回值为指定区间内的元素列表。
LRANGE key start stop
redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> LRANGE mylist 0 0
1) "one"
redis> LRANGE mylist -3 2
1) "one"
2) "two"
3) "three"
redis> LRANGE mylist -100 100
1) "one"
2) "two"
3) "three"
redis> LRANGE mylist 5 10
(empty array)
1.1.6 LPOP
LPOP 命令用于从列表左侧移除并返回第一个元素(头删)。返回值为移除的元素,若列表为空则返回 nil。
LPOP key
RPUSH mylist "one" "two" "three" "four" "five"
(integer) 5
LPOP mylist
"one"
LPOP mylist
"two"
LPOP mylist
"three"
LRANGE mylist 0 -1
1) "four"
2) "five"
1.1.7 RPOP
RPOP 命令用于从列表右侧移除并返回最后一个元素(尾删)。返回值为被移除的元素,若列表为空则返回 nil。
RPOP key
redis> RPUSH mylist "one" "two" "three" "four" "five"
(integer) 5
redis> RPOP mylist
"five"
redis> LRANGE mylist 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
1.1.8 LINDEX
LINDEX 命令用于获取列表中指定索引位置(从左数)的元素。返回值为对应的元素,若索引位置不存在则返回 nil。
LINDEX key index
redis> LPUSH mylist "World"
(integer) 1
redis> LPUSH mylist "Hello"
(integer) 2
redis> LINDEX mylist 0
"Hello"
redis> LINDEX mylist -1
"World"
redis> LINDEX mylist 3
(nil)
1.1.9 LINSERT
LINSERT 命令用于在列表的指定位置插入元素。返回值为插入后列表的长度。
LINSERT key <BEFORE | AFTER> pivot element
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
1.1.10 LLEN
LLEN 命令用于获取列表的长度。返回值为列表的长度。
LLEN key
redis> LPUSH mylist "World"
(integer) 1
redis> LPUSH mylist "Hello"
(integer) 2
redis> LLEN mylist
(integer) 2
1.1.11 阻塞版本命令
BLPOP 和 BRPOP 是 LPOP 和 RPOP 的阻塞版本,功能与对应的非阻塞版本基本相同,不同之处在于:
特点 | 描述 |
---|---|
有元素时表现一致 | 当列表中有元素时,阻塞版本与非阻塞版本的表现一致,直接弹出元素。 |
无元素时的表现 | 当列表中无元素时,非阻塞版本立即返回 nil,而阻塞版本会根据 timeout 阻塞一段时间,此期间 Redis 可执行其他命令,但执行该命令的客户端处于阻塞状态。 |
多键操作 | 如果命令设置了多个键,将从左到右依次遍历,一旦某个键对应的列表有元素可弹出,命令立即返回。 |
并发情况下的处理 | 如果多个客户端同时对同一个键执行 pop 操作,最先执行命令的客户端将获得弹出的元素。 |
1.1.12 BLPOP
BLPOP 命令是 LPOP 的阻塞版本。返回值为取出的元素,若超时或列表为空则返回 nil。
BLPOP key [key ...] timeout
redis> EXISTS list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BLPOP list1 list2 0
1) "list1"
2) "a"
1.1.13 BRPOP
BRPOP 命令是 RPOP 的阻塞版本。返回值为取出的元素,若超时或列表为空则返回 nil。
BRPOP key [key ...] timeout
redis> DEL list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BRPOP list1 list2 0
1) "list1"
2) "c"
1.1.14 命令小结
命令 | 时间复杂度 | 描述 |
---|---|---|
rpush key value [value …] | O(k),k 是元素个数 | 从右侧插入一个或多个元素 |
lpush key value [value …] | O(k),k 是元素个数 | 从左侧插入一个或多个元素 |
linsert key before | after pivot value | O(n),n 是 pivot 距离头尾的距离 |
lrange key start end | O(s+n),s 是 start 偏移量,n 是范围 | 获取指定范围的元素列表 |
lindex key index | O(n),n 是索引的偏移量 | 获取指定索引位置的元素 |
llen key | O(1) | 获取列表长度 |
lpop key | O(1) | 从左侧弹出第一个元素 |
rpop key | O(1) | 从右侧弹出最后一个元素 |
lrem key count value | O(k),k 是元素个数 | 删除列表中指定值的元素 |
ltrim key start end | O(k),k 是元素个数 | 截取指定范围的列表 |
lset key index value | O(n),n 是索引的偏移量 | 修改指定索引位置的元素值 |
blpop / brpop | O(1) | 从列表两端阻塞弹出元素 |
1.2 内部编码
列表类型的内部编码有两种:
内部编码类型 | 使用条件 | 描述 |
---|---|---|
ziplist | 当列表元素个数小于 list-max-ziplist-entries(默认 512 个),且每个元素长度小于 list-max-ziplist-value(默认 64 字节)时使用。 | 使用紧凑的内存结构,减少内存消耗。 |
linkedlist | 当列表类型不满足 ziplist 条件(如元素个数过多或元素长度过大)时使用。 | 使用链表结构,支持更复杂的操作。 |
- 当元素个数较少且没有大元素时,内部编码为 ziplist:
127.0.0.1:6379> rpush listkey e1 e2 e3
OK
127.0.0.1:6379> object encoding listkey
"ziplist"
- 当元素个数超过 512 时,内部编码为 linkedlist:
127.0.0.1:6379> rpush listkey e1 e2 e3 ... 省略 e512 e513
OK
127.0.0.1:6379> object encoding listkey
"linkedlist"
- 当某个元素的长度超过 64 字节时,内部编码为 linkedlist:
127.0.0.1:6379> rpush listkey "one string is bigger than 64 bytes ... 省略 ..."
OK
127.0.0.1:6379> object encoding listkey
"linkedlist"
1.3 使用场景
1.3.1 消息队列
Redis 可以通过 lpush 和 brpop 命令组合实现经典的阻塞式生产者-消费者模型。生产者客户端使用 lpush 命令从列表左侧插入元素,多个消费者客户端通过 brpop 命令以阻塞方式从队列中“争抢”队尾元素。通过多个客户端的协作,可以实现消费的负载均衡和高可用性。
1.3.2 分频道的消息队列
Redis 也可以通过 lpush 和 brpop 命令配合使用,通过不同的键来模拟频道的概念。不同的消费者可以使用 brpop 命令监听不同的键,从而实现订阅不同频道的功能。
二: Set 集合
集合类型用于保存多个字符串类型的元素,与列表类型不同,集合具有以下特点:元素之间是无序的;元素不允许重复。一个集合最多可以存储 2^32 - 1 个元素。Redis 不仅支持集合内的增删查改操作,还支持集合之间的交集、并集和差集操作。合理利用集合类型,可以高效解决实际开发中的许多问题。
2.1 常用命令
2.1.1 SADD
SADD 命令用于将一个或多个元素添加到集合中。注意,集合中不能存在重复元素,重复的元素将不会被添加。返回值为本次成功添加的元素个数。
SADD key member [member ...]
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"
2.1.2 SMEMBERS
SMEMBERS 命令用于获取集合中的所有元素。注意,集合中的元素是无序的。返回值为集合中所有元素的列表。
SMEMBERS key
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SMEMBERS myset
1) "Hello"
2) "World"
2.1.3 SISMEMBER
SISMEMBER 命令用于判断一个元素是否存在于集合中。返回值:1 表示元素在集合中;0 表示元素不在集合中或集合不存在。
SISMEMBER key member
redis> SADD myset "one"
(integer) 1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0
2.1.4 SCARD
SCARD 命令用于集合中元素的数量。返回值为集合中元素的个数。
SCARD key
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SCARD myset
(integer) 2
2.1.5 SPOP
SPOP 命令用于从集合中随机删除并返回一个或多个元素。由于集合内元素是无序的,因此返回的元素是随机的,具体取出哪个元素是未定义的行为。
SPOP key [count]
SADD myset "one"
(integer) 1
SADD myset "two"
(integer) 1
SADD myset "three"
(integer) 1
SPOP myset
"one"
SMEMBERS myset
1) "three"
2) "two"
SADD myset "four"
(integer) 1
SADD myset "five"
(integer) 1
SPOP myset 3
1) "three"
2) "four"
3) "two"
SMEMBERS myset
1) "five"
2.1.6 SMOVE
SMOVE 命令用于将一个元素从源集合移动到目标集合。返回值:1 表示移动成功,0 表示移动失败(如元素不存在于源集合中)。
SMOVE source destination member
SADD myset "one"
(integer) 1
SADD myset "two"
(integer) 1
SADD myotherset "three"
(integer) 1
SMOVE myset myotherset "two"
(integer) 1
SMEMBERS myset
1) "one"
SMEMBERS myotherset
1) "three"
2) "two"
2.1.7 SREM
SREM 命令用于删除集合中指定的元素。返回值为成功删除的元素个数。
SREM key member [member ...]
redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SREM myset "one"
(integer) 1
redis> SREM myset "four"
(integer) 0
redis> SMEMBERS myset
1) "three"
2) "two"
2.1.8 集合间操作
集合间操作包括以下几种:交集(inter)、并集(union)和差集(diff)。
2.1.9 SINTER
SINTER 命令用于获取指定集合之间的交集元素。返回值为交集中的元素列表。
SINTER key [key ...]
SADD key1 "a"
(integer) 1
SADD key1 "b"
(integer) 1
SADD key1 "c"
(integer) 1
SADD key2 "c"
(integer) 1
SADD key2 "d"
(integer) 1
SADD key2 "e"
(integer) 1
SINTER key1 key2
1) "c"
2.1.10 SINTERSTORE
SINTERSTORE 命令用于计算指定集合的交集,并将结果保存到目标集合中。返回值为交集中元素的数量。
SINTERSTORE destination key [key ...]
redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SINTERSTORE key key1 key2
(integer) 1
redis> SMEMBERS key
1) "c"
2.1.11 SUNION
SUNION 命令用于获取指定集合的并集元素。返回值为并集中的元素列表。
SUNION key [key ...]
redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SUNION key1 key2
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"
2.1.12 SUNIONSTORE
SUNIONSTORE 命令用于计算指定集合的并集,并将结果保存到目标集合中。返回值为并集中元素的数量。
SUNIONSTORE destination key [key ...]
redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SUNIONSTORE key key1 key2
(integer) 5
redis> SMEMBERS key
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"
2.1.13 SDIFF
SDIFF 命令用于获取指定集合之间的差集元素。返回值为差集中的元素列表。
SDIFF key [key ...]
SADD key1 "a"
(integer) 1
SADD key1 "b"
(integer) 1
SADD key1 "c"
(integer) 1
SADD key2 "c"
(integer) 1
SADD key2 "d"
(integer) 1
SADD key2 "e"
(integer) 1
SDIFF key1 key2
1) "a"
2) "b"
2.1.14 SDIFFSTORE
SDIFFSTORE 命令用于计算指定集合的差集,并将结果保存到目标集合中。返回值为差集中元素的数量。
SDIFFSTORE destination key [key ...]
SADD key1 "a"
(integer) 1
SADD key1 "b"
(integer) 1
SADD key1 "c"
(integer) 1
SADD key2 "c"
(integer) 1
SADD key2 "d"
(integer) 1
SADD key2 "e"
(integer) 1
SDIFFSTORE key key1 key2
(integer) 2
SMEMBERS key
1) "a"
2) "b"
2.1.15 命令小结
命令 | 时间复杂度 | 描述 |
---|---|---|
sadd key element [element …] | O(k),k 是元素个数 | 向集合中添加一个或多个元素 |
srem key element [element …] | O(k),k 是元素个数 | 从集合中删除一个或多个元素 |
scard key | O(1) | 获取集合中元素的个数 |
sismember key element | O(1) | 判断一个元素是否在集合中 |
srandmember key [count] | O(n),n 是 count | 随机返回集合中的一个或多个元素 |
spop key [count] | O(n),n 是 count | 随机移除集合中的一个或多个元素 |
smembers key | O(k),k 是元素个数 | 返回集合中的所有元素 |
sinter key [key …] | O(m * k),k 是最小集合的元素个数,m 是集合个数 | 获取多个集合的交集 |
sinterstore key [key …] | O(m * k),k 是最小集合的元素个数,m 是集合个数 | 计算多个集合的交集并保存到目标集合 |
sunion key [key …] | O(k),k 是所有集合的元素总数 | 获取多个集合的并集 |
sunionstore key [key …] | O(k),k 是所有集合的元素总数 | 计算多个集合的并集并保存到目标集合 |
sdiff key [key …] | O(k),k 是所有集合的元素总数 | 获取多个集合的差集 |
sdiffstore key [key …] | O(k),k 是所有集合的元素总数 | 计算多个集合的差集并保存到目标集合 |
2.2 内部编码
集合类型的内部编码有两种:
内部编码类型 | 使用条件 | 描述 |
---|---|---|
intset | 当集合中的元素都是整数,且元素个数小于 set-max-intset-entries 配置(默认 512 个)时使用。 | 使用紧凑的内存结构,减少内存消耗。 |
hashtable | 当集合不满足 intset 条件(如元素个数过多或包含非整数元素)时使用。 | 使用哈希表结构,支持更高效的操作。 |
- 当元素个数较少并且都为整数时,内部编码为 intset:
sadd setkey 1 2 3 4
(integer) 4
object encoding setkey
"intset"
- 当元素个数超过 512 个,内部编码为 hashtable:
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 513
127.0.0.1:6379> object encoding setkey
"hashtable"
- 当存在元素不是整数时,内部编码为 hashtable:
127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> object encoding setkey
"hashtable"
2.3 使用场景
集合类型在标签管理中有着典型的应用场景。例如,用户 A 对娱乐和体育感兴趣,用户 B 对历史和新闻感兴趣,这些兴趣点可以被抽象为标签。通过这些数据,可以找出喜欢同一标签的用户以及用户之间的共同兴趣标签,这对提升用户体验和增强用户粘性非常有帮助。例如,在电子商务网站中,可以根据不同的标签为用户推荐个性化的产品。
- 给用户添加标签
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
- 给标签添加用户
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:4 user:9 user:28
- 删除用户下的标签
srem user:1:tags tag1 tag5
- 删除标签下的用户
srem tag1:users user:1
srem tag5:users user:1
- 计算用户的共同兴趣标签
sinter user:1:tags user:2:tags