Redis知识点整理

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,和Memcached类似;它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型);同时,还支持地理位置GEO和Bitmap扩展类型。

Redis之所以这么受欢迎,得益于它的性能,说到性能又不得不提它的单线程设计。那为什么redis采用单线程还这么高效呢?主要由以下原因:

  1. 减少上下文切换时间
  2. 纯内存访问
  3. 非阻塞IO,使用epoll

数据类型

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
set key xx ex[seconds] | px [milliseconds] [nx |xx] # 如何实现分布式共享锁
setex seconds key
setnx key
setxx key

get key
del key

mset key1 value1 [key2 value2 …] # mset 与 set 之间最大的差异和性能改善在哪儿?
mget key1 [key2 …]

append key value

exists key
expire key seconds

strlen key

getset key value

setrange key offset value
getrange key start end

type key
object encoding key

incr key
decr key

哈希

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
hset key field value
Hget key field
Hdel key field [field …]

hmset key field value [field value …]
hmget key field [field …]

hexists key field

hkeys key
hvals key
hgetall key

hstrlen key field
hlen key

列表

特征:

  1. 列表中元素有序
  2. 列表中元素可重复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
lpush key value [value …]
rpush key value [value …]

lrange start end # lrange key 0 -1
linsert key before | after pivot value
lindex key index

llen key

lpop key
rpop key

ltrim key count value # count>0 从左到右删除最多count个, count<0 从右到左删除最多count个,count=0 删除所有
ltrim key start end # 保留start->end

lset key index value

blpop key timeout
brpop key timeout

集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sadd key element [element …]
srem key element [element …]

scard key # 计算元素个数
sismember key element # 判断元素是否在集合中
srandmemeber key [count] # 随机从集合中返回指定个数值(不会删除)
spop key # 从集合中随机弹出元素(随即删除)

sinter key [key ...] # 交集
suinon key [key ...] # 并集
sdiff key [key ...] # 差集


sinterstore destination key [key ...] # 交集集合保存
suionstore destination key [key ...] # 并集集合保存
sdiffstore destination key [key ...] # 差集集合保存

有序集合

特征:

  1. 保留不可重复特征
  2. 给每个元素设置一个score作为排序的依据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
zadd key score member [score member ...]
zcard key

zscore key member # 计算某个成员的分数
zrank key member # 从低到高排名
zrevrank key member # 从高到低排名

zrem key member [member ...] # 删除成员
zincrby key increment member # 增加成员分数

zrange key start end [withscores] # 返回指定排名范围的成员
zrevrange key start end [withscores]

zrangebyscore key min max [withscores] [limit offset count] # 返回指定分数范围的成员
zrevrangebyscore key max min [withscores] [limit offset count]

zcount key min max # 返回指定分数范围的成员个数

zremrangebyrank key start end # 删除指定排名内的升序元素
zremrangebyscore key min max # 删除指定分数范围的成员

其他

包括用于计算地理位置的GEO 和 BITMAP 两种扩展类型。

键管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dbsize

rename key newKey
randomkey # 随机返回一个key

expire key second
persist key
ttl key

move key db

dump key # 序列化为RDB格式
restore key ttl value

flushdb # 清除当前数据库
flushall # 清除所有数据库

使用场景

  • 分布式锁(setnx ex)
  • 消息队列(list)
  • 延时队列(zset)
  • 计数(bitmap)
  • 统计UV(HyperLogLog pfadd/pfcount)
  • 大规模数据集判断是否存在(布隆过滤器 bf.add/bf.exists)
  • 限流(cl.throttle)
  • 计算距离(GeoHash)

管道

本质客户端行为,是将多个操作放到一个命令里面发送到服务器端处理。

事务

  • multi -> exec

    是客户端将多个命令发送到服务器端缓存,然后执行exec的时候再统一执行。
    它不具有原子性,无法保障里面的多个命令都成功或者都失败。

    一般管道和事务和结合使用,在multi之后,将多个操作命令压如管道中,一次发送到服务器端执行。

  • watch

    watch 会在事务开始之前盯住一个或多个关键变 量,当事务执行时,也就是服务器收到了 exec 指令要顺序执行缓存的事务队列时,Redis 会检查关键变量自 watch 之后 是否被修改了(包括当前事务所在的客户端)。如果关键变量被人动过了, exec 指令就会返回 NULL 回复告知客户端事务执行失败,这个时候客户端一般会选择重试。

    Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前盯住关键 变量,否则会出错。

日常运维

慢查询

1
2
3
4
5
config set slowlog-log-slower-than 20000    # 慢查询标准为慢于20000微秒
config set slowlog-max-len 1000 # 日志最大条数

slowlog get [n] # 读取慢查询日志
slowlog len # 获取慢查询日志长度

事务

1
2
3
4
multi
xxx
xxx
exec/discard

run id

redis的run id在重启之后会发生变化,这样slave就能基于该id来判断需要重新全量同步master的数据,因为重启过程可能发生数据恢复等操作。

如果希望不改变run id的情况下重新加载配置,需要执行 redis-cli debug reload

数据持久化

  • RDB快照手动触发时,save和bgsave的差异?
  • AOF以独立日志方式记录每次写操作,重启时重放以恢复数据。

触发方法

  1. 手动bgrewriteaof
  2. 自动根据auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage确定自动触发机制

持久化流程

命令写入 -> AOF缓冲区 -> AOF文件 -> rewrite -> 重启(load)

执行重写后,为什么AOF文件会变小?

  1. 干掉一些中间状态的数据,比如set之后又del的数据;
  2. expire已经timeout的数据,不再写入;
  3. 多条命令可以合并为一条;

三种AOF缓冲区同步文件策略

  • always
  • everysec
  • none

复制相关

1
2
3
slave of host port
slave of no one
info replication

默认情况下, 从节点使用 slave-read-only=yes 配置为只读模式。

哨兵模式

redis sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。

如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数 Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。

哨兵的流程:

  1. 监控并发送报文给各自的节点;
  2. 发现主观下线;
  3. 询问,并投票,客观下线;
  4. 领导者节点选举->raft(基本为谁最早发现某个redis节点主观下线,就会发起投票,然后的票就会最多,然后成为切换该主redis的领导者);
  5. 故障转移;选出新的老大(在从节点中,过滤不健康的,然后基于优先级->复制偏移量->runid

集群方案

集群方案在这里有详细介绍。
redis cluser采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式: slot = CRC16(key)& 16383

集群限制

  1. 不支持多个db
  2. Key的批量操作有限,比如mset

节点握手

1
2
3
cluster meet {ip} {port}
cluster nodes
cluster info

分配槽

1
cluster addslots {0…5555}

从节点设置

1
cluster replicate {master node id}

缓存穿透解决办法

  1. 缓存空对象
  2. 布隆过滤器

值得深入思考的问题

  1. redis如何实现消息队列?
  2. 如何使用redis实现每分钟获取验证码的次数不超过5次?
  3. redis的订阅与发布适合用来做消息队列吗?为什么?
  4. 如果一个key设置了expire时间,get操作之后,expire会发生变化吗?
  5. 关系型数据库存储与hash散列存储的差异性在哪儿?
  6. 将数据直接序列化后使用string存到redis和将数据按照hash存到redis两者的优缺点?
  7. 如何通过redis快速计算出具有共同兴趣爱好的一类人?
  8. 如何通过redis计算被点赞数的用户排名?
  9. 当系统中同时存在AOF和RDB文件时,系统重启默认有限加载哪一个?
  10. 哨兵模式下,客户端连接的是redis节点,还是哨兵节点?
  11. 缓存雪崩后,重建缓存可能会被很多程序调用到,这个时候如何采用较好的方法避免出现大家都去重建缓存呢?
0%