一.读写分离
1.对读写能力进行扩展,采用读写分离方式解决性能问题
运行一些额外的服务器,让他们与主服务器进行连接,然后将主服务器发送的数据副本通过网络进行准实时的更新(具体的更新速度取决于网络带宽),通过将请求分散到不同的服务器上面进行处理,用户可以从新添加的从服务器上获得额外的读查询处理能力
2.redis自身集成了读写分离供用户使用
我们只需在从redis服务的配置文件里面加上一条,【slaveof 主节点ip 主节点port】语句
3.redis读写分离之数据同步
(1)进行复制中的主从服务器双方的数据库将保存相同的数据,概念上将这种现象称作“数据库状态一致”
(2)redis2.8版本之前使用旧版复制功能SYNC
全量同步
全量同步发生在初次同步和断线重连同步这两个阶段
当从服务器第一次连上主服务器,此时要进行一次初步的同步操作,具体流程如下:
a.从服务器向主服务器发送sync指令
b.主服务器收到指令,执行BGSAVE指令,也就是RDB,在后台生成一个rdb文件,并适用一个缓冲区开始记录写命令
c.主服务器生成rdb文件后,将该文件发给从服务器,从服务器将这个二进制文件加载到内存中
d.主服务器将缓冲区中的写操作发送给从服务器,从服务器逐个执行,此时就完成了一次全量同步
当从服务器挂掉了,重新连接上来的时候,主从之间数据已经不一致了,那么此时还要进行一次同步。在Redis2.8之前,会采用一次全量同步。而在Redis2.8之后,就会灵活选择策略,这个在后面说
在执行完初次同步之后,就完事了吗?当然不是,主服务器是不断处理写请求的,数据不断更新,此时也需要有实时同步策略才能保证主从一致
增量同步其实很简单,主服务器将每一条写命令都发送给从服务器去执行,就能保证动态保证主从一致了
Redis2.8之前同步策略的缺点
这个缺点主要是表现在断线重连之后,直接进行全量同步的这个策略。
考虑这样一个场景,从数据库虽然挂掉了,但是很快就重连上来了,此时它丢失的写操作只有很少,比如说只有几条,那这时直接进行一次全量同步sync很明显是不划算的操作
并且sync是一个重量级操作:
- 生成rdb文件需要进行磁盘IO,如果数据量很大的话速度很慢
- 由父进程fork出一个子进程进行的,消耗CPU资源
- 生成rdb文件实际上是Copy-On-Write方法进行的,内存会暴涨原来的两倍
所以Redis2.8之后主要就是针对该点进行了优化,用psync替代了sync
(3)redis2.8版本之后使用PSYNC
Redis2.8之后,主要是对断线重连之后的同步策略进行了优化。在断线重连之后,可以自动按照实际情况选择是进行全量同步还是增量同步的价值更高
这个策略的实现由三个部分组成
- 复制偏移量
- 复制积压缓存区
- 运行ID
主从服务器都会各自维护一个复制偏移量,当主服务器向从服务器传播N个字节时,自己的复制偏移量加上N;从服务器接受到主服务器传来的N个字节时,自己的复制偏移量也会加上N。所以如果主从的复制偏移量相同,则说明主从一致。
那么根据复制偏移量的差异就可以得知主从服务器之间从哪到哪的数据是没有同步的,根据这个数量可以选择具体要实现哪个策略来进行重同步。要作出选择,还需要基于另一个东西,也就是复制积压缓存区
复制积压缓存区其实是一个先进先出(FIFO)队列,但是和传统的先进先出队列不一样,里面的元素不能被主动取出,只有当队列已满,并且再添加新元素时,队列头的元素才会被挤出来。所以该队列的长度是固定的。
当主服务器执行写命令时,它不仅会传播给从服务器,还会将该命令+复制偏移量加入队列中,如图:
而当从服务器断线重连后,如果它的复制偏移量对应的操作还存在于主服务器的队列中,说明断线没多久,此时进行增量同步操作
如果复制偏移量的值已经不在队列中了,说明已经被挤出去了,此时进行全量同步
那设置复制积压偏移量这个队列的大小就很关键了,它能间接决定断线重连后同步的效率。这个队列的最小值一般可以设定为 second*write_size_per_second second为从服务器的平均断线时间,write_size_per_second为主服务器平均每秒执行的写操作数量
除了复制偏移量和复制积压缓存区之外,实现部分重同步还需要一个运行ID
主从服务器都有一个自己的运行ID,由40个随机的十六进制字符组成。
当从服务器第一次连接上主服务器时,主服务器会将自己的运行ID发给从服务器,从服务器会保存这个运行ID
当从服务器断线重连后,首先会向主服务器发送自己记录的主服务器运行ID
- 若发送的运行ID与主服务器的运行ID相同,说明从服务器之前连的就是该主服务器,此时可以根据策略选择进行全量同步还是增量同步
- 若不同,说明从服务器连接上了新的主人,此时直接进行全量同步