Redis 2/2
12 持久化—RDB
12.1 介绍
-
RDB,Redis DataBase。在指定的时间间隔内将内存中的数据集快照(即Snapshot 快照)写入磁盘, 恢复时直接将快照文件读到内存中。
-
Redis 会单独创建(fork)一个子进程来进行持久化。先将数据写入到 一个临时文件中,在持久化过程都结束时,再用这个临时文件替换上次持久化的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性要求不是非常高,那么 RDB 方式要比 AOF 方式更加的高效。
-
RDB 的缺点是最后一次持久化的数据可能丢失。
-
RDB 默认开启。
12.2 Fork
- Fork 的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在 Linux 程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会被 exec 系统调用,出于效率考虑,Linux 中引入了“写时复制技术”。
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
12.3 相关文件及配置
-
dump.rdb:存放 RDB 持久化数据的文件。
dump.rdb 默认存放在启动 Redis 的目录下。如,在 /opt/ 下使用命令:redis-server ,则会在 /opt 下创建 dump.rdb 文件。可以在 Redis.conf 中修改名称和位置。
修改 dump.rdb 的保存位置:
-
触发保存数据到持久化文件中的事件:
(1)以shutdown
方式正常退出 Redis 时。
(2)save 多少秒内 被修改的键的个数
:当在 n 秒内,有 m 个 key 被修改(包括新建、修改、删除等)时,将这 m 个 key 保存到持久化文件中。
-
save 和 bgsave:
save:阻塞式保存,需要手动保存,不建议使用。
bgsave:Redis 会在后台异步进行快照操作(自动持久化数据), 快照同时还可以响应客户端请求。
-
stop-writes-on-bgsave-error:当 Redis 无法将数据写入磁盘时(如磁盘已满),关掉 Redis 的写操作,默认为 yes。
-
rdbcompression:是否将持久化文件压缩,默认为 yes。
对于存储到磁盘中的快照,可以设置是否进行压缩。如果为 yes,redis 会采用 LZF 算法将持久化文件进行压缩。
-
rdbchecksum:检查文件完整性,默认为 yes。
让 redis 使用 CRC64 算法进行数据校验。
12.4 恢复
Redis 每次启动时,会自动在持久化文件目录下加载 dump.rdb 文件,进行数据的恢复。
12.5 优缺点
优点:
- 适合大规模的数据恢复。
- 对数据完整性和一致性要求不高的场景。
- 节省磁盘空间。
- 恢复速度快。
缺点:
- Fork 时,内存中的数据被克隆了一份,大致 2 倍的膨胀性需要考虑。
- 虽然 Redis 在 fork 时使用了“写时拷贝技术”,但是如果数据过于庞大,还是比较消耗性能。
- 可能会丢失最后一次持久化的数据。
13 持久化—AOF
13.1 介绍
- AOF,Append Only File。以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件。redis 在启动时会读取该文件重新构建数据,即 redis 重启时根据 AOF 文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
- AOF 默认不开启。
- AOF 和 RDB 同时开启时,Redis 默认读取 AOF 的数据,因为 AOF 的数据不会丢失。
13.2 相关文件及配置
- appendonly.aof:存放 AOF 持久化数据的文件。默认的存放目录与 RDB 的目录相同。
- AOF 同步频率设置:
appendfsync always:始终同步。每次 Redis 的写入操作都会立即记入日志,性能较差但数据完整性比较好。
appendfsync everysec:每秒同步。每秒记入日志一次,如果 Redis 死机,本秒的数据可能丢失。
appendfsync no:Redis 不主动进行同步,把同步时机交给操作系统。 - Rewrite(压缩):AOF 采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩, 只保留可以恢复数据的最小指令集。
(1)auto-aof-rewrite-min-size:设置重写的基准值,最小文件 64MB,当 AOF 文件大小达到这个值时第一次触发重写。
(2)auto-aof-rewrite-percentage:设置重写的基准值,默认是当前 AOF 文件是上次重写后的文件大小的 1 倍时开始重写。
(3)Redis 会记录上次重写时的 AOF 文件大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的 1 倍且文件大于 64M 时第 n 次触发。
例如:文件达到 70MB 时开始重写,第一次重写将文件大小降到 50MB,则当文件大小达到 100MB 时开始下一次重写。
13.3 AOF 的开启、修复和恢复
- 开启 AOF:AOF 服务默认不开启,修改 Redis.conf 中的 appendonly no 为
appendonly yes
,开启 AOF。 - appendonly.aof 文件可能出现损坏、无法加载等异常,可以使用
redis-check-aof --fix appendonly.aof
命令进行修复。 - Redis 每次启动时,会自动在持久化文件目录下加载 appendonly.aof 文件,进行数据的恢复。当 AOF 和 RDB 同时开启时,Redis 默认读取 AOF 的数据。
13.4 AOF 的持久化流程
- 客户端的写命令会被 append 追加到 AOF 缓冲区内。
- AOF 缓冲区根据 AOF 持久化策略 [always,everysec,no] 将操作同步到磁盘的 AOF 文件中。
- AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件重写,压缩 AOF 文件容量。
- Redis 服务重启时,会重新加载 AOF 文件中的写操作达到数据恢复的目的。
13.5 优缺点
优点:
- 备份机制稳健,丢失数据的可能性很低。
- 可读的日志文本,可以处理误操作。
缺点:
- 与 RDB 相比占用更多的磁盘空间。
- 恢复备份速度更慢。
- 每次写操作都同步时,有一定的性能压力。
- 存在个别 Bug,造成不能恢复。
13.6 RDB 与 AOF 用哪个?
- 官方推荐两个都启用。
- 如果对数据不敏感,可以单独使用 RDB。
- 不建议单独使用 AOF,因为可能会出现 Bug。
- 如果只用作纯内存缓存,两者可以都不使用。
14 主从复制
14.1 介绍
将 Redis 服务器分为主服务器和从服务器,主服务器负责写操作,从服务器负责读操作。主服务器负责将数据同步到所有的从服务器中。
主从复制的好处是可以实现读写分离,解决 Redis 的读写压力。
14.2 配置
假设配置一主二从:
-
复制 3 份 Redis.conf 配置文件,分别命名为便于识别的名字。如,redis6379.conf、redis6380.conf、redis6381.conf。
-
在这些配置文件中,使用:
include
,引入 redis.conf 文件(要使用绝对路径),并修改下面这些配置:
(1)开启 daemonize yes。
(2)pidfile 的存储路径(pidfile 用来保存 pid)。
(3)指定端口 port。
(4)log 文件名。
(5)dbfilename(RDB 持久化文件名)。路径使用 dir 配置。
(6)关闭 AOF 持久化方式,或重命名 Appendonly.aof 文件名。
(7)如果主服务器设置了密码,从服务器的配置文件中要配置:masterauth
。
其他配置文件改成相应的信息即可。 -
使用:
redis-server 配置文件路径
,启动 3 台服务器。
-
因为每台服务器的端口号不同,因此使用:
redis-cli -h 服务器ip地址 -p 端口号
,打开客户端。
-
使用:
info replication
,查看主从复制的相关信息。
-
配从不配主。在需要配置成从服务器的客户端内,使用:
salveof ip port
,将本服务器设置为该 ip,port 的从服务器。如,将 6380 和 6381 设置为 6379 的从服务器。注意:在新版的 Redis 中,推荐使用:replicaof
,代替 slaveof。
-
在主服务器上写数据,这些数据会同步到所有的从服务器上,在从服务器上可以读取这些数据。
注意:
- 主服务器可读可写,从服务器只能读。
- 当主服务器掉线时,所有的从服务器都会处于等待状态,role 仍然是 slave。当主服务器再次上线时,仍然是之前那些从服务器的主服务器。
- 当从服务器掉线时,主服务器的 slaves 列表中就会将该从服务器删除。当掉线的这个从服务器再次上线后,需要再次执行 slaveof 命令,才可以成为主服务器的从服务器。
- 可以在从服务器的配置文件中配置主服务器的信息,配置后,当从服务器掉线再上线后,会自动成为从服务器,而不需要再次输入 slaveof 命令。
14.3 一主二从
- 主机新接入的从机,会不会有接入之前的主机数据?
有。不管何时接入,主机都会将所有的数据同步到从机上。 - 从机是否可以写?
不可以。从机只能读。 - 主机 shutdown 后情况如何?从机是上位还是原地待命?
主机下线后,默认情况下,从机会原地待命(不会变成主机),直到主机再次上线。 - 主机再次上线后,主机新增记录,从机还能否顺利复制?
能。 - 其中一台从机下线后情况如何?
从机下线后,主机的 slaves 列表中就会将这台从机删除,就不会同步数据给这个下线的从机。当下线的从机再次上线后,主机会将所有数据都同步给这个刚上线的从机。
14.4 反客为主
在一般模式下,主机下线后,所有的从机都处于等待状态。此时由于从机不具备写操作,如果主机长时间不上线,那么 Redis 就会长时间处于不能写,只能读的状态。这种状态是我们不希望看到的。
解决办法:
当主机下线后,在希望成为新的主机的从机内,使用:replicaof no one
,将这个从机设置为新的主机。之后修改其他服务器为这个新主机的从机即可。
14.5 薪火相传
在反客为主模式下,主机下线后需要两部操作:
(1)从机使用 replicaof no one 变为新主机。
(2)其他从机使用 replicaof ip port 变为新主机的从机。
薪火相传是指:
主机与从机之间类似于上图所示的串联结构:master 是 slave1 的主机,slave1 是 slave2 的主机。当 master 下线后,slave1 与 slave2 仍然构成了主从关系。此时只需在 slave1 中执行:replicaof no one
,即可将 slave1 设置为 slave2 的主机。
实现方式:假设 6379 是 master,6380 是 slave1,6381 是 slave2。
方式1:临时方式
(1)在 6380 客户端中执行:replicaof 127.0.0.1 6379
。
(2)在 6381 客户端中执行:replicaof 127.0.0.1 6380
。
方式2:永久方式
(1)6380 的配置文件 redis6380.conf 中,添加:replicaof 127.0.0.1 6379
。
(2)6381 的配置文件 redis6381.conf 中,添加:replicaof 127.0.0.1 6380
。
当 master 下线(shutdown)后,
薪火相传模式下,master 中的数据会同步到 slave1,slave1 中的数据会同步到 slave2,最终实现数据的全同步。
优点:可以有效减轻 master 的写压力,去中心化,降低风险。
缺点:一旦中间的某个 slave 下线,则后面的 slave 都不能再备份新的数据。
14.6 哨兵模式
哨兵模式是反客为主的自动版,能够在后台监控主机是否出现故障。如果出现了故障,那么每个哨兵都会对现有的从机进行投票(每个哨兵 1 票),根据投票结果,将得票最多的从机自动转换为新的主机。
实现步骤:
- 建立一主二从的关系:6379 为主机,6380 和 6381 为 6379 的两个从机。
- 在 /opt/myRedis/ 下创建哨兵配置文件:
sentinel.conf
。 - 在配置文件中填写:
(1)sentinel monitor 监控的主服务器别名(随便写) ip port 票数n
。其中,票数 n 是指当有 n 个哨兵投票认为该主服务器出现故障时,才将这个主服务器认定为故障。
(2)后台开启哨兵模式:daemonize yes
。这里为了演示效果,暂不使用后台开启。
(3)如果主机与从机设置了密码,需要在配置文件中声明:sentinel auth-pass 服务器别名 密码
。注意的是,sentinel 不能分别为 master 和 slave 设置不同的密码,因此 master 和 slave 的密码应该设置相同。
- 启动哨兵。在任意位置执行:
redis-sentinel sentinel.conf文件路径
,开启哨兵模式。
- 当主机发生故障下线后,哨兵会在从机中投票选出一个新的主机。原主机的所有从机会自动变成新主机的从机。
新主机的选举原则:
- 优先级越靠前越优先。可以在每个服务器的配置文件中(如,redis6379.conf)设置:
slave-priority n
,n 表示作为从机时的优先级。默认为 100。值越小优先级越高,如 n = 10 要优先于 n = 50 被选作为新的主机。 - 偏移量越大越优先。偏移量是指从机获得的原主机中数据的多少。
- runid 越小越优先。每个 redis 服务启动后都会随机生成一个 40 位的 runid。
15 集群
15.1 介绍
容量不够,Redis 如何进行扩容?并发写操作,Redis 如何分摊?集群就是解决这些问题的方法。
Redis 集群实现了对 Redis 的水平扩容,即启动 N 个 Redis 节点,将整个数据库分布存储在这 N 个节点中,每个节点存储总数据的 1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
15.2 配置
- 删除之前实验生成的 dump.rdb:
rm -rvf dump*.rdb
,防止造成干扰。
- 模拟 6 个服务器,端口号使用 6379,6380,6381,6389,6390,6391。并创建这些服务器的配置文件。
(1)配置基本信息:
1. 开启 daemonize yes。
2. pidfile 的存储路径(pidfile 用来保存 pid)。
3. 指定端口 port。
4. log 文件名。
5. dbfilename(RDB 持久化文件名)。路径使用 dir 配置。
6. 关闭 AOF 持久化方式,或重命名 Appendonly.aof 文件名。
7. 参与集群的多 Redis 服务器,在集群之前不要设置密码,否则会导致 Redirect 失败。集群的密码可以在之后设置。之前的项目我都设置了密码,因此现在要把 redis.conf 中的 requirepass 注释掉,并设置 protected-mode no。
(2)配置集群信息:
1.cluster-enabled yes
:打开集群模式。
2.cluster-config-file nodes-xxx.conf
:设置节点配置文件名称。
3.cluster-node-timeout 15000
:设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换(相当于哨兵模式)。
- 启动这 6 个服务器。并检查 nodes.conf 文件是否正常生成。
- 将这 6 个服务器(节点)合并成一个集群。
(1)进入redis安装目录/src
下,我的是:cd /opt/redis-6.2.2/src
。
(2)执行命令:redis-cli [-a 密码] --cluster create --cluster-replicas 1 192.168.61.128:6379 192.168.61.128:6380 192.168.61.128:6381 192.168.61.128:6389 192.168.61.128:6390 192.168.61.128:6391
,此处的 ip 地址要使用服务器的真实地址(可以使用 ifconfig 查看),不能使用 127.0.0.1。-replicas 1
:采用最简单的方式配置集群,一台主机,一台从机,正好三组。
(3)以集群的方式进入客户端:redis-cli -c -p port
。
- 使用命令:
cluster nodes
:命令查看集群信息。
- 在某一个客户端使用 set 命令时,会将添加的数据自动对应一个 slot(插槽),并将服务器跳转到对应的端口。get 命令也可以自动跳转到包含所需数据的服务器内。
15.3 原理及命令
- redis cluster 如何分配这六个节点?
一个集群至少要有三个主节点。
选项:--cluster-replicas n
,表示为集群中的每个主节点创建 n 个从节点。
分配原则:(1)尽量保证每个主服务器运行在不同的 ip 地址上(2)尽量保证每个主服务器和它的从服务器不运行在同一个 ip 地址上。 - slots 插槽:
一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个。集群中的每个节点负责处理一部分插槽。 例如,一个集群有 3 个主节点, 那么:
节点 A 负责处理 0 - 5460 号插槽。
节点 B 负责处理 5461 - 10922 号插槽。
节点 C 负责处理 10923 - 16383 号插槽。 - 在集群中录入值:
(1)使用 redis-cli 启动的客户端,每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是该客户端对应服务器的插槽,redis 会报错,并告知应前往的 redis 实例地址和端口。
(2)使用 redis-cli -c -p 启动的客户端,提供了 –c 参数实现自动重定向。每次录入、查询键值对可以自动重定向。
(3)不在一个同 slot 下的键值,不能使用 mget,mset 等批量操作。
(4)可以通过{}
定义键所在的组,这样就可以使组相同的键存储到同一个 slot 中。
- 集群的一些命令:
(1)cluster keyslot k
:计算键 k 对应的插槽。
(2)cluster countkeysinslot s
:获取插槽 s 中包含的键值对数量。
(3)cluster getkeysinslot s c
:返回 c 个 插槽 s 中的键。如,获取插槽 1230 中的 5 个键。
- 集群的故障处理:
(1)如果主节点下线,从节点能否自动升为主节点?若超时 15 秒还没有上线,则从节点上升为主节点。
(2)主节点恢复后,主从关系会如何?主节点再次上线后变成从节点。
(3)如果某一段插槽的主从节点都下线了,那么 Redis 服务是否还能继续?不能。这种情况受到 redis.conf 中的参数 cluster-require-full-coverage 约束。该参数为 yes 表示 16384 个插槽都正常时,才能对外提供服务。
如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 yes ,则整个集群都不能使用,无法进行读取操作;若 cluster-require-full-coverage 为 no ,则该段插槽将不能使用,这段插槽无法进行读取操作。
15.4 Jedis 操作集群的 JedisCluster
Jedis 类与 JedisCluster 类的区别是启动客户端的方式不同。类内的许多方法都是相同的。
public class MyCluster {
public static void main(String[] args) {
HashSet<HostAndPort> nodes = new HashSet<>();
HostAndPort hostAndPort = new HostAndPort("192.168.61.128",6379);
nodes.add(hostAndPort);
JedisCluster cluster = new JedisCluster(nodes);
// cluster.set("jedisClusterKey","jedisClusterVal");
String key = cluster.get("jedisClusterKey");
System.out.println(key);
cluster.close();
}
}
15.5 集群的优缺点
优点:
- 实现扩容
- 分摊压力
- 无中心配置相对简单
缺点:
- 不支持多键操作。
- 不支持多键事务。
- 不支持 lUA 脚本。
- 由于 Redis Cluster 集群方案出现较晚,很多公司已经采用了其他的集群方案。而代理或客户端分片的方案想要迁移至 Redis Cluster,需要整体迁移而非逐步过渡,复杂度较大。