Redis之基础篇
Redis简介
Redis是一种基于键值对(Key-Value)的NoSQL数据库,它支持string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法。因此Redis可以满足多种应用场景。而且因为Redis会将数据存储在内存中,因此它的读写性能非常好,Redis还具有将数据存到快照或者日志上的机制,这便于数据恢复。Redis还提供键过期、发布订阅、事务、流水线、lua脚本等附加功能
Redis特性
- 速度快,读写速度据统计可以达到
10w/s
- 基于键值对的数据结构服务器,支持字符串、哈希、列表、集合等数据结构和算法
- 功能丰富,提供键过期、发布订阅、事务、流水线、lua脚本等附加功能
- 简单稳定
- 支持多种语言的客户端
- 提供了
RDB
和AOF
两种持久化策略 - 支持主从复制
- 支持高可用和分布式
Redis的使用场景
- 缓存
- 排行榜系统
- 社交网络点赞、粉丝等功能
- 计数器应用如:播放数、浏览数
- 消息队列功能
Linux下安装Redis并启动
安装Redis
搭建C语言的编译环境
yum install gcc-c++
gcc --version # 校验是否安装成功
下载Redis
在usr
文件夹下输入以下命令下载redis
wget https://download.redis.io/releases/redis-6.2.1.tar.gz
解压redis-6.2.1.tar.gz
tar -zxvf redis-6.2.1.tar.gz
编译
进入到redis-6.2.1
文件夹中,建入make
执行编译,完成后执行make install
,自此安装完成。
注意:请预先安装c语言编译环境,否则到此步骤会报错
Redis启动
redis安装完后会产生几个以redis开头的可执行文件,这些Redis Shell
可以用来启动、停止Redis。
可执行文件 | 作用 |
---|---|
redis-server | 启动Redis |
redis-cli | Redis客户端 |
redis-bechmark | Redis测试工具 |
redis-check-aof | Redis AOF持久化文件检测和修复工具 |
redis-check-rdb | Redis RDB持久化文件检测和修复工具 |
前台启动
这种方式会使用Redis默认配置来启动,当退出终端或者按住Ctrl+C
就会退出。
redis-server /etc/redis/redis.conf
后台启动
修改redis.conf,找到daemonize,将此参数设置为yes,如下图所示
启动即可
redis-server /etc/redis/redis.conf
Redis关闭服务端
- redis-cli shutdown
- kill redis进程号
注意最好不要使用kill -9来强制杀死redis服务,这种情况不会做持久化操作,极端情况会造成AOF和复制丢失数据。
Redis的常见操作
查看所有键
该命令会将所有的键输出
127.0.0.1:6379> keys *
(empty array)
添加和删除字符串键值
# 添加字符串
127.0.0.1:6379> set hello zayton
OK
127.0.0.1:6379> set spring boot
OK
127.0.0.1:6379> set java basic
OK
# 查看所有键
127.0.0.1:6379> keys *
1) "java"
2) "spring"
3) "hello"
# 删除字符串
127.0.0.1:6379> del hello
(integer) 1
# 再次查看所有键
127.0.0.1:6379> keys *
1) "java"
2) "spring"
#插入一个列表类型的键值对
127.0.0.1:6379> rpush mylist a b c d e
(integer) 5
检查key是否存在
# exists key
127.0.0.1:6379> exists java
(integer) 1
该命令如果键存在则返回1,不存在则返回0
设置键过期
Redis支持键过期时间,当超过过期时间后,会自动删除键,下面我们给键设置10秒过期,然后用ttl命令观察剩余过期时间。
127.0.0.1:6379> set hello zayton
OK
127.0.0.1:6379> expire hello 10
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 8
127.0.0.1:6379> ttl hello
(integer) 6
127.0.0.1:6379> ttl hello
(integer) 4
127.0.0.1:6379> ttl hello
(integer) 4
127.0.0.1:6379> ttl hello
(integer) 1
127.0.0.1:6379> ttl hello
(integer) -2
127.0.0.1:6379>
ttl命令会返回三种返回值:
- 大于0表示剩余过期时间
-1
表示没设置过期时间-2
表示键不存在
查看键的数据结构
127.0.0.1:6379> type java
string
127.0.0.1:6379> type mylist
list
字符串类型常见命令
设置值
set key value [ex seconds] [px milliseconds] [nx|xx]
示例,设置一个key为hello,value为world的字符串
127.0.0.1:6379> set hello world
OK
- ex seconds:为键设置秒级过期时间
- px milliseconds:为键设置毫秒级过期时间
- nx:若键不存在才能设置成功
- xx:若键存在才能设置成功
setnx、setex
setnx的作用和set的nx选项一样,都是键不存在才能设置成功。因为redis单线程命令处理机制,如果有多个客户端同时执行setnx key value
,则只会有一个成功,因此setnx常用于分布式锁,是一种乐观锁。上述设置了key为hello的键值对,下面我们用setnx命令再次设置,会发现值没有变化。
127.0.0.1:6379> setnx hello world2
(integer) 0
127.0.0.1:6379> get hello
"world"
setex相当于set指令的ex选项
# 设置一个10s过期的键值对
127.0.0.1:6379> SETEX zayton 10 squid
OK
127.0.0.1:6379> get zayton
"squid"
127.0.0.1:6379> get zayton
"squid"
# 10s后查询
127.0.0.1:6379> get zayton
(nil)
获取值
get key
批量设置值、获取值
mset key value [key value ...]
gset key [key ...]
示例
127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"
注意:如果需要从redis中获取大量key值,建议使用mget
执行n次get命令请求模型如下图所示
# 耗时
n次get时间 = n次网络时间 + n次命令时间
而执行一次mget命令请求模型如下图所示
#耗时
n次get时间 = 1次网络时间 + n次命令时间
从耗时我们可以看出,一次mget
请求只需要消耗一次网络时间,而n次get
请求需要消耗n次网络时间,故而使用mget
有助于提高业务处理效率。
计数
incr key
命令用于对值进行自增操作,若值存在且为整数,则自增;若值不存在,则创建并设置初始值为1;若值存在且非整数,则返回错误。并且因为redis
是单线程架构,无需考虑使用CAS
机制来保证线程安全,而是在服务端中都按顺序自增,所以性能比较好。
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> set zayton squid
OK
127.0.0.1:6379> incr zayton
(error) ERR value is not an integer or out of range
127.0.0.1:6379> incr num
(integer) 2
除了incr key
命令,redis还提供了decr key(自减)
、incrby key (自增指定数字)
、
decrby key(自减指定数字)
、incrbyfloat key(自增浮点数)
127.0.0.1:6379> decr num
(integer) 1
127.0.0.1:6379> incrby num 10
(integer) 11
127.0.0.1:6379> decrby num 5
(integer) 6
127.0.0.1:6379> incrbyfloat floatNum 2.2
"2.2"
字符串类型典型使用场景
缓存功能
如下所示,可将常用的数据库数据存到redis中提高访问数据,建议使用的key为表名:对象名:id,例如userInfo:user:1
用于获取用户的基础信息示例
public UserInfo getUserInfo(long id){
userRedisKey = "user:info:" + id;
// 从Redis获取值
value = redis.get(userRedisKey);
if (value != null) {
// 将值进行反序列化为UserInfo并返回结果
userInfo = deserialize(value);
return userInfo;
}
计数
可以用作视频播放量计数,如下代码示例
public long incrVideoCounter(long id) {
key = "video:playCount:" + id;
return redis.incr(key);
}
共享Session
为了保证用户信息在集群场景下能够共用一个session,可以在另起一台服务器搭建redis用来保存用户信息,避免用户因为负载均衡在各个服务器之间每次都要重新登陆。
限速
在日常中会发现很多验证码登录限制每分钟获取验证码的频率,这是出于安全考虑设计的,那么可以用redis实现该功能。
phoneNum = "138xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <=5){
// 通过
}else{
// 限速
}
哈希类型常见命令
哈希类型指存储键值对的数据结构,也叫做字典、关联数组;例如value={{field1,value1},…{filedN,valueN}}。
设置值和获取值
# 设置值
hset key field value
# 获取值
hget key field
示例
127.0.0.1:6379> hset user:1 name squid
(integer) 1
127.0.0.1:6379> hget user:1 name
"squid"
删除field
HDEL key field
示例
127.0.0.1:6379> hdel user:1 name
(integer) 1
hdel可以删除一个或多个field,返回结果为成功删除filed的个数
计算哈希类型的field的个数
hlen key
示例
127.0.0.1:6379> hlen user:1
(integer) 1
批量设置值、获取值
hmset key field value [field value ...]
hmget key field [field ...]
示例
127.0.0.1:6379> hmset user:1 name zayton age 18 sex male
OK
127.0.0.1:6379> hmget user:1 name age sex
1) "zayton"
2) "18"
3) "male"
判断field是否存在
hexists key field
示例
127.0.0.1:6379> hexists user:1 name
(integer) 1
获取所有field、所有value、所有field-value
# 获取所有field
hkeys key
# 获取所有value
hvals key
# 获取所有field-value
hgetall key
示例
127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> hvals user:1
1) "zayton"
2) "18"
3) "male"
127.0.0.1:6379> hgetall user:1
1) "name"
2) "zayton"
3) "age"
4) "18"
5) "sex"
6) "male"
建议:若开发过程中果一定要获取全部field-value,可以使用hscan命令,而非hgetall,避免造成redis拥堵。
自增filed
hincrby key field
hincrbyfloat key field
示例
127.0.0.1:6379> hincrby hash num 1
(integer) 1
127.0.0.1:6379> hincrbyfloat hash money 1.5
"1.5"
计算field的长度
hstrlen key field
示例
# 获取user:1 的name的长度
127.0.0.1:6379> HSTRLEN user:1 name
(integer) 6
127.0.0.1:6379>
哈希类型命令的时间复杂度
命令 | 时间复杂度 |
---|---|
hset key field value | O(1) |
hget key field | O(1) |
hdel key field [field …] | O(k),k是field个数 |
hlen key | O(1) |
hgetall key | O(n),n是field总数 |
hmget field [field …] | O(k),k是field个数 |
hmset field value [field value …] | O(k),k是field个数 |
hexists key field | O(1) |
hkeys key | O(n),n是field总数 |
hvals key | O(n),n是field总数 |
hsetnx key field value | O(1) |
hincrby key field increment | O(1) |
hincrbyfloat key field increment | O(1) |
hstrlen key field | O(1) |
哈希类型典型使用场景
在日常开发中,如果我们要缓存某行用户信息,那么使用哈希类型存储非常合适不过了。
使用哈希类型缓存信息主要有以下原因
- 相较于字符串(占用过多的键,内存占用量较大),哈希内聚更好更易于维护大量的用户信息。
- 如果使用序列化字符串存储,序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据取出进行反序列化,更新后再序列化到Redis中。
- 操作简单,修改用户字段更加方便,使用合理可以减少内存空间的使用。
但是需要注意的是哈希存储相对二维表更加稀疏,如上图,二维表中null的数据,在哈希表中key是完全不存在的,用户使用时需要考虑到这一点。
列表类型常见命令
列表的概念与Java中的List差不多,都是线性结构,可以用来存储多个有序的字符串,允许重复元素,它可以充当队列和栈。
添加操作
从右边插入元素
rpush key value [value ...]
示例
# 右边一次插入 a b c d e f
127.0.0.1:6379> rpush list a b c d e f
(integer) 6
# lrange key start stop命令可以从左到右获取指定范围内的列表元素
127.0.0.1:6379> lrange list 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
从左边插入元素
lpush key value [value ...]
示例
127.0.0.1:6379> lpush leftList e f g
(integer) 3
127.0.0.1:6379> lrange leftList 0 -1
1) "g"
2) "f"
3) "e"
向某个元素前或者后插入元素
linsert key before|after pivot value
示例:向列表list中的a元素后插入元素s
127.0.0.1:6379> linsert list after a s
(integer) 7
127.0.0.1:6379> lrange list 0 -1
1) "a"
2) "s"
3) "b"
4) "c"
5) "d"
6) "e"
7) "f"
查找操作
获取指定范围内的元素列表
lrange key start end
示例
# 查询列表list 下标0-2的元素
127.0.0.1:6379> lrange list 0 2
1) "a"
2) "s"
3) "b"
# 查询列表list所有元素
127.0.0.1:6379> lrange list 0 -1
1) "a"
2) "s"
3) "b"
4) "c"
5) "d"
6) "e"
7) "f"
# 查询不存在的列表list2的元素
127.0.0.1:6379> lrange list2 0 1
(empty array)
获取列表指定索引下标的元素
lindex key index
示例
127.0.0.1:6379> lindex list 3
"c"
获取列表长度
llen key
示例
127.0.0.1:6379> llen list
(integer) 7
删除操作
从列表左侧弹出元素
lpop key
示例
127.0.0.1:6379> lpop list
"a"
127.0.0.1:6379> lrange list 0 -1
1) "s"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
从列表右侧弹出
rpop key
示例
127.0.0.1:6379> rpop list
"f"
127.0.0.1:6379> lrange list 0 -1
1) "s"
2) "b"
3) "c"
4) "d"
5) "e"
删除指定元素
lrem key count value
分为以下三种情况:
- count>0,从左到右,删除最多count个元素。
- count<0,从右到左,删除最多count绝对值个元素
- count=0,删除所有。
示例
127.0.0.1:6379> lrange list 0 -1
1) "s"
2) "b"
3) "c"
4) "d"
5) "e"
127.0.0.1:6379> lrem list 1 s
(integer) 1
127.0.0.1:6379> lrem list -1 e
(integer) 1
127.0.0.1:6379> lrem list 0 d
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "b"
2) "c"
按照索引范围修剪列表
ltrim key start end
示例:保留列表list第2个到第5个元素
127.0.0.1:6379> lpush list a a b d
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "d"
2) "b"
3) "a"
4) "a"
5) "b"
6) "c"
127.0.0.1:6379> ltrim list 1 4
OK
127.0.0.1:6379> lrange list 0 -1
1) "b"
2) "a"
3) "a"
4) "b"
修改操作
修改指定索引下标的元素
lset key index newValue
示例:在列表list下标为4的位置插入元素‘zayton’
127.0.0.1:6379> lset list 3 zayton
OK
127.0.0.1:6379> lrange list 0 -1
1) "b"
2) "a"
3) "a"
4) "zayton"
阻塞操作
blpop key [key ...] timeout
brpop key [key ...] timeout
blpop示例,若列表为空,当timeout大于0时,时间耗尽后返回nil;当tinmeout等于0时,一直阻塞等待,直到列表有数据。若列表不为空,当timeout大于0时,时间耗尽后返回数据;当timeout等于0时,立即返回数据。
127.0.0.1:6379> brpop blist 3
(nil)
(3.02s)
127.0.0.1:6379> bprop list 0
# 此时通过另一个客户端push元素zayton,该阻塞会弹出元素zayton
127.0.0.1:6379> brpop blist 0
1) "blist"
2) "zayton"
(61.12s)
列表命令时间复杂度
命令 | 时间复杂度 |
---|---|
rpush key value [value…] | O(k),k是field个数 |
lpush key value [value …] | O(k),k是field个数 |
linsert key beforelafter pivot value | O(n),n是pivot 距离列表头或尾的距离 |
lrange key start end | o(s+n),s是start偏移量,n是start到end的范围 |
lindex key index | O(n),n是索引的偏移量 |
llen key | O(1) |
lpop key | O(1) |
rpop key | O(1) |
lrem count value | O(n),n是field总数 |
ltrim key start end | O(n),n是要裁剪的元素总数 |
lset key index value | O(n),n是索引的偏移量 |
blpop brpop | O(1) |
列表类型典型使用场景
Redis的lpush+brpop命令组合即可实现阻塞队列,如下图,每个消费者只需要关注自己需要的文章列表即可,生产者只需不断使用;push
添加文章即可。
关于列表更多的使用口诀如下:
- lpush+lpop=Stack(栈)
- lpush+rpop=Queue(队列)
- lpsh+ltrim=Capped Collection(有限集合)
- lpush+brpop=Message Queue(消息队列)
集合类型常见命令
集合类型可以保存多个字符串元素,但它的元素是无序的且不能重复,一个集合最多可以存储2^32-1个元素。接下来我们来说说它常见的命令:
添加操作
sadd key element [element ...]
示例,可以看到第二次添加的元素并未成功
127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a b
(integer) 0
删除操作
srem key element [element ...]
示例
127.0.0.1:6379> srem set a b
(integer) 2
127.0.0.1:6379> srem set zayton
(integer) 0
计算元素个数
scard的时间复杂度为O(1),它不会遍历集合所有元素,而是通过Redis内部的变量得来的。
scard key
示例
127.0.0.1:6379> scard set
(integer) 1
判断元素是否在集合中
sismember key element
示例
# 不存在则返回0
127.0.0.1:6379> sismember set a
(integer) 0
# 存在则返回1
127.0.0.1:6379> sismember set c
(integer) 1
随机从集合返回指定个数元素
srandmember key [count]
示例
127.0.0.1:6379> sadd set a b d e
(integer) 4
127.0.0.1:6379> srandmember set 3
1) "e"
2) "a"
3) "b"
# count不填默认为1
127.0.0.1:6379> srandmember set
"a"
从集合随机弹出元素
与srandmember
相似,只不过spop
会将元素从集合中删除
spop key
示例
127.0.0.1:6379> spop set
"c"
127.0.0.1:6379> smembers set
1) "e"
2) "a"
3) "d"
4) "b"
# Redis从3.2版本开始,spop也支持[count]参数
127.0.0.1:6379> spop set 2
1) "e"
2) "d"
127.0.0.1:6379> smembers set
1) "a"
2) "b"
获取所有元素
smembers key
示例
127.0.0.1:6379> smembers set
1) "a"
2) "b"
求多个集合的交集
sinter key [key ...]
示例
127.0.0.1:6379> sadd set1 a b c d
(integer) 4
127.0.0.1:6379> sadd set2 a b e f
(integer) 4
127.0.0.1:6379> sinter set1 set2
1) "b"
2) "a"
求多个集合的并集
sunion key [key ...]
示例
127.0.0.1:6379> sunion set1 set2
1) "e"
2) "f"
3) "a"
4) "d"
5) "c"
6) "b"
求多个集合的差集
这个差集指的是首个集合中有的而其他集合中没有的元素。
sdiff key [key ...]
示例
127.0.0.1:6379> sdiff set1 set2
1) "d"
2) "c"
将交集、并集、差集的结果保存
sinterstore destination key [key ...]
sunionstore destination key [key ...]
sdiffstore destination key [key ...]
示例
127.0.0.1:6379> sinterstore set3 set1 set2
(integer) 2
127.0.0.1:6379> smembers set3
1) "a"
2) "b"
127.0.0.1:6379> sunionstore set4 set1 set2
(integer) 6
127.0.0.1:6379> smembers set4
1) "e"
2) "f"
3) "a"
4) "d"
5) "c"
6) "b"
127.0.0.1:6379> sdiffstore set5 set1 set2
(integer) 2
127.0.0.1:6379> smembers set5
1) "d"
2) "c"
集合类型命令的时间复杂度
命令 | 时间复杂度 |
---|---|
zadd key score member [score member …] | O(k×1og(n)),h是添加成员的个数,"是当前有序集合成员个数 |
zcard key | O(1) |
scard key | O(1) |
sismember key element | O(1) |
srandmember key [count] | O(count) |
spop key | O(1) |
smembers key | O(n) ,n是元素总数 |
sinter key [key …]或者 sinterstore | O(m*k),k是多个集合中元素最少的个数,m是键个数 |
sunion key [key …]或者 sunionstore | O(k),k是多个集合元素个数和 |
sdiff key [key …]或者 sdiffstore | O(k),k是多个集合元素个数和 |
集合类型典型使用场景
- sadd=Tagging(标签,例如体育网站对各类体育类型的标签)
- spop/srandmember=Random item(生成随机数,比如抽奖)
- sadd+sinter=Social Graph(社交需求)
有序集合类型常见命令
与集合差不多,相比集合多了score
属性,使得集合有序,且它保留了集合的不可重复元素,但score
是可重复的。
添加操作
zadd key score member [score member ...]
示例
127.0.0.1:6379> zadd user:ranking 1 zayton 2 jack 3 james
(integer) 3
redis3.2添加了四个选项:
- nx:member必须不存在,才可以设置成功,用于添加。
- xx:member必须存在,才可以设置成功,用于更新。
- ch:返回此次操作后,有序集合元素和分数发生变化的个数
- incr:对score做增加,相当于后面介绍的zincrby。
相比于集合多了排序字段,但是添加元素也由O(1)变为O(log(n))。
计算指定key的有序集合的大小
zcard key
示例
127.0.0.1:6379> zcard user:ranking
(integer) 3
计算某个成员的分数
zscore key member
示例
127.0.0.1:6379> zscore user:ranking zayton
"1"
# 如果成员不存在则返回nil
127.0.0.1:6379> zscore user:ranking squid
(nil)
计算成员的排名
# 升序排名
zrank key member
# 降序排名
zrevrank key member
示例
127.0.0.1:6379> zrank user:ranking zayton
(integer) 0
127.0.0.1:6379> zrevrank user:ranking zayton
(integer) 2
删除成员
zrem key member [member ...]
示例
127.0.0.1:6379> zrem user:ranking zayton
(integer) 1
增加成员的分数
zincrby key increment member
示例
127.0.0.1:6379> zincrby user:ranking 20 jack
"22"
127.0.0.1:6379> zincrby user:ranking 4 jack
"26"
获取指定排名范围内的成员以及score
zrange key start end [withscores]
zrevrange key start end [withscores]
示例
127.0.0.1:6379> zrange user:ranking 0 -1 withscores
1) "james"
2) "3"
3) "jack"
4) "26"
返回指定分数范围的成员
zrangebyscore表示升序,zrevrangebyscore表示降序,min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]
示例
127.0.0.1:6379> zrangebyscore user:ranking 0 50 withscores
1) "james"
2) "3"
3) "jack"
4) "26"
# 获取20到无限大的成员
127.0.0.1:6379> zrangebyscore user:ranking (20 +inf withscores
1) "jack"
2) "26"
返回指定分数范围成员个数
zcount key min max
示例
127.0.0.1:6379> zcount user:ranking 10 30
(integer) 1
删除指定排名内的升序元素
zremrangebyrank key start end
示例
127.0.0.1:6379> zadd user:ranking 40 zayton
(integer) 1
127.0.0.1:6379> zrange user:ranking 0 -1
1) "james"
2) "jack"
3) "zayton"
127.0.0.1:6379> zremrangebyrank user:ranking 0 2
(integer) 3
删除指定分数范围的成员
zremrangebyscore key min max
示例
127.0.0.1:6379> zadd user:ranking 10 zayton 24 jack 26 james 35 squid
(integer) 4
127.0.0.1:6379> zremrangebyscore user:ranking 0 25
(integer) 2
求多个集合的交集
zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
这个命令参数较多,下面分别进行说明:
- destination:交集计算结果保存到这个键。
- numkeys:需要做交集计算键的个数。
- key[key…]:需要做交集计算的键。
- weights weight[weight…]:每个键的权重,在做交集计算时,每个键中的每个member会将自己分数乘以这个权重,每个键的权重默认是1。
- aggregate sum|min|max:计算成员交集后,分值可以按照sum(和)、min(最小值)、max(最大值)做汇总,默认值是sum。
示例
127.0.0.1:6379> zinterstore zset3 2 zset1 zset2
(integer) 2
127.0.0.1:6379> zrange zset3 0 -1 withscores
1) "jack"
2) "73"
3) "squid"
4) "135"
# zset2的权重变为0.5,并且取两个集合中最大值
127.0.0.1:6379> zinterstore zset3 2 zset1 zset2 weights 1 0.5 aggregate max
(integer) 2
127.0.0.1:6379> zrange zset3 0 -1 withscores
1) "jack"
2) "24.5"
3) "squid"
4) "50"
求多个集合的并集
zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
示例
127.0.0.1:6379> zunionstore zset4 2 zset1 zset2
(integer) 7
127.0.0.1:6379> zrange zset4 0 -1 withscores
1) "zayton"
2) "10"
3) "tom"
4) "19"
5) "james"
6) "26"
7) "jack"
8) "73"
9) "jerry"
10) "77"
11) "mike"
12) "101"
13) "squid"
14) "135"
有序集合命令的时间复杂度
命令 | 时间复杂度 |
---|---|
zadd key score member [score member …] | O(k*log(n)),是添加成员的个数,n是当前有序集合成员个数 |
zcard key | O(1) |
zscore key member | O(1) |
zrank key member zrevrank key member | O(log(n)),n 是当前有序集合成员个数 |
zrem key member [member …] | O(k*log(n)),是删除成员的个数,n是当前有序集合成员个数 |
zincrby key increment member | O(log(n)),n 是当前有序集合成员个数 |
zrange key start end [withscores] zrevrange key start end [withscores] | O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 |
zrangebyscore key max min [withscores] zrevrangebyscore key max min [withscores] | O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 |
zcount | O(log(n)),n 是当前有序集合成员个数 |
zremrengebyrank key start end | O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数 |
zremrangebyscore key min max | O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数 |
zinterstore destination numkeys key [key …] | O(nk+O(mlog(m)),n是成员数最小的有序集合成员个数,k是有序集合的个数,m 是结果集中成员个数 |
zunionstore destination numkeys key [key …] | O(nk+O(mlog(m)),n是成员数最小的有序集合成员个数,k是有序集合的个数,m 是结果集中成员个数 |
有序集合典型使用场景
可用于点赞、播放量排行榜等。
例如点赞功能:
# 张三获得10个点赞数
127.0.0.1:6379> zadd user:ranking:20230901 10 zhangsan
(integer) 1
# 小三获得3个点赞数
127.0.0.1:6379> zadd user:ranking:20230901 3 xiaosan
(integer) 1
# 老三获得6个点赞数
127.0.0.1:6379> zadd user:ranking:20230901 6 laosan
(integer) 1
# 张三点赞数+1
127.0.0.1:6379> zincrby user:ranking:20230901 1 zhangsan
"11"
# 查看点赞数前三
127.0.0.1:6379> zrevrange user:ranking:20230901 0 2
1) "zhangsan"
2) "laosan"
3) "xiaosan"
# 张三被取消一个点赞
127.0.0.1:6379> zincrby user:ranking:20230901 -1 zhangsan
"10"
127.0.0.1:6379> zrevrange user:ranking:20230901 0 2
1) "zhangsan"
2) "laosan"
3) "xiaosan"
# 小三是作弊的,被删掉
127.0.0.1:6379> zrem user:ranking:20230901 xiaosan
(integer) 1
127.0.0.1:6379> zrevrange user:ranking:20230901 0 2
1) "zhangsan"
2) "laosan"
参考
https://book.douban.com/subject/26971561/