配置
主数据库(master) 读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。
从数据库(slave) 一般是只读的,并接受主数据同步过来的数据。
在slave数据库/redis.conf 修改如下,master数据库 无须进行任何配置。
slaveof 127.0.0.1 6379
masterauth 123456
启动master数据库
[root@TA30-53 local]# ./redis-3.0.7/src/redis-server ./redis-3.0.7/redis.conf
启动slave数据库
[root@TA30-53 local]# ./redis-3.0.7-slave/src/redis-server ./redis-3.0.7-slave/redis.conf
打开redis-cli实例A 连接主数据库
[root@TA30-53 redis-3.0.7]# ./src/redis-cli -p 6379 -a 123456
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=1081,lag=1
master_repl_offset:1081
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:1080
127.0.0.1:6379> set name yinn
OK
127.0.0.1:6379> get name
"yinn"
打开redis-cli实例B 连接从数据库
[root@TA30-53 redis-3.0.7-slave]# ./src/redis-cli -p 6380 -a 123456
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:1282
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6380> get name
"yinn"
127.0.0.1:6380> set name 123
(error) READONLY You can't write against a read only slave.
除了通过配置文件或者命令行参数设置slaveof参数,还可以在运行时使用slaveof命令修改
redis>slaveof 127.0.0.1 6379
如果该数据库已经是其他主数据库的从数据库了,slaveof命令会停止和原来的数据库的同步,转而和新数据库同步。
对于从数据库来说,还可以使用slaveof no one 命令来使当前数据库停止接受其他数据库的同步并转换为主数据库。
原理
复制初始化:
1.当一个slave数据库启动后,会向master数据库发送 SYNC 命令。
2.master数据库接收到 SYNC命令后会开始在后台保存快照(RDB持久化的过程),并将保存快照期间接收到的命令缓存起来。
当快照完成后,Redis会将快照文件和所有缓存的命令发送给slave数据库。
3.slave数据库收到后,会载入快照文件并执行收到的缓存的命令。
复制同步阶段:
复制初始化结束后,master数据库每当收到写命令时就会将命令同步给slave数据库,从而保证主从数据库数据一致。
当主从数据库之间的连接断开重连后,Redis 2.6以及之前的版本会重新进行复制初始化。
Redis 2.8版能够支持 断线重连有条件的增量数据传输,主数据库只需要将断线期间执行的命令传送给从数据库。
Redis采用了乐观复制(optimistic replication)的复制策略,容忍在一定时间内主从数据库的内容是不同的,但是两者的数据会最终同步。具体来说,Redis在主从数据库之间复制数据的过程本身是异步的,主数据库执行完客户端请求的命令后会立即将命令在主数据库的执行结果返回给客户端,并异步地将命令同步给从数据库。
min-slaves-to-write 1
min-slaves-max-lag 10
min-slaves-to-write表示只有当大于等于1个的从数据库连接到主数据库时,主数据库才是可写的,否则会返回错误(error) READONLY You can't write against a read only slave.
min-slaves-max-lag 表示允许从数据库最长失去连接的时间,如果从数据库最后与主数据库联系(即发送 REPLCONF ACK命令)的时间小于这个值,则认为从数据库还在保持与主数据库的连接。通过复制可以实现读写分离,以提高服务器的负载能力。在常见的场景中,读的频率大于写,当单机的Redis无法应付大量的读请求时(尤其是较耗资源的请求,如 SORT 命令等)可以通过复制功能建立多个从数据库节点,主数据库只进行写操作,而从数据库负责读操作。这种一主多从的结构很适合读多写少的场景,而当单个的主数据库不能够满足需求时,就需要使用Redis 3.0 推出的集群功能。
从数据库持久化
持久化操作对耗时,为了提高性能,可以通过复制功能多个从数据库,并在从数据库中启用持久化,同时在主数据库禁用持久化。当从数据库崩溃重启后主数据库会自动将数据同步过来,所以无需担心数据丢失。当主数据库崩溃时,需要手工 通过从数据库数据恢复主数据库数据,需要严格按照以下两步进行。
(1)在从数据库中使用 SLAVEOF NO ONE命令将从数据库提升成主数据库继续服务。
(2)启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置成新的主数据库的从数据库,即可将数据同步回来。
注意 当开启复制且主数据库关闭持久化功能时,一定不要使用 Supervisor 以及类似的进程管理工具令主数据库崩溃后自动重启。同样当主数据库所在的服务器因故关闭时,也要避免直接重新启动。
因为当主数据库重新启动后,因为没有开启持久化功能,所以数据库中所有数据都被清空,这时从数据库依然会从主数据库中接收数据,使得所有从数据库也被清空,导致从数据库的持久化失去意义。
无硬盘复制
Redis复制的工作原理是基于RDB方式的持久化实现的,即主数据库端在后台保存 RDB 快照,从数据库端则接收并载入快照文件。这样的实现优点是可以显著地简化逻辑,复用已有的代码,但是缺点也很明显。
(1)当主数据库禁用RDB快照时(即删除了所有的配置文件中的save语句),如果执行了复制初始化操作,Redis依然会生成RDB快照,所以下次启动后主数据库会以该快照恢复数据。因为复制发生的时间不能确定,这使得恢复的数据可能是任何时间点的。
(2)因为复制初始化时需要在硬盘中创建RDB快照文件,所以如果硬盘性能很慢时这一过程会对性能产生影响。举例来说,当使用 Redis 做缓存系统时,因为不需要持久化,所以服务器的硬盘读写速度可能较差。但是当该缓存系统使用一主多从的集群架构时,每次和从数据库同步,Redis都会执行一次快照,同时对硬盘进行读写,导致性能降低。
因此从2.8.18版本开始,Redis引入了无硬盘复制选项,开启该选项时,Redis在与从数据库进行复制初始化时将不会将快照内容存储到硬盘上,而是直接通过网络发送给从数据库,避免了硬盘的性能瓶颈。
目前无硬盘复制的功能还在试验阶段,可以在配置文件中使用如下配置来开启该功能: repl-diskless-sync yes
增量复制
上面介绍复制的原理时提到当主从数据库连接断开后,从数据库会发送SYNC命令来重新进行一次完整复制操作。
这样即使断开期间数据库的变化很小(甚至没有),也需要将数据库中的所有数据重新快照并传送一次。
Redis 2.8版实现了主从断线重连的情况下的增量复制。
(1)从数据库会存储主数据库的RUN ID。每个Redis 运行实例均会拥有一个唯一的运行ID,每当实例重启后,就会自动生成一个新的运行ID。
(2)在复制同步阶段,主数据库每将一个命令传送给从数据库时,都会同时把该命令存放到一个积压队列(backlog)中,并记录下当前积压队列中存放的命令的偏移量范围。
(3)同时,从数据库接收到主数据库传来的命令时,会记录下该命令的偏移量。
这3点是实现增量复制的基础。
之前主从通信流程,当主从连接准备就绪后,从数据库会发送一条 SYNC 命令来告诉主数据库可以开始把所有数据同步过来了。
而 2.8 版之后,不再发送 SYNC命令,而是发送 PSYNC,格式为“PSYNC主数据库的运行 ID 断开前最新的命令偏移量”。
主数据库收到 PSYNC命令后,会执行以下判断来决定此次重连是否可以执行增量复制。
(1)首先主数据库会判断从数据库传送来的运行ID是否和自己的运行ID相同。确保从数据库之前确实是和自己同步的,以免从数据库拿到错误的数据(比如主数据库在断线期间重启过,会造成数据的不一致)。
(2)然后判断从数据库最后同步成功的命令偏移量是否在积压队列中,如果在则可以执行增量复制,并将积压队列中相应的命令发送给从数据库。
如果此次重连不满足增量复制的条件,主数据库会进行一次全部同步(即与Redis 2.6的过程相同)。
需要开发者设置的就是积压队列的大小了。 积压队列在本质上是一个固定长度的循环队列,默认情况下积压队列的大小为 1 MB,积压队列越大,其允许的主从数据库断线的时间就越长。
repl-backlog-size 1mb
因为积压队列存储的内容是命令本身,如 SET foo bar,所以估算积压队列的大小只需要估计主从数据库断线的时间中主数据库可能执行的命令的大小即可。
与积压队列相关的另一个配置选项是repl-backlog-ttl,即当所有从数据库与主数据库断开连接后,经过多久时间可以释放积压队列的内存空间。默认时间是1小时。
repl-backlog-ttl 3600