第2章 API 的理解和使用
2.1 预备
2.1.1 全局命令
2.1.2 数据结构和内部编码
每种基本类型数据结构都有自己的底层的内部编码实现。
Redis 这样设计有两种好处:
第一,它可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数据结构和命令。
第二,多种内部编码实现可以再不同的场景发挥各自的优势。
2.1.3 单线程架构
Redis 使用了单线程架构和 I/O 多路复用模型来实现高性能的数据库服务。
为什么单线程还这么快?
第一,纯内存访问
第二,非阻塞 I/O
第三,单线程避免了线程切换和竞态产生的消耗。
2.2 字符串:string
键都是字符串类型
字符串类型的最大值不能超过512MB
2.2.1 命令
- 设置值
set key ex seconds value #设置秒级过期时间
set key px milliseconds value #设置毫秒级过期时间
set key value nx #键不存在时,设置成功
set key value xx #键存在时,设置成功,常用于更新操作
setex key secends value #类似于ex
setnx key value #类似于nx
- 获取值
get key
- 批量设置值
- 批量获取值
- 计数
incr key #自增
decr key #自减
incrby key #自增指定数字
decrby key #自减指定数字
incrbyfloat #自增浮点数
不常用命令
- 追加值
append key value
- 字符串长度
strlen key
- 设置并返回原值
getset key value
- 设定指定位置的字符
setrange key offset value
- 获取部分字符串
getrange key start end
2.2.2 内部编码
字符串类型内部编码有三种:
- int:8个字节的长整型
- embstr:小于等于39个字符的字符串
- raw:大于39个字节的字符串
2.2.3 典型使用场景
- 缓存功能
- 计数
- 共享Session
- 限速
2.3 哈希:hash
2.3.1命令
- 设置值
hset user:1 name tom
- 获取
hget user:1 name
- 删除值
hdel user2 name
- 批量设置,获取值
hmset user:2 name huangxuwei age 21 city chenzhou
OK
hmget user:2 name city
1) "huangxuwei"
2) "chenzhou"
- 计数
hlen user:1
- 判断是否存在
hexists user:1 name
- 获取所有 field
hkeys user:2
1) "name"
2) "age"
3) "city"
- 获取所有 value
hvals user:2
1) "huangxuwei"
2) "21"
3) "chenzhou"xxxxxxxxxx 127.0.0.1:6379>
- 获取所有 field-value
hgetall user:2
1) "name"
2) "huangxuwei"
3) "age"
4) "21"
5) "city"
6) "chenzhou"
2.3.2 内部编码
- ziplist:更省内存
- hashtable:更快
- 当field 个数较少且没有较大的 value 时,内部编码为 ziplist
- 当 value 大于64字节,或者 field个数超过512时,内部编码由 ziplist 转为 hashtable
2.3.3 使用场景
哈希类型与关系型数据库的区别
- 哈希类型是稀疏的,而关系型数据库是完全结构化的
- 关系型数据库可以做复杂的关系查询,而 Redis 去模拟关系型复杂查询成本高
三种方法缓存用户信息
- 原生字符串类型
- 序列化字符串类型
- 哈希类型
2.4 列表:list
一个列表最多可以存储2的32次方减1个元素
有序可重复
2.4.1 命令
- 添加,查看,删除,获取长度
127.0.0.1:6379> rpush listkey key a b c d 1 2 3 4,5 #添加
(integer) 9
127.0.0.1:6379> lrange listkey 0 -1 #查看
1) "key"
2) "a"
3) "b"
4) "c"
5) "d"
6) "1"
7) "2"
8) "3"
9) "4,5"
127.0.0.1:6379> lpop listkey #左删除
"key"
127.0.0.1:6379> rpop listkey #右删除
"4,5"
127.0.0.1:6379> lrem listkey 1 a #删除指定元素
(integer) 1
127.0.0.1:6379> lrange listkey 0 -1
1) "b"
2) "c"
3) "d"
4) "e"
5) "f"
6) "g"
7) "h"
8) "i"
9) "j"
10) "k"
11) "l"
12) "m"
13) "n"
127.0.0.1:6379> ltrim listkey 1 8 #保留指定区间
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "c"
2) "d"
3) "e"
4) "f"
5) "g"
6) "h"
7) "i"
8) "j"
127.0.0.1:6379> llen listkey #获取长度
(integer) 4
- 修改操作
127.0.0.1:6379> rpush listkey java cpp python
(integer) 3
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "cpp"
3) "python"
127.0.0.1:6379> lset listkey 1 javaee #修改指定索引下标的元素
OK
127.0.0.1:6379> lset listkey 2 spring
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "javaee"
3) "spring"
- 阻塞操作
2.4.2 内部编码
- ziplist:当列表元素小于 list-max-ziplist-entries 配置值(512),同时列表中元素的值都小于 **list-max-ziplist-value **配置值(默认64字节)Redis 使用ziplist 来作为列表的内部编码以减少内存的使用。
- linkedlist:当ziplist条件不满足时,Redis 使用linkedlist来作为列表的内部编码。
- quicklist:Redis 3.2版本提供,它是一个 ziplist 为节点的 linkedlist,结合了两者的优势
2.4.3 使用场景
- 消息队列
- 文章列表
口诀:
- lpush + lpop = Stack
- lpush + rpop = Queue
- lpush + ltrim =Capped Collection
- lpush + brpop = Message Queue
2.5 集合:set
无序互异性
一个集合最多包含2的32次方减1个元素
Redis 不仅支持集合的增删查改,还支持集合取交集,并集,差集。
2.5.1 命令
集合内:
- 添加元素
sadd myset test a b c d
- 删除元素
srem myset test
- 计算元素个数
scard myset
- 查看所有元素
127.0.0.1:6379> SMEMBERS myset
1) "c"
2) "d"
3) "a"
4) "b"
- 判断元素是否在集合中
sismember myset a
- 随机从集合返回指定个数元素
127.0.0.1:6379> srandmember myset 2
1) "b"
2) "a"
- 随机从集合弹出元素
127.0.0.1:6379> spop myset
"c"
127.0.0.1:6379> smembers myset
1) "d"
2) "a"
3) "b"
集合间:
- 交集
127.0.0.1:6379> sadd user1 follow it music his sports
(integer) 5
127.0.0.1:6379> sadd user2 follow it news ent sports
(integer) 5
127.0.0.1:6379> sinter user1 user2
1) "follow"
2) "it"
3) "sports"
- 并集
127.0.0.1:6379> sunion user1 user2
1) "sports"
2) "follow"
3) "music"
4) "news"
5) "his"
6) "ent"
7) "it"
- 差集(与前后顺序有关)
127.0.0.1:6379> sdiff user1 user2
1) "his"
2) "music"
- 将交集,并集,差集的结果保存
127.0.0.1:6379> sinterstore user1_2 user1 user2
(integer) 3
127.0.0.1:6379> SMEMBERS user1_2
1) "sports"
2) "follow"
3) "it"
sinterstore:原命令+store
2.5.2 内部编码
- intset:当集合元素全是整数且个数小于set-max-intset-entries配置值(默认值是512)时
- hashtable:当intset条件不满足时
2.5.3 使用场景
- 标签
2.6 有序集合:zset
有序互异
2.6.1 命令
集合内:
- 添加元素
127.0.0.1:6379> zadd user:ranking 251 tom
(integer) 1
127.0.0.1:6379> zadd user:ranking 251 tom 123 huang 11 xu 13 wei 222 2cosmos
- 计算排名
zrank key member
zrevrank key member
127.0.0.1:6379> zrank user:ranking xu
(integer) 0
127.0.0.1:6379> zrevrank user:ranking xu
(integer) 4
- 删除成员
127.0.0.1:6379> zrem user:ranking huang xu
(integer) 2
- 返回成员分数
127.0.0.1:6379> zrange user:ranking 0 2
1) "2cosmos"
2) "tom"
3) "wei"
127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "2cosmos"
2) "222"
3) "tom"
4) "251"
5) "wei"
6) "10013"
- 增加分数
127.0.0.1:6379> zincrby user:ranking 100000000 2cosmos
"100000222"
127.0.0.1:6379> zrange user:ranking 0 2 withscores #zrevrange 逆序
1) "tom"
2) "251"
3) "wei"
4) "10013"
5) "2cosmos"
6) "100000222"
- 返回指定分数范围的成员
127.0.0.1:6379> zrangebyscore user:ranking 1 100000 withscores
1) "tom"
2) "251"
3) "wei"
4) "10013"
- 返回指定分数范围成员个数
127.0.0.1:6379> zcount user:ranking 1 100000
(integer) 2
- 删除指定范围分数成员
127.0.0.1:6379> zremrangebyscore user:ranking (250 +inf
(integer) 3
127.0.0.1:6379> zrangebyscore user:ranking 1 inf withscores
(empty array)
集合间:
- 交集
127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:rang:1 user:rang:2
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "wei"
2) "2"
3) "xu"
4) "24"
5) "huang"
6) "182"
- 并集
127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:rang:1 user:rang:2
(integer) 5
127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores
1) "wei"
2) "2"
3) "xu"
4) "24"
5) "cosmos"
6) "99"
7) "cosmosweis"
8) "99"
9) "huang"
10) "182"
2.6.2 内部编码
- ziplist:当有序集合元素小于zset-max-ziplist-entres配置值(默认为128个)时,同时每个元素的值都小于zset-max-ziplist-value配置值(默认为64字节)时,Redis使用ziplist 作为内部实现,ziplist可以有效减少内存的使用。
- skiplist:当 ziplist 条件不满足时使用,提高读写效率。
2.6.3 使用场景
排行榜系统
- 添加用户赞数。
- 取消赞数。
- 展示获赞数最多的几个用户。
- 展示用户信息以及用户分数。
2.7 键管理
2.7.1 单个键管理
- 键重命名
rename key newkey
rename之前,newkey 已经存在的话,它的值会被覆盖。
为了被强行 rename,Redis 提供了 renamenx 命令
- 重命名期间会del旧键,如果键对应的值过大,会存在阻塞问题。
- rename newkey 已存在,Redis 3.2之前,返回错误;Redis 3.2之后,返回 OK。
- 随机返回一个键
randomkey
- 键过期
expire key secends #键在secends秒后过期
expireat key timesamp #键在秒级时间戳 timesamp 后过期
ttl #查看剩余过期时间
pttl #查看毫秒级过期时间
pexpire key millisecends #键在millisecends秒后过期
pexpireat key millisecends-timesamp #键在秒级时间戳 timesamp 后过期
- 无论过期时间是时间戳,秒级,还是毫秒级,Redis 内部最终使用的都是 pexpireat。
- 对于字符串类型键,执行 set 命令会去掉过期时间。
- persist 命令可以将过期时间清除。
- Redis 不支持二级数据结构内部元素的过期功能,例如不能对列表类型的一个元素设置过期时间。
- setex 命令作为set + expire 的组合,不但是原子执行,同时减少了一次网络通信。
- 迁移键:把部分数据由一个 Redis 迁移到另一个 Redis
- move:Redis 内部使用,内部数据库
- dump+restore:Redis 之间使用
- migrate:Redis 之间使用,实际上就是对dump,restore,set ,命令进行原子性整合
2.7.2 遍历键
- 全量遍历键:keys pattern(*,?,[1,2],\x)
- 渐进式遍历
Redis 2.8 之后:scan 命令
scan 并不能保证完整的遍历出来所有的键。
2.7.3 数据库管理
- select:切换数据库,Redis 默认有16个数据库
- flushdb/flushall:清空数据库
Redis 3.0 以后在弱化这个功能,Redis Cluster 只允许使用0号数据库
为什么:
- Redis 是单线程的,
- 调试和运维变得困难
- 部分 Redis 客户端不支持这种方式