Redis 序列化协议使用的是文本协议,客户端的开发非常简单,易于解析。

常见的一些数据库和缓存性能对比

MYSQL QPS:8000 读,4000 写; redis 10W 读写。

memcached 的瓶颈在于libevent,而 redis 用了 epoll。

一些典型的使用场景

  1. 最新 N 个数据的操作。例如取网站的最新文章,我们可以将最新的5000条评论的id放入到 redis 的 list 中,并将超出链表的部分从 DB 获取(热数据缓存)。
  2. 构建队列系统。使用list可构建队列,使用sorted set可以构建优先级队列。
  3. 使用 setbit 记录连续登陆 7 天的用户(每天一个 key,用户登陆就把对应的位设置 1,最后将 7 天的 key 做按位 and),使用 DB 的话表大,并且要 sum,group 计算。和《编程珠玑》中的位图排序有异曲同工之妙。
  4. 使用 srandommember 做有放回的抽奖,spop 做不放回的抽奖。

数据结构

String

k-v类型,其中value不仅可以是字符串,也可以是数字。可以享受定时持久化(RDB模式和AOF模式)。是二进制安全的,也就是说redis的string可以包含任何数据,例如jpg图片或者序列化的对象,内部实现可以看做byte数组,上限为1GB,定义如下:

1
2
3
4
5
struct{
long len;
long free;
char buf[];
}

部分命令可以按照int处理,例如INCR,如果只使用该种类型,相当于带有持久化功能的memcached。

Hash

在memcached中,我们经常将一些结构化的信息打包成hashmap,在客户端序列化后存储为一个字符串的值(一般是JSON),例如用户的昵称、年龄、性别、积分等。这时候需要修改其中的某一项时需要将字符串(JSON)取出来,然后进行反序列化,修改某一项的值,再序列化成JSON存储回去,消耗太大,不适合用于一些可能并发操作的场合(例如2个并发操作都需要修改积分),而redis的hash可以像在DB中UPDATE一个属性一样只修改某个属性的值。

List

说白了就是链表(redis使用双向链表实现的List)。使用该结构可以轻松实现最新消息排行榜等功能(sina微博的timeline)。另外一个作用就是消息队列,利用PUSH操作将任务存在List中,然后工作线程用POP操作将任务取出并执行。提供了操作List某一段元素的API,可直接查询、删除List中某一段元素。

Set

例如在weibo应用中,将一个用户所有关注的人存储在一个集合中,将其粉丝存在一个集合中。redis针对集合提供了方便的求交集、并集、补集等操作,可以非常方便实现共同关注、共同喜好、二度好友等功能,集合也可以存储在一个新的集合中。使用SPOP命令可以方便实现抽签功能(已经抽过签的人不能再次抽签),使用SINTER可以方便实现张三选了课,李四选了课,求张三和李四的公共选课这样的需求。

Sorted Set

有序集将Set中的元素增加了一个权重参数score,使得集合中的元素能够按照score进行升序排列,例如一个存储全班成绩的Sorted Set,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候就已经进行了天然的排序(游戏用户得分排行榜)。另外还可以使用有序集来做带权重的队列,例如普通消息的score为1,重要消息的score为2,然后工作线程可以按照score的逆序来获取工作任务。让重要的任务先执行。

事务和锁

Redis 中的事务需要和数据库中的事务有区别,它的原子性是相对的(轻量级事务),因为一个事务中如果有一条命令执行失败,其后的命令仍然会被继续执行,lua 脚本也存在这个问题。它是将命令放到队列中,是没有回滚概念的,它是 DISCARD。

提供了 Watch 功能,对 key 进行 watch,然后再执行 transaction(multi,exec),在这个过程中,如果这个 watched 的 key 被修改,那么这个 transaction 会发现并拒绝执行(返回 nil)。例如:以下的命令在多客户端连接的时候可能出现数据一致性问题:

1
2
3
4
set age 10
incr age
incr age # 此时有一个客户端进行get age,将得到11
get age

以上的命令打包成一个原子操作即可解决。使用 watch 可以实现乐观锁。

配置文件

rename-command CONFIG "",命令重命名,在共享环境下可以重命名相对危险的命令,例如把CONFIG重命名为一个不容易猜测的字符。rename-command CONFIG dsfd8324kndskdksfjdfdsjnfdsdfjsl,如果想要删除这个别名直接重命名为空字符串即可。

持久化

  • RDB,默认为 dump.rdb,内存快照,导出快照的时候回开启一个子进程专门处理。
  • AOF,(append only file)记录每一次操作,生成日志文件,更加精细的持久化方案(文件更大)。当我们对一个key进行反复操作时,就会产生多条记录,AOF重写指的是在某一瞬间将内存中的数据直接逆化到文件中(BGREWRITEAOF命令)。

如果AOF和RDB同时存在,将会优先加载 AOF, 因为它能更精确恢复到最新状态,这 2 种方式可同时配置。

如何选择合适的持久化方式

AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用 flushall 命令清空了所有数据,只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 flushall 命令给删了,然后再将该 AOF 文件放回去,就可以通过恢复机制,自动恢复所有数据。redis 为了避免 AOF 文件过大的问题,会对 AOF 文件进行 rewrite,rewrite 的时候是直接基于当前内存中的数据进行指令的重新构建,而不是基于旧的指令日志进行 merge,这样鲁棒性会好很多。

redis需要同时开启AOF和RDB。用AOF来保证数据不丢失,作为数据恢复的第一选择;而使用RDB来做不同程度的冷备,在AOF问你见你都丢失或者损坏不可用的时候还可以使用RDB来进行快速的数据恢复。

过期策略

定期删除 + 惰性删除。

所谓定期删除,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。

假设 redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的灾难。实际上 redis 是每隔 100ms 随机抽取一些 key 来检查和删除的。

但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个 key 的时候,redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。

获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。

但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,咋整?

答案是:走内存淘汰机制(比如最常用的是allkeys-lru)。

主从复制

从网络拓扑上有下面的两种架构,一种是传统的星型结构,另一种是线性结构。

redis主从复制架构

redis主从复制原理

以 6379 为 master 节点,6380 和 6381 为slave节点(需要配置 slaveof)。

现在 master down 掉,设置 6380 为新的 master(SLAVEOF no one;CONFIG SET slave-read-only no),6381 作为 6380 的 slave(SLAVEOF 127.0.0.1 6380)。我们可以使用 redis 自带的sentinel自动完成上述操作。

主从复制的缺陷:

每次 slave 断开后(无论是主动断开还是由于网络故障被动断开)再连接 master 都要全部 dump 出来 rdb,再 aof,即:同步的过程需要重新执行一遍。所以一定要记住:多台 slave 不要一下子都启动起来,否则 master 可能 IO 飙升。

如何实现 redis 的高可用和高并发

redis 实现高并发主要依靠主从架构,一主多从,一般来说,很多项目其实就足够了,单主用来写入数据,单机几万 QPS,多从用来查询数据,多个从实例可以提供每秒 10w 的 QPS。

如果想要在实现高并发的同时,容纳大量的数据,那么就需要 redis 集群,使用 redis 集群之后,可以提供每秒几十万的读写并发。

redis 高可用,如果是做主从架构部署,那么加上哨兵就可以了,就可以实现,任何一个实例宕机,可以进行主备切换。

redis主从架构实现高并发

单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。

redis 采用异步方式复制数据到 slave 节点.注意,如果采用了主从架构,那么建议必须开启 master node 的持久化,不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了。另外,master 的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能确保启动的时候,是有数据的,即使采用了后续讲解的高可用机制,slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。

redis的主从复制支持断点续传、无磁盘化复制(master在内存中创建RDB发送给slave),对于过期key的处理是master过期了key或者LRU淘汰了一个key,那么会模拟一条del命令发送给salve。

基于哨兵集群的高可用

sentinel,中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能:

  • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

  • 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
  • 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。

哨兵核心知识

  • 哨兵至少需要 3 个实例,来保证自己的健壮性。
  • 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
  • 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。

运维常用命令

dbsize查看有多少个键; config get/config set 查看或者设置redis的配置; SLOWLOG get查看慢日志。

如果不小心运行了flushall命令清空了redis中的数据怎么办?必须马上运行shutdown nosave命令,然后编辑 AOF 文件,删除掉最后一条命令(flushall),因为别的进程可能向 redis 中写入数据导致 AOF 重写了,重写后的 AOF 被清空了。复制 rdb 文件需要在 redis 进程处于杀掉状态的时候进行,进程处于活动状态的时候复制的是同样的句柄

redis 数据迁移只需要 dump 出 rdb,然后从此文件恢复即可。

redis5.0新特性

Stream数据类型

本质是抽象日志,基于时间序列的数据。物联网,各种传感器产生的时间序列数据,定位未来。

redis5.0之stream尝鲜

Timers & Cluster API

RDB现在存储LFU和LRU信息

集群管理器从Ruby(redis-trib.rb)移植到C

搭建集群

集群创建不用先安装ruby。配置3主3从的redis集群:端口范围是5001~5006

redis5001.conf

1
2
3
4
port 5001
pidfile /usr/local/var/run/redis5001.pid
logfile /tmp/logs/redis/redis-server5001.log
cluster-config-file redis5001.conf

其他配置文件类似。使用6个配置文件启动redis。可以使用redis-cli --cluster help命令查看帮助信息:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 创建集群,主从比例为1:1
➜ redis-cluster redis-cli --cluster create 127.0.0.1:5001 127.0.0.1:5002 127.0.0.1:5003 127.0.0.1:5004 127.0.0.1:5005 127.0.0.1:5006 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:5004 to 127.0.0.1:5001
Adding replica 127.0.0.1:5005 to 127.0.0.1:5002
Adding replica 127.0.0.1:5006 to 127.0.0.1:5003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[0-5460] (5461 slots) master
M: 6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002
slots:[5461-10922] (5462 slots) master
M: 965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003
slots:[10923-16383] (5461 slots) master
S: 2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004
replicates a118aa98313f9d7bb3799ad942f3a69489e55e89
S: 51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005
replicates 6592afc22ce09055b713a049c594445ecdf9246c
S: f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006
replicates 965e863a346c5c0558623313bbc4b9124a7707ee
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 127.0.0.1:5001)
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005
slots: (0 slots) slave
replicates 6592afc22ce09055b713a049c594445ecdf9246c
M: 965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006
slots: (0 slots) slave
replicates 965e863a346c5c0558623313bbc4b9124a7707ee
S: 2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004
slots: (0 slots) slave
replicates a118aa98313f9d7bb3799ad942f3a69489e55e89
M: 6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

从上面的信息可以看出16384个slots几乎平均分配给了3个master节点,3个slave节点是没有slots的,分别和对应的主节点的数据是一致的。

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
29
30
31
32
33
34
35
36
37
38
➜  redis-cluster redis-cli -c -p 5001
127.0.0.1:5001> set a 1
-> Redirected to slot [15495] located at 127.0.0.1:5003
OK
127.0.0.1:5003> get a
"1"
127.0.0.1:5003> set b 2
-> Redirected to slot [3300] located at 127.0.0.1:5001
OK
127.0.0.1:5001> get b
"2"
127.0.0.1:5001> keys *
1) "b"
# 当前连接到了5001端口,这个服务器上只存放了b的key
127.0.0.1:5001> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:469
cluster_stats_messages_pong_sent:463
cluster_stats_messages_sent:932
cluster_stats_messages_ping_received:458
cluster_stats_messages_pong_received:469
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:932
127.0.0.1:5001> CLUSTER NODES
51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005@15005 slave 6592afc22ce09055b713a049c594445ecdf9246c 0 1550143239486 5 connected
965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003@15003 master - 0 1550143238000 3 connected 10923-16383
a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001@15001 myself,master - 0 1550143237000 1 connected 0-5460
f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006@15006 slave 965e863a346c5c0558623313bbc4b9124a7707ee 0 1550143240496 6 connected
2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004@15004 slave a118aa98313f9d7bb3799ad942f3a69489e55e89 0 1550143240000 4 connected
6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002@15002 master - 0 1550143240000 2 connected 5461-10922

集群扩容

创建2个配置文件5007和5008并启动服务器。

添加主节点5007

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
➜  redis-cluster redis-cli --cluster add-node 127.0.0.1:5007 127.0.0.1:5001
>>> Adding node 127.0.0.1:5007 to cluster 127.0.0.1:5001
>>> Performing Cluster Check (using node 127.0.0.1:5001)
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005
slots: (0 slots) slave
replicates 6592afc22ce09055b713a049c594445ecdf9246c
M: 965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006
slots: (0 slots) slave
replicates 965e863a346c5c0558623313bbc4b9124a7707ee
S: 2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004
slots: (0 slots) slave
replicates a118aa98313f9d7bb3799ad942f3a69489e55e89
M: 6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:5007 to make it join the cluster.
[OK] New node added correctly.
1
2
3
4
5
6
7
8
127.0.0.1:5001> CLUSTER NODES
51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005@15005 slave 6592afc22ce09055b713a049c594445ecdf9246c 0 1550143794065 5 connected
965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003@15003 master - 0 1550143797086 3 connected 10923-16383
a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001@15001 myself,master - 0 1550143797000 1 connected 0-5460
f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006@15006 slave 965e863a346c5c0558623313bbc4b9124a7707ee 0 1550143795000 6 connected
32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007@15007 master - 0 1550143795071 0 connected
2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004@15004 slave a118aa98313f9d7bb3799ad942f3a69489e55e89 0 1550143796079 4 connected
6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002@15002 master - 0 1550143798092 2 connected 5461-10922

新添加的5007节点作为了master节点,但是还没有slots,暂时还不能存储数据,需要进行分片的操作将其他master节点的slots分一些给5007节点均衡一下存储。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
➜  redis-cluster redis-cli --cluster reshard 127.0.0.1:5007
>>> Performing Cluster Check (using node 127.0.0.1:5007)
M: 32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007
slots: (0 slots) master
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004
slots: (0 slots) slave
replicates a118aa98313f9d7bb3799ad942f3a69489e55e89
M: 965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005
slots: (0 slots) slave
replicates 6592afc22ce09055b713a049c594445ecdf9246c
S: f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006
slots: (0 slots) slave
replicates 965e863a346c5c0558623313bbc4b9124a7707ee
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 500
What is the receiving node ID? 32a4a4886b43f7005c265b3b5813fea2fe578541 # 输入5007的id
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: all

Ready to move 500 slots.
Source nodes:
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
Destination node:
M: 32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007
slots: (0 slots) master
Resharding plan:
Moving slot 5461 from 6592afc22ce09055b713a049c594445ecdf9246c
Moving slot 11088 from 965e863a346c5c0558623313bbc4b9124a7707ee
Do you want to proceed with the proposed reshard plan (yes/no)? yes

添加从节点5008

1
redis-cluster redis-cli --cluster add-node 127.0.0.1:5008 127.0.0.1:5001

上面的操作将5008添加为了master节点(可以使用客户端连上使用CLUSTER NODES查看),需要设置其变为从节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  redis-cluster redis-cli -c -p 5008
127.0.0.1:5008> CLUSTER NODES
965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003@15003 master - 0 1550144560000 3 connected 11089-16383
32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007@15007 master - 0 1550144562933 7 connected 0-165 5461-5627 10923-11088
f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006@15006 slave 965e863a346c5c0558623313bbc4b9124a7707ee 0 1550144560914 3 connected
a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001@15001 master - 0 1550144561920 1 connected 166-5460
aafdcb6b443c9449ca0131562e1ee516f2f6a6e4 127.0.0.1:5008@15008 myself,master - 0 1550144559000 0 connected
51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005@15005 slave 6592afc22ce09055b713a049c594445ecdf9246c 0 1550144561000 2 connected
6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002@15002 master - 0 1550144560000 2 connected 5628-10922
2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004@15004 slave a118aa98313f9d7bb3799ad942f3a69489e55e89 0 1550144559903 1 connected
127.0.0.1:5008> CLUSTER REPLICATE 32a4a4886b43f7005c265b3b5813fea2fe578541
OK
127.0.0.1:5008> CLUSTER NODES
965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003@15003 master - 0 1550144580000 3 connected 11089-16383
32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007@15007 master - 0 1550144579000 7 connected 0-165 5461-5627 10923-11088
f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006@15006 slave 965e863a346c5c0558623313bbc4b9124a7707ee 0 1550144579000 3 connected
a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001@15001 master - 0 1550144582129 1 connected 166-5460
aafdcb6b443c9449ca0131562e1ee516f2f6a6e4 127.0.0.1:5008@15008 myself,slave 32a4a4886b43f7005c265b3b5813fea2fe578541 0 1550144578000 0 connected
51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005@15005 slave 6592afc22ce09055b713a049c594445ecdf9246c 0 1550144581114 2 connected
6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002@15002 master - 0 1550144580104 2 connected 5628-10922
2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004@15004 slave a118aa98313f9d7bb3799ad942f3a69489e55e89 0 1550144581000 1 connected
127.0.0.1:5008>

缩容

删除从节点

1
redis-cli --cluster del-node 127.0.0.1:5008 aafdcb6b443c9449ca0131562e1ee516f2f6a6e4

删除主节点

删除主节点之前要先进行分片,将slots中的数据分配给集群中的其他master。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
➜  redis-cluster redis-cli --cluster reshard 127.0.0.1:5007
>>> Performing Cluster Check (using node 127.0.0.1:5007)
M: 32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007
slots:[0-165],[5461-5627],[10923-11088] (499 slots) master
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[166-5460] (5295 slots) master
1 additional replica(s)
M: 6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002
slots:[5628-10922] (5295 slots) master
1 additional replica(s)
S: 2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004
slots: (0 slots) slave
replicates a118aa98313f9d7bb3799ad942f3a69489e55e89
M: 965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003
slots:[11089-16383] (5295 slots) master
1 additional replica(s)
S: 51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005
slots: (0 slots) slave
replicates 6592afc22ce09055b713a049c594445ecdf9246c
S: f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006
slots: (0 slots) slave
replicates 965e863a346c5c0558623313bbc4b9124a7707ee
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 500 # 因为扩容的时候分配了500个
What is the receiving node ID? a118aa98313f9d7bb3799ad942f3a69489e55e89 # 随机挑选一个master节点,此处为node1
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: 32a4a4886b43f7005c265b3b5813fea2fe578541 # 5007的id
Source node #2: done

Ready to move 500 slots.
Source nodes:
M: 32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007
slots:[0-165],[5461-5627],[10923-11088] (499 slots) master
Destination node:
M: a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001
slots:[166-5460] (5295 slots) master
1 additional replica(s)
Resharding plan:
Moving slot 0 from 32a4a4886b43f7005c265b3b5813fea2fe578541
Moving slot 1 from 32a4a4886b43f7005c265b3b5813fea2fe578541
Moving slot 11088 from 32a4a4886b43f7005c265b3b5813fea2fe578541
Do you want to proceed with the proposed reshard plan (yes/no)? yes
1
2
3
4
5
6
7
8
9
➜  redis-cluster redis-cli -c -p 5001
127.0.0.1:5001> CLUSTER NODES
51c8959f1b428fe9e21b634800bf1da80b2d97e7 127.0.0.1:5005@15005 slave 6592afc22ce09055b713a049c594445ecdf9246c 0 1550145060000 5 connected
965e863a346c5c0558623313bbc4b9124a7707ee 127.0.0.1:5003@15003 master - 0 1550145058000 3 connected 11089-16383
a118aa98313f9d7bb3799ad942f3a69489e55e89 127.0.0.1:5001@15001 myself,master - 0 1550145061000 8 connected 0-5627 10923-11088
f0ba58939c6ae6de4e178e7f7070c78ed19d376d 127.0.0.1:5006@15006 slave 965e863a346c5c0558623313bbc4b9124a7707ee 0 1550145061000 6 connected
32a4a4886b43f7005c265b3b5813fea2fe578541 127.0.0.1:5007@15007 master - 0 1550145061643 7 connected
2c06122ac54a7cf34027d1248a6b89df4e74ff0d 127.0.0.1:5004@15004 slave a118aa98313f9d7bb3799ad942f3a69489e55e89 0 1550145062000 8 connected
6592afc22ce09055b713a049c594445ecdf9246c 127.0.0.1:5002@15002 master - 0 1550145062649 2 connected 5628-10922

5007的slots已经分配给了别人!最后从集群中移除5007

1
redis-cli --cluster del-node 127.0.0.1:5007 32a4a4886b43f7005c265b3b5813fea2fe578541

新的sorted set命令zpopmin/max和阻塞变种

取出集合中分值最大和最小的元素。

主动碎片整理V2 & 更好的内存统计报告

当删除一个key的时候,redis并不会立即回收内存空间。如果反复进行增删键值,内存中将产生大量碎片,这就会影响到之后申请大块连续的内存,所以有必要对内存碎片进行整理。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 快速产生测试数据
127.0.0.1:6379> DEBUG populate 300000 abc 1000
127.0.0.1:6379> info memory
# Memory
used_memory:326834704
used_memory_human:311.69M
used_memory_rss:317788160
used_memory_rss_human:303.07M
used_memory_peak:331743840
used_memory_peak_human:316.38M
used_memory_peak_perc:98.52%
used_memory_overhead:17232110
used_memory_startup:988032
used_memory_dataset:309602594
used_memory_dataset_perc:95.01%
allocator_allocated:326801360
allocator_active:317750272
allocator_resident:317750272
total_system_memory:8589934592
total_system_memory_human:8.00G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:0.97
allocator_frag_bytes:18446744073700500528
allocator_rss_ratio:1.00
allocator_rss_bytes:0
rss_overhead_ratio:1.00
rss_overhead_bytes:37888
mem_fragmentation_ratio:0.97
mem_fragmentation_bytes:-9013200
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:49694
mem_aof_buffer:0
mem_allocator:libc
active_defrag_running:0
lazyfree_pending_objects:0

上述used_memory_human:311.69M,而used_memory_rss_human:303.07M,rss比used还小,这种情况比较糟糕,redis服务器的内存可能被OS换到了交换空间中了,相反如果used小很多的话可能有大量内存碎片;还可以查看mem_fragmentation_ratio,这个参数大于1是比较理想的情况,如果这个只为几十或者几百也表示有大量内存碎片。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
127.0.0.1:6379> MEMORY STATS
1) "peak.allocated" # 最大内存
2) (integer) 331743840
3) "total.allocated" # 当前已使用的内存
4) (integer) 326834688
5) "startup.allocated" # 服务启动初始化之后的内存
6) (integer) 988032
7) "replication.backlog" # 主从复制中backlog占用
8) (integer) 0
9) "clients.slaves"
10) (integer) 0
11) "clients.normal"
12) (integer) 49694
13) "aof.buffer"
14) (integer) 0
15) "lua.caches"
16) (integer) 0
17) "db.0"
18) 1) "overhead.hashtable.main"
2) (integer) 16194384
3) "overhead.hashtable.expires"
4) (integer) 0
19) "overhead.total"
20) (integer) 17232110
21) "keys.count"
22) (integer) 300002
23) "keys.bytes-per-key"
24) (integer) 1086
25) "dataset.bytes"
26) (integer) 309602578
27) "dataset.percentage"
28) "95.014816284179688"
29) "peak.percentage"
30) "98.520195007324219"
31) "allocator.allocated"
32) (integer) 326801360
33) "allocator.active"
34) (integer) 312306688
35) "allocator.resident"
36) (integer) 312306688
37) "allocator-fragmentation.ratio"
38) "0.95564687252044678"
39) "allocator-fragmentation.bytes"
40) (integer) -14494672
41) "allocator-rss.ratio"
42) "1"
43) "allocator-rss.bytes"
44) (integer) 0
45) "rss-overhead.ratio"
46) "1.0001213550567627"
47) "rss-overhead.bytes"
48) (integer) 37888
49) "fragmentation"
50) "0.95576280355453491"
51) "fragmentation.bytes"
52) (integer) -14456784
# 查看单个键占用的内存
127.0.0.1:6379> MEMORY USAGE abc:2
(integer) 1053

许多带有子命令的命令现在都有一个HELP子命令

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
29
30
31
32
33
34
35
36
37
127.0.0.1:6379> XINFO help
1) XINFO <subcommand> arg arg ... arg. Subcommands are:
2) CONSUMERS <key> <groupname> -- Show consumer groups of group <groupname>.
3) GROUPS <key> -- Show the stream consumer groups.
4) STREAM <key> -- Show information about the stream.
5) HELP -- Print this help.
127.0.0.1:6379> XINFO CONSUMERS codehole cg1
1) 1) "name"
2) "c1"
3) "pending"
4) (integer) 2
5) "idle"
6) (integer) 675253
127.0.0.1:6379> XINFO STREAM codehole
1) "length"
2) (integer) 3
3) "radix-tree-keys"
4) (integer) 1
5) "radix-tree-nodes"
6) (integer) 2
7) "groups"
8) (integer) 2
9) "last-generated-id"
10) "1550066609501-0"
11) "first-entry"
12) 1) "1550066141188-0"
2) 1) "name"
2) "youming"
3) "age"
4) "60"
13) "last-entry"
14) 1) "1550066609501-0"
2) 1) "name"
2) "xiaorui"
3) "age"
4) "1"
127.0.0.1:6379>

pubsub,xgroup也有子命令。

最佳实践

  1. 批量处理。将一次处理一条数据改善为1次处理多条数据性能可成倍提高。网络 IO。
  2. 最好在本机部署,性能提高 10~20 倍。
  3. 少用 get/set,多用 hset。主要是为了内存考虑。假设一个k-v单元最少占用512bytes,即使只存储了一个字节也占用了512bytes。这时候有一个设计模式,可以将key复用,几个k-v对放在一个key中,将value作为一个set存入,同样512bytes可存储10~100倍的容量。

参考资料: