1.1 基础
1.1.1 全局命令
Redis有五种数据结构,对于键来说有一些通用的命令
1、查看所有的键
**Keys **
127.0.0.1:6379> set name_1 kebe
OK
127.0.0.1:6379> set name_2 james
OK
127.0.0.1:6379> set name_3 wade
OK
127.0.0.1:6379> keys *
1) "name_3"
2) "name_1"
3) "name_2"
127.0.0.1:6379>
2、键总数
dbsize
127.0.0.1:6379> dbsize
(integer) 3
127.0.0.1:6379>
3、检查键是否存在
exists key 如果 key 存在返回 1,不存在返回 0;
127.0.0.1:6379> dbsize
(integer) 3
127.0.0.1:6379> exists name_1
(integer) 1
127.0.0.1:6379> exists key_4
(integer) 0
127.0.0.1:6379>
4、删除键
del key [key…] 无论是什么类型的数据格式,del命令都可以删除。删除成功返回 1,删除失败(不存在)返回 0;
127.0.0.1:6379> del name_1
(integer) 1
127.0.0.1:6379> del name_1
(integer) 0
127.0.0.1:6379> del name_2 name_3
(integer) 2
127.0.0.1:6379>
5、键过期
expire key seconds
-
Redis 支持对键添加过期时间,当超过过期时间后,会自动删除键;
-
ttl 命令会返回键的剩余过期时间,它有 3 中返回值:
-
大于等于 0 的整数:剩余过期时间
-
-1:键没有设置过期时间
-
-2:键不存在
-
127.0.0.1:6379> set name_1 kebe
OK
127.0.0.1:6379> set name_2 james
OK
127.0.0.1:6379> ttl name_3
(integer) -2
127.0.0.1:6379> expire name_1 60
(integer) 1
127.0.0.1:6379> ttl name_1
(integer) 56
127.0.0.1:6379> ttl name_2
(integer) -1
127.0.0.1:6379>
6、键的数据类型
type key 如果 key 不存在,返回 none
127.0.0.1:6379> type name_2
string
127.0.0.1:6379> lpush list_1 a b c d e
(integer) 5
127.0.0.1:6379> type list_1
list
127.0.0.1:6379> type name_4
none
127.0.0.1:6379>
1.1.2 数据结构和内部编码
type 命令就是返回对应 key 的数据结构类型
- string(字符串)
- hash(哈希)
- list(列表)
- set(集合)
- zset(有序集合)
以上这写都是Redis 对外的数据结构,实际Redis 还有自己的底层的内部编码,如下图
1.2 字符串(String)
1.2.1 常用命令
命令 | 描述 | 时间复杂度 |
---|---|---|
set key value | 添加 | O(1) |
get key | 获取 | O(1) |
del key [key…] | 删除/批量删除 | O(n) n 键的长度 |
mset key value [key value…] | 批量添加 | O(n) n 键的长度 |
mget key [key…] | 批量获取 | O(n) n 键的长度 |
incr key | 自增+1 | O(1) |
decr key | 自减-1 | O(1) |
incrby key increment | 增加指定长度 | O(1) |
decrby key decrement | 减少指定长度 | O(1) |
incrbyfloat key increment | 自增浮点数 | O(1) |
append key value | 追加 | O(1) |
strlen key | 字符串长度 | O(1) |
getset key value | 设置并返回原值 | O(1) |
setrange key offset value | 设置指定位置的字符 | O(1) |
getrange key start end | 获取指定位置的字符 | O(n),n 是字符串长度,由于获取字符串非常快,字符串长度不长可以视同为 O(1) |
1、示例:
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set name_1 "Kebe Bryant"
OK
127.0.0.1:6379> get name_1
"Kebe Bryant"
127.0.0.1:6379> set name_2 james
OK
127.0.0.1:6379> del name_2
(integer) 1
127.0.0.1:6379> set name_2 james
OK
127.0.0.1:6379> mget name_1 name_2
1) "Kebe Bryant"
2) "james"
127.0.0.1:6379> mset name_3 wade name_4 irving
OK
127.0.0.1:6379> set name_5 1
OK
127.0.0.1:6379> incr name_5
(integer) 2
127.0.0.1:6379> get name_5
"2"
127.0.0.1:6379> decr name_5
(integer) 1
127.0.0.1:6379> get name_5
"1"
127.0.0.1:6379> incrby name_5 3
(integer) 4
127.0.0.1:6379> get name_5
"4"
127.0.0.1:6379> decrby name_5 3
(integer) 1
127.0.0.1:6379> get name_5
"1"
127.0.0.1:6379> incrbyfloat name 2.3
"2.3"
127.0.0.1:6379> incrbyfloat name_5 2.3
"3.3"
127.0.0.1:6379> get name_5
"3.3"
127.0.0.1:6379> append name_1 "no.24"
(integer) 16
127.0.0.1:6379> get name_1
"Kebe Bryantno.24"
127.0.0.1:6379> strlen name_1
(integer) 16
127.0.0.1:6379> getset name_1 kebe
"kebe"
127.0.0.1:6379> setrange name_1 0 K
(integer) 4
127.0.0.1:6379> getrange name_1 0 -1
"Kebe"
127.0.0.1:6379>
1.2.2 内部编码
1、字符串类型的内部编码有 3 种
- int : 8 个字节的长整型
- enbstr :小于等于 39 个字节的字符串
- raw : 大于 39 个字节的字符串
Redis 会根据当前值得类型和长度决定使用那种内部编码实现:
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set name_1 kebe
OK
127.0.0.1:6379> object encoding name_1
"embstr"
127.0.0.1:6379> set name_2 123
OK
127.0.0.1:6379> object encoding name_2
"int"
127.0.0.1:6379> set name_3 "The 3rd leading score in NBA history,18 time All-star,4 time All-star MVP,The 2008 MVP,2 time NBA finals MVP,5 time NBA champion,A 6`6 guard from Lower Merion High School,In his twenty season,The BLACK MAMBA,The one,The only,No._24,Kobe Bryant."
OK
127.0.0.1:6379> object encoding name_3
"raw"
127.0.0.1:6379>
1.2.3 经典场景
1、缓存功能
web 开发我们经常会以MySQL、Oracle作为数据存储,使用Redis作为缓存。80%的请求被打倒缓存中获取对应数据,由于Redis可以支撑高并发的特性,可以大大服务对数据库的压力;
1、图例:
2、代码设计 (java)
/**
* 根据用户编号获取用户信息
* @param userId 用户编号
* @return UserModel
*/
public UserModel getUserInfo(String userId){
// system:user:info:2
String userKey = "s:u:i:" + userId;
// 获取用户
String userModel = stringRedisTemplate.opsForValue().get(userKey);
// 是否命中缓存
if (StringUtils.isBlank(userModel)) {
// 数据库获取用户信息
UserModel userModelData = userMapper.selectUserInfoByUserId(userId);
// 将获取的数据放入缓存
stringRedisTemplate.opsForValue()
.setIfPresent(userKey,userModelData.toString(),30,TimeUnit.MINUTES);
return userModelData;
}
return JSONObject.parseObject(userModel,UserModel.class);
}
2、计数
简单的标志位,例如:统计今日系统访问量,用户登录次数,分布式主键主键等
1、代码设计(java)
public void demo_1(){
// 系统每日访问 system:browse:20210801
String systemKey = "s:b:"+ LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
stringRedisTemplate.opsForValue().increment(systemKey);
// 用户登录次数 user:login:20210801
String loginKey = "u:l:"+ LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
stringRedisTemplate.opsForValue().increment(systemKey);
// 分部式主键 dataBase:table:id;
String idKey = "d:t:i";
Long id = stringRedisTemplate.opsForValue().increment(idKey);
}
3、共享session
多系统用户跳转的互相认证令牌,
1、图例
4、限速
有时候为了限制不当操作,系统在对应的请求方法上,添加访问次数的拦截;
1、代码设计(java)
/**
* 用户编号获取是否通过方法
* @param userId 用户
* @return true-过 false-不过
*/
public Boolean getShotLimitStatus(String userId) {
// 访问限制 short:limit:queryMoney:用户编号
String limitKey = "s:l:qM:"+userId;
// 1min 限制5次 set key value EX 60 NX
stringRedisTemplate.opsForValue().setIfPresent(limitKey, "1", 1, TimeUnit.MINUTES);
return stringRedisTemplate.opsForValue().increment(limitKey) < 6;
}
1.3 哈希(Hash)
1.3.1 常用命令
命令 | 描述 | 时间复杂度 |
---|---|---|
hset key field value | 添加key 列名 | O(1) |
hget key field | 获取key field | O(1) |
hdel key field [field…] | 删除key field | O(k) k是field的个数 |
hlen key | 获取key 个数 | O(1) |
hgetall key | 获取key 的 field 和 value | O(n) n是field总数 |
hmget field [field . … ] | 批量获取 | O(k) k是field的个数 |
hmset field value [field value . . . ] | 批量添加 | O(k) k是field的个数 |
hexists key field | 添加 key 的 field 是否存在 | O(1) |
hkeys key | 获取 key 的所有列名 | O(n) n是field总数 |
hvals key | 获取 key 的所有value | O(n) n是field总数 |
hsetnx key field value | 添加 key field 的value 不存在成功,反之失败 | O(1) |
hincrby key field increment | 和 string incrby一样 | O(1) |
hincrbyfloat key field increment | 和 string incrbyfloat一样 | O(1) |
hstrlen key field | 计算 Key 的 field 的长度 | O(1) |
1、示例:
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset user:1 id 1
(integer) 1
127.0.0.1:6379> hget user:1 id
"1"
127.0.0.1:6379> hset user:1 name kebe
(integer) 1
127.0.0.1:6379> hset user:1 sex 0
(integer) 1
127.0.0.1:6379> hdel user:1 id name
(integer) 2
127.0.0.1:6379> hlen user:1
(integer) 1
127.0.0.1:6379> hgetall user:1
1) "sex"
2) "0"
127.0.0.1:6379> hmget user:1 sex
1) "0"
127.0.0.1:6379> hmset user:1 name kebe id 1
OK
127.0.0.1:6379> hexists user:1 id
(integer) 1
127.0.0.1:6379> hkeys user:1
1) "sex"
2) "name"
3) "id"
127.0.0.1:6379> hvals user:1
1) "0"
2) "kebe"
3) "1"
127.0.0.1:6379> hsetnx user:1 id 2
(integer) 0
127.0.0.1:6379> hvals user:1
1) "0"
2) "kebe"
3) "1"
127.0.0.1:6379> hincrby user:1 id 2
(integer) 3
127.0.0.1:6379> hvals user:1
1) "0"
2) "kebe"
3) "3"
127.0.0.1:6379> hincrbyfloat user:1 id 2.3
"5.3"
127.0.0.1:6379> hvals user:1
1) "0"
2) "kebe"
3) "5.3"
127.0.0.1:6379> hstrlen user:1 id
(integer) 3
127.0.0.1:6379>
1.3.2 内部编码
1、ziplist (压缩列表):
当哈希类型元素个数小于hash-max- ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis 会使用ziplist作为哈希的内部实现,ziplist 使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
2、hashtable (哈希表):
当哈希类型无法满足ziplist的条件时,Redis 会使用hashtable作为哈希的内部实现,因此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为0(1)。
演示下哈希类型的内部编码及变化:
2.1、当 field 个数比较少且没有大的 value 时,内部编码是 ziplist
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hmset user:1 id 1 name kebe sex 0
OK
127.0.0.1:6379> object encoding user:1
"ziplist"
2.2、当有 value 大于 64 字节,内部编码会由 ziplist 变为 hashtable
127.0.0.1:6379> hset user:1 description "The 3rd leading score in NBA history,18 time All-star,4 time All-star MVP,The 2008 MVP,2 time NBA finals MVP,5 time NBA champion.A 6`6 guard from Lower Merion High School,In his twenty season"
(integer) 1
127.0.0.1:6379> object encoding user:1
"hashtable"
2.3、当 field 个数超过 512,内部编码也会由 ziplist 变为 hashtable
这个留个你们去演示吧,
1.3.3 经典场景
下图:
- 左边 是关系型数据表存储、展示用户信息,列是用户属性,行是用户信息
- 右边 是哈希类型存储,field 是用户属性,value 是用户信息
这个类型相较于String(字符串),缓存用户数据,哈希变的更直观,但是需要注意的是,如果实体中的属性是 null,那么 hash 中就不会存储对应的 field,还要注意 hash 在 ziplist 和 hashtable 两种内存编码的转换,hashtable 会消耗更多的内存
1、代码设计(java)
/**
* 根据用户编号获取用户信息
* @param userId 用户编号
* @return UserModel
*/
public UserModel getUserInfo(String userId){
// system:user:info:2 为了减少由于键过长造成内存的浪费,建议选择
String userKey = "s:u:i:"+userId;
// 获取用户
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(userKey);
// 是否命中缓存
if (entries.isEmpty()) {
// 数据库获取用户信息
UserModel userModel = userMapper.selectUserInfoByUserId(userId);
// 将获取的数据放入缓存
stringRedisTemplate.opsForHash().putAll(userKey, JSON.parseObject(JSON.toJSONString(userModel), Map.class));
// 过期时间 24h
stringRedisTemplate.expire(userKey,24, TimeUnit.HOURS);
return userModel;
}
return JSON.parseObject(JSON.toJSONString(entries), UserModel.class);
}
1.4 列表(List)
1.4.1 常用命令
命令 | 描述 | 时间复杂度 |
---|---|---|
rpush key value [value … . ] | 添加(右端进入) | O(k),k是元素个数 |
lpush key value [value …] | 添加(左端进入) | O(k),k是元素个数 |
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是列表长度 |
ltrim key start end | 根据索引范截取列表 | O(n),n是要裁剪的元素总数 |
lset key index value | 修改指定索引下标的元素 | O(n),n是索引的偏移量 |
blpop brpop | 阻塞操作 | O(1) |
1、示例
127.0.0.1:6379> lpush user:1 kebe
(integer) 1
127.0.0.1:6379> rpush user:1 james wade
(integer) 3
127.0.0.1:6379> linsert user:1 before james paul
(integer) 4
127.0.0.1:6379> lrange user:1 0 -1
1) "kebe"
2) "paul"
3) "james"
4) "wade"
127.0.0.1:6379> lindex user:1 1
"paul"
127.0.0.1:6379> llen user:1
(integer) 4
127.0.0.1:6379> lpop user:1
"kebe"
127.0.0.1:6379> rpop user:1
"wade"
127.0.0.1:6379> lrange user:1 0 -1
1) "paul"
2) "james"
127.0.0.1:6379> lrange user:1 0 -1
1) "paul"
2) "james"
127.0.0.1:6379> lrem user:1 1 paul
(integer) 1
127.0.0.1:6379> lpush user:1 kebe paul wade
(integer) 4
127.0.0.1:6379> ltrim user:1 0 1
OK
127.0.0.1:6379> lset user:1 0 love
OK
127.0.0.1:6379> lrange user:1 0 -1
1) "love"
2) "paul"
1.4.2 内部编码
1、列表类型的内部编码有两种。
-
ziplist (压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis 会选用ziplist来作为列表的内部实现来减少内存的使用。
-
linkedlist (链表):当列表类型无法满足ziplist的条件时,Redis 会使用因为列Iinkedlist作为列表的内部实现。
Redis3.2后quicklist内部编码,简单地说它是以一个ziplist为节点的Iinkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现,
1.4.3 经典场景
1、消息队列
Redis 的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插人元素,多个消费者客户端使用brpop命令阻塞式的“抢”尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
2、文档列表
全个周户有于自己的文事列来,观需建分页原示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素:
1、使用哈希(Hash)保存文章信息:
hmset acticle:1 title "活着" author "余华" createTime "1983年"
hmset acticle:2 title "平凡的世界" author "路遥" createTime "1986年"
hmset acticle:3 title "边城" author "沈从文" createTime "1934年"
hmset acticle:4 title "白鹿原" author "陈忠实" createTime "1993年"
2、使用列表(list)保存用户与文章信息:
lpush user:1 acticle:1 acticle:3
lpush user:2 acticle:2 acticle:4
3、列表获取用户和文章列表
acticles = lrange user:1 0 2
for (acticle in acticles){
hgetall acticle
}
使用列表类型保存和获取文章列表会存在两个问题。第一,如果每次 分页获取的文章个数较多,需要执行多次hgetall操作,此时可以考虑使用Pipeine (第3章会介绍)批量获取,或者考虑将文章数据序列化为字符串类型,使用mget批量获取。第二,分页获取文章列表时,lrange 命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元紫性能会变差,此时可以考虑将列表做二级拆分,或者使用Redis 3.2的quicklist内部编码实现,它结合ziplist和linkedlist的特点,获取列表中间范围的元素时也可以高效完成。
1.5 集合(Set)
1.5.1 常用命令
命令 | 描述 | 时间复杂度 |
---|---|---|
sadd key element [element …] | 添加元素 | O(k),k是元素个数 |
srem key element [element …] | 删除元素 | O(k),k是元素个数 |
scard key | 计算 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 …] 或 suionstore | 多个集合的并集 | O(k),k是多个集合元素个数和 |
sdiff key [key …] 或 sdiffstore | 多个集合的差集 | O(k),k是多个集合元素个数和 |
1、示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd user:1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> srem user:1 1 2
(integer) 2
127.0.0.1:6379> scard user:1
(integer) 3
127.0.0.1:6379> sismember user:1 4
(integer) 1
127.0.0.1:6379> srandmember user:1
"5"
127.0.0.1:6379> spop user:1
"4"
127.0.0.1:6379> sadd user:2 1 2 3 4 5
(integer) 5
127.0.0.1:6379> sadd user:3 1 2 3 4 5
(integer) 5
127.0.0.1:6379> sinter user:1 user:2
1) "3"
2) "5"
127.0.0.1:6379> sinter user:2 user:3
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> sunion user:1 user:2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> sinter user:1 user:2
1) "3"
2) "5"
127.0.0.1:6379> sadd user 6 7
(integer) 2
127.0.0.1:6379> sdiff user:1 user
1) "3"
2) "5"
1.5.2 内部编码
-
intset (整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis 会选用intset来作为集合的内部实现,从而减少内存的使用。
-
hashtable (哈希表):当集合类型无法满足intset的条件时,Redis 会使用hashtable作为集合的内部实现。
1、示例
127.0.0.1:6379> object encoding user
"intset"
127.0.0.1:6379> sadd user:3 kebe
(integer) 1
127.0.0.1:6379> object encoding user:3
"hashtable"
1.5.3 经典场景
集合类型比较典型的使用场景是标签( tag)。例如一一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的利益。
1.6 有序集合(Zset)
1.6.1 常用命令
命令 | 描述 | 时间复杂度 |
---|---|---|
zadd key score member [score member …] | 添加一个或多个元素 | O(kX log())k是添加成员的个数,n是当前有限集合成员个数 |
zcard key | 获取集合的成员数 | 0(1) |
zscore key member | 获取key 指定元素的分数 | 0(1) |
zrank key member zrevrank key member | 排名 | O(log(n),n是当前有序集合成员个数 |
zrem key member [member . . .] | 基础集合中的成员 | O(k*log(n)),k 是删除成员的个数,n是当前有序合成员个数 |
zincrby key increment member | 对应key 成员加分 | O(log(n)),n是当前有序集合成员个数 |
zrange key start end [withscores] zrevrange key start end [withscores] | 排名 | O(log(n)+k),k是要获取的成员个数,n是当前有序合成员个数 |
zrangebyscore key min max [withscores] zrevrangebyscore key max min [withscores] | 排名带分数 | O(log(n)+k),k是要获取的成员个数,n是当前有序合成员个数 |
zcount key | 返回集合成员 | O(log(n)),n是当前有序集合成员个数 |
zremrangebyrank 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(n×k)+O(m×log(m),n是成员数最小的有序集合成员个数,k是有序集合的个数,m是结果集中成员个数 | |
zinterstore destination numkeys key [key …] | 交集 | O(m)+O(m*log(m),n是所有有序集合成员个数和,m是结果集中成员个数 |
1、示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd user 1 kebe 2 james 3 wade
(integer) 3
127.0.0.1:6379> zcard user
(integer) 3
127.0.0.1:6379> score user kebe
(error) ERR unknown command 'score'
127.0.0.1:6379> zscore user kebe
"1"
127.0.0.1:6379> zrange user 0 3 withscores
1) "kebe"
2) "1"
3) "james"
4) "2"
5) "wade"
6) "3"
127.0.0.1:6379> zrevrank user kebe
(integer) 2
127.0.0.1:6379> zrevrank user james
(integer) 1
127.0.0.1:6379> zrevrank user wade
(integer) 0
127.0.0.1:6379> zrem user wade
(integer) 1
127.0.0.1:6379> zrange user 0 3 withscores
1) "kebe"
2) "1"
3) "james"
4) "2"
127.0.0.1:6379> zincrby user 1 kebe
"2"
127.0.0.1:6379> zrange user 0 3 withscores
1) "james"
2) "2"
3) "kebe"
4) "2"
127.0.0.1:6379> zrevrange user 0 3 withscores
1) "kebe"
2) "2"
3) "james"
4) "2"
127.0.0.1:6379> zincrby user 2 kebe
"4"
127.0.0.1:6379> zrangebyscore user 0 5 withscores
1) "james"
2) "2"
3) "kebe"
4) "4"
127.0.0.1:6379> zrevrangebyscore user 5 0 withscores
1) "kebe"
2) "4"
3) "james"
4) "2"
127.0.0.1:6379> zadd user 1 wade
(integer) 1
127.0.0.1:6379> zadd user 5 paul
(integer) 1
127.0.0.1:6379> zadd user 6 love
(integer) 1
127.0.0.1:6379> zremrangebyrank user 0 1
(integer) 2
127.0.0.1:6379> zrangebyscore user 0 10 withscores
1) "kebe"
2) "4"
3) "paul"
4) "5"
5) "love"
6) "6"
127.0.0.1:6379> zremrangebyscore user 0 4
(integer) 1
127.0.0.1:6379> zrangebyscore user 0 10 withscores
1) "paul"
2) "5"
3) "love"
4) "6"
127.0.0.1:6379> sadd score 1 kebe 2 james
(integer) 4
127.0.0.1:6379> zinterstore user:score 2 user score
(integer) 0
127.0.0.1:6379> sadd score 1 paul
(integer) 1
127.0.0.1:6379> zinterstore user:score 2 user score
(integer) 1
127.0.0.1:6379> zrange user:score 0 -1 withscores
1) "paul"
2) "6"
127.0.0.1:6379>
1.6.2 内部编码
-
**ziplist (压缩列表)😗*当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个)同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。
-
**skiplist (跳跃表)😗*当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降。
1、示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd user:1 1 kebe 2 james 3 wade 4 paul
(integer) 4
127.0.0.1:6379> object encoding user:1
"ziplist"
127.0.0.1:6379> zadd user:2 1 kebe 2 james 3 wade 4 paul ...... 130 love
(integer) 130
127.0.0.1:6379> object encoding user:2
"skiplist"
1.6.3 经典场景
有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播放数量、按照获得的赞数。本节使用赞数这个维度,记录每天用户上传视频的排行榜。
1、添加用户点赞数
例:用户kebe,上传了一个小金人视频,并获得12w个赞,使用有序集合 zadd 和 zincrby 功能:
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd movie kebe 120000
(error) ERR value is not a valid float
127.0.0.1:6379> zadd movie 120000 kebe
(integer) 1
127.0.0.1:6379> zadd movie 100000 james
(integer) 1
127.0.0.1:6379>
如获得一个赞可以使用 zincrby
127.0.0.1:6379> zincrby movie 1 kebe
"120001"
2、取消点赞数
127.0.0.1:6379> zrem movie 1 kebe
(integer) 1
3、获取top10
zrevrangebyscore movie +inf 0 withscores limit 0 10
1) "kebe"
2) "120000"
1.7 健康管理
1.7.1 单个键管理
1、键重命名 (rename key newKey)
- 如果newKey 不存在就会直接更新;
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set name kebe
OK
127.0.0.1:6379> rename name nameKebe
OK
127.0.0.1:6379> get nameKebe
"kebe"
127.0.0.1:6379> get name
(nil)
- 如果newKey 存在那么会将newKey对应的删除掉,再将的 key 替换成 newKey
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set kebe 36
OK
127.0.0.1:6379> set james 38
OK
127.0.0.1:6379> rename james kebe
OK
127.0.0.1:6379> get james
(nil)
127.0.0.1:6379> get kebe
"38"
- 为了防止被强制执行rename,他还提供了一个命令 renamenx, newKey 不存在才会更新,1-更新成功,0-更新失败
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set kebe 38
OK
127.0.0.1:6379> set james 39
OK
127.0.0.1:6379> renamenx james kebe
(integer) 0
127.0.0.1:6379> renamenx kebe paul
(integer) 1
127.0.0.1:6379> get kebe
(nil)
127.0.0.1:6379> get paul
"38"
127.0.0.1:6379> get james
"39"
2、随机返回一个Key (RandomKey)
127.0.0.1:6379> dbsize
(integer) 2
127.0.0.1:6379> randomkey
"paul"
127.0.0.1:6379> randomkey
"james"
3、键过期
- expire(设置超时时间)
127.0.0.1:6379> expire paul 500
(integer) 1
- ttl
- ≥ 0 ,键剩余的过期时间(ttl:秒 pttl:毫秒)
- -1 ,键没有设置过期时间
- -2 ,键不存在
127.0.0.1:6379> ttl paul
(integer) 495
127.0.0.1:6379> ttl james
(integer) -1
127.0.0.1:6379> ttl love
(integer) -2
- expireat 设置键的秒级过期时间戳 (20210823 21:00)
127.0.0.1:6379> expireat paul 61590542400000
(integer) 1
- pexpire
127.0.0.1:6379> set kebe 38
OK
127.0.0.1:6379> pexpire kebe 100000
(integer) 1
127.0.0.1:6379> ttl kebe
(integer) 95
- pexpireat
127.0.0.1:6379> pexpireat kebe 61590542400000
(integer) 1
- pttl
127.0.0.1:6379> pttl paul
(integer) 61588913832398073
- persist 清除键的过期时间
127.0.0.1:6379> persist paul
(integer) 1
1.7.2 遍历键
1、全量遍历键 (keys pattern)
-
- 代表匹配任意字符
-
[ ]代表匹配部分字符串
-
?代表匹配一个字符串
127.0.0.1:6379> set kebe 34
OK
127.0.0.1:6379> set love 34
OK
127.0.0.1:6379> dbsize
(integer) 4
127.0.0.1:6379> keys *
1) "paul"
2) "love"
3) "kebe"
4) "james"
2、渐进式遍历(scan cursor [match pattern] [count number]) 推荐使用
- cursor : 必传 是一个游标,第一次遍历从 0 开始,每次 scan 遍历完都会返回当前游标的值,直到游标值为 0,表示遍历结束
- match :pattern 可选,它是做一个模式匹配这和keys 的模式匹配很想
- count : number 可选,它是表明每次要遍历的键个数,默认是 10 ,这个值可以适当增大。
127.0.0.1:6379> scan 0 match "ke*"
1) "0"
2) 1) "kebe"
1.7.3 数据库管理
1、切换数据库 (select num)
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]>
2、清除数据库 flushAll/flushdb
- flushAll 清除所有库 慎用
- flushdb 清除当前库