redis集群
- 从结构上,单个redis服务器会发生单点故障,同时一台服务器需要承受所有的请求负载,这就需要为数据生成多个副本并分配在不同的服务器上
- 从容量上,单个redis服务器的内存非常容易成为瓶颈,所以需要进行数据分片
复制(replication)
出现问题:
前面已经对数据进行了持久化,重启数据也不会丢失.但是假如这台服务器出现故障,数据就会丢失.为了避免单点故障,应该怎么做呢?
解决方案:
为了避免单点故障,通常的做法是将数据库复制多个副本以部署在多台服务器上,即使有一台服务器出现故障,其他服务器依然可以提供服务.为此,
可以使用redis的复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将新的数据同步到其他数据库上
配置主从数据库
作用:
主数据库用来进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库,从数据库一般都是只读的,接受从主数据库同步过来的数据.
一个主数据库可以拥有多个从数据库
配置文件配置:
在从数据库的配置文件中加入slaveof 主数据库地址 主数据库端口
即可,主数据库无需进行任何配置在终端配置主从:
这时在主数据库存储数据后在从数据库就可以查到了,但是默认情况下,从数据库是只读的,所以在从数据库中存储数据时会报错# 1.启动一个redis实例作为主数据库,该实例默认监听6379端口 reids-server # 2.加上slaveof参数启动另一个redis实例作为从数据库,监听6380端口 reids-server --port 6380 --slaveof 127.0.0.1 6379 # 3.在两个终端分别打开两个数据库实例 redis-cli -p 6379 redis-cli -p 6380 # 4.使用info命令能够获取replication节的相关信息 info replication # 主数据库: role为master,即主数据库,同时已连接的从数据库(connected_slaves)为1 role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=554,lag=0 # 从数据库: role为slave,即从数据库,同时其主数据库的地址为127.0.0.1,端口为6379 role:slave master_host:127.0.0.1 master_port:6379
ERR wrong number of arguments for 'set' command
在运行时配置主从
127.0.0.1:6380>SLAVEOF 127.0.0.1:6379
SLAVEOF命令会停止和原来数据库的同步而转为和新数据库同步- 配置多台从数据库的方法一样,在所有的从数据库的配置文件中都加上
slaveof
参数指向同一个主数据库即可 修改从数据库可写:
将从数据库的配置文件中的slave-read-only
设置为no
,但是一般情况下不会如此设置,否则可能会导致潜在的应用逻辑错误主从数据库转换
使用SLAVEOF NO ONE
命令来是当前数据库停止接收其他数据库的同步并转换为主数据库
redis复制的原理
redis复制的过程
1. 复制初始化
- 当一个从数据库启动后,会向主数据库发送SYNC命令.同时主数据库接收到SYNC命令后会开始在后台保存快照(即RDB持久的过程),
并将保存快照期间接收到的命令缓存起来. - 当快照完成后,主数据库会将快照文件和所有缓存的命令发送给从数据库
- 从数据库收到后,会载入快照文件并执行收到的缓存命令
知识点:
在redis2.6以及之前版本,当主从数据库之间断开重连后,主从数据库会重新执行复制初始化,即使从数据库只有几条命令没有收到.
这使得主从数据库断开重连后的数据恢复过程效率非常低下,在redis2.8有了重大的改版,支持有条件的增量数据传输,当从数据库断线重连后,
主数据库只需要将断线期间的执行的命令传送给从数据库,大大提高了复制的实用性.
乐观复制
- redis采用了乐观复制的复制策略,容忍在一定时间内主从数据库的内容是不同的,但是两者的数据会最终同步
- 过程: redis在主从数据库之间复制数据的过程本身是异步的,这意味着,主数据库执行完客户端请求的命令后会立即将命令在主数据库的执行结果返回给客户端,
并异步的将命令同步给从数据库,而不会等待从数据库接收到命令后再返回给客户端 - 影响: 这一特性保证了启用复制后主数据库的性能不会受到影响,但是会产生一个主从数据库不一致的时间窗口,当主数据库执行了一条写命令后,
主数据库的数据已经发生了变动,然而在主数据库将该命令传送给从数据库之前,如果两个数据库之间的网络断开连接,此时二者之间的数据就会使不一致的,
从这个角度看,主数据库是无法知道某个命令最终同步给了多少个数据库的 - 解决方法: 配置两个选项来限制只有当数据至少同步给指定数量的从数据库时,主数据库才是可写的
下面的配置中,min-slaves-to-write表示只有当3个或3个以上的从数据库连接到主数据库时,主数据库才是可写的,否则会返回错误NOREPLTCAS Not enough good slaves to write
min-slaves-max-lag 表示允许从数据库最长失去连接的时间,如果从数据库最后与主数据库联系(即发送REPLCONF ACK命令)的时间小于这个值,
则认为从数据库还在保持与主数据库的连接.
这一特性默认是关闭的,在分布式系统中,打开并合理配置该选项后可以降低主从架构中因为网络分区导致的数据不一致的问题
min-slaves-to-write 3
min-slaves-max-lag 10
图结构
从数据库不仅可以接受主数据库的同步数据,自己也可以同时作为主数据库的存在,形成类似图的结构
读写分离与一致性
- 通过复制可以实现读写分离,以提高服务器的负载能力,再常见的场景中,读的频率大于写
- 当单机的Redis无法应付大量的读请求时,可以通过复制功能建立多个从数据库节点,主数据库只进行写操作,而从数据库负责读操作.
- 一主多从的结构很适合读多写少的场景,而当单个主数据库不能满足需求时,就需要使用redis3.0推出的集群功能
从数据库持久化
- 当存在多个从数据库时,可以在从数据库中启用持久化,同时在主数据库禁止持久化,当从数据库崩溃重启后,主数据库会自动将数据同步过来,无须担心数据丢失
- 当主数据库崩溃时,手动进行操作
- 在从数据库中使用SLAVEOF NO ONE命令将从数据库提升为主数据库
- 启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置为新的主数据库的从数据库,将数据同步过来
- 当开启复制且主数据库关闭持久化功能时,一定不要使用supervisor以及类似的进程管理工具将主数据库自动重启,这样会导致主数据库和从数据库的数据全部丢失
- 我们可以使用自动化方案–哨兵来实现这一过程
无硬盘复制
- 复制初始化结束以后,主数据库执行的任何会导致数据变化的命令都会异步的传送给从数据库,这一过程称为复制同步阶段
- 复制同步阶段会贯穿整个主从同步过程的始终,直到主从关系终止为止
- 在复制过程中,快照无论在主数据库还是从数据库中都起到了很大的作用,只要执行复制就会进行快照,
即使我们关闭了RDB方式的持久化(通过删除所有save参数) - redis在2.8.18后支持了无硬盘复制
好处:
开启该选项时,Redis在与从数据库进行复制初始化时将不会将快照内容存储到硬盘上,而是直接通过网络发送给从数据库,避免的硬盘的性能瓶颈
开启方式:
在配置文件中使用如下配置repl-diskless-sync yes
增量复制
实现增量复制的基础:
- 从数据库会存储主数据库的运行ID(run id),每个Redis运行实例均会拥有一个唯一的运行ID,每当实例重启后,就会自动生成一个新的运行ID
- 在复制同步阶段,主数据库每将一个命令传送给从数据库时,都会同时把该命令存放到一个积压队列(backlog)中,并记录下当前积压队列中存放的命令的偏移量范围
- 同时从数据库接收到主数据库传来的命令时,会记录下该命令的偏移量
实现增量复制的原理
- 当主从连接准备就绪之后,从数据库会发送一条PSYNC命令,格式为
PSYNC 主数据库的运行ID 断开前最新的命令偏移量
- 首先主数据库会判断从数据库传过来的运行ID和自己的运行ID是否相同,这一步骤的意义在于确保从数据库之前确实是和自己同步的,
以免从数据库拿到错误的数据(比如主数据库在断线期间重启过,会造成数据的不一致) - 然后判断从数据库最后同步成功的命令偏移量是否在积压队列中,如果在则可以执行增量复制,并将积压队列中相应的命令发送给从数据库.
如果此次重连不满足增量复制的条件,主数据库会进行一次全部同步
唯一需要我们设置的是积压队列的大小
- 积压队列本质上是一个固定长度的循环队列,默认情况下积压队列的大小为1MB,可以通过配置文件的
repl-backlog-size
选项来调整 - 积压队列越大,其允许的主从数据库断线的时间就越长.根据主从数据库之间的网络状态,设置一个合理的积压队列很重要.
- 积压队列存储的是命令本身,如 SET name xiaoming, 所以估算积压队列的大小只需要估计主从数据库断线的时间中主数据库可能执行的命令的大小即可
- 与积压队列相关的另一个配置选项是repl-backlog-ttl,即当所有从数据库与主数据库断开连接后,经过多久时间可以释放积压队列的内存空间,默认时间为1小时