之前讲到单机redis的为了保证数据安全,必须做好数据备份等基础工作。但是如果流量越来越大,
redis的读写请求压力越来越大,到了一个极限值,性能依旧不够用我们应该如何处理?
这边文章就分为以下几步来一次介绍redis读写分离:
- 1、两个问题分析redis瓶颈
- 2、解释什么是读写分离以及读写分离流程与原理
- 3、读写分离对于数据的安全意义
- 4、docker搭建一主两从读写分离的实战(附带配置和操作流程)
- 5、总结
1、两个问题分析redis瓶颈
所以先看看一下两个问题:
- 1.redis不能支撑高并发的瓶颈在哪里?
- 第一,一台redis的性能首先和机器性能,业务复杂度,以及redis操作的复杂度这三点是密切相关的。
- 如果是单纯的String ,key,value操作,性能一般能到1-5w qps。
- 第二,当一台性能不够用,可以横向扩展机器,就能达到扩容的效果。
- 2.如何redis要支撑超过10w+的并发,那应该怎么做?
- 单机redis几乎不可能超过10w+,除非机器性能非常好,物理机,维护好,操作简单
- 所以必须能横向扩展,采取读写分离主从架构(master - slave)
- 一个主节点,只写数据
- 多个从节点,主节点把数据同步给多个从节点
- 然后读就从从节点读,分摊读压力,从节点还可以横向扩展
注意:读写分离是分摊读请求的压力,如果是写请求的操作瓶颈,需要通过cluster让多节点写,
或者通过异步队列,异步完成写操作的方式解决
2、解释什么是读写分离以及读写分离流程与原理
读写分离就是基于redis replication搭建多个redis节点,然后分为master主节点,slave从节点。
master节点主要作用是接收所有写请求,写数据,然后同步到slave。然后多个slave节点,每个都有
全量的数据,分摊读请求。简单是说,就是master只负责写和同步数据到slave,slave只负责读。
这种架构适用于读多写少的系统,slave可以横向扩展,接收更多的读请求。
master和slave数据同步的完整流程
首先介绍一下复制过程中的一些核心机制
-
offset
- master和slave都会维护一个offset
- 主要作用是在全量复制过程中记录上方的数据差异
- slave每秒都会上报自己的offset给master,同时master也会保存每个slave的offset
- 这个倒不是说特定就用在全量复制的,主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的情况
-
backlog
- master node有一个backlog,默认是1MB大小
- master node给slave node复制数据时,也会将数据在backlog中同步写一份
- backlog主要是用来做全量复制中断后的增量复制的
-
master run id
- info server,可以看到master run id
- 如果根据host+ip定位master node,是不靠谱的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分,run id不同就做全量复制
- 如果需要不更改run id重启redis,可以使用redis-cli debug reload命令
-
psync
- 从节点使用psync从master node进行复制,psync runid offset
- master node会根据自身的情况返回响应信息,可能是FULLRESYNC runid offset触发全量复制,可能是CONTINUE触发增量复
-
heartbeat
- 主从节点互相都会发送heartbeat信息
- master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat
-
主从复制的断点续传
- 如果主从复制过程中,网络连接断开了,那么可以接着上次复制的地方,连续复制下去,而不是从头开始复制一份
- master node会在内存中存一个backlog,master和skave都会保存一个replica offset 还有一个master id ,offset就是保存在backlog中的,如果master和slave网络连接断掉了,slave会让master从上次的replica offset 开始继续复制。但是如果没有找到对应的offset,那么就会执行一次 resynchronhronization
数据复制流程:
-
复制的完整流程
- msater就是根据slave发送的psync中的offset来从backlog中获取数据的
- slave node内部有个定时任务,每秒检查是否有新的master node要连接和复制,如果发现,就跟master node建立socket网络连接。
- slave node发送ping命令给master node
- master node第一次执行全量复制,将所有数据发给slave node
- master node后续持续将写命令,异步增量复制给slave node
-
全量复制
- 当slave刚连上来,或者master重启,run id不一致,就执行全量复制
- master执行bgsave,在本地生成一份rdb快照文件
- master node将rdb快照文件发送给salve node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可以适当调节大这个参数
- master node在生成rdb时,会将所有新的写命令缓存在内存中,在salve node保存了rdb之后,再将新的写命令复制给salve node
- slave node接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中,同时基于旧的数据版本对外提供服务
- 如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF
- 注意:
- master node将rdb快照文件发送给salve node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可以适当调节大这个参数
- 对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s
- client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
- rdb生成、rdb通过网络拷贝、slave旧数据的清理、slave aof rewrite,很耗费时间
- 如果复制的数据量在4G~6G之间,那么很可能全量复制时间消耗到1分半到2分钟
-
增量复制
- 如果全量复制过程中,master-slave网络连接断掉,那么salve重新连接master时,会触发增量复制
- master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
- msater就是根据slave发送的psync中的offset来从backlog中获取数据的
-
异步复制
- master每次接收到写命令之后,先在内部写入数据,然后异步发送给slave node
3、读写分离对于数据的安全意义
- 首先,master是必须做数据持久化的,因为这里是数据来源,slave做不做无所谓了。
- 然后,没个slave都有全量数据,也就可以成为master的一个热备份,当master挂了,能顶上去
- slave顶替master在后面哨兵继续讲。
4、docker搭建一主两从读写分离的实战(附带配置和操作流程)
都是放在/data/redis/redis.conf下,dir都是容器内的/data目录。
redis的master节点配置:
port 6379
requirepass "123456"
dbfilename "dump.rdb"
dir "/data"
logfile "/data/redis.log"
save 10 5
appendonly yes
appendfsync everysec
redis的slave配置(slave的ip和端口,填写自己的master,我这里是192.168.5.48 6379)
port 6379
requirepass "123456"
slaveof 192.168.5.10 6379
masterauth "123456"
slave-read-only yes
docker启动脚本
docker run -d --name redis -p 6379:6379 -v /data/redis/:/data/ redis redis-server /data/redis.conf
启动容器后,查看replication的命令
docker exec -it redis /bin/bash
redis-cli -h 127.0.0.1 -a 123456 -p 6379
info replication
操作步骤
- 准备三台centos虚拟机(可参照我之前的文章:virtualBox+vagrant搭建多节点虚机)
- 复制master的redis配置到/data/redis/redis.conf
- 使用docker启动命令启动master的redis
- 另外两台虚拟机重复上面的操作,唯一不同的就是修改redis.conf内容为上面从节点的配置,注意修改slave of的ip和端口
-
三个redis都启动完毕,就在每台机器上执行上面查看replication命令,查看集群是否搭建完毕。下图是搭建成功的示例。
验证数据同步:
- 首先进入master的redis-cli ,执行
set name along
- 然后去从节点去执行命令:
get name
,如果发现,能获取到,就说明master把数据同步给了slave
5、总结
- 当面对读多写少的业务场景,可以选择redis主从复制,读写分离的架构
- 单机是redis提高qps的瓶颈,好的架构能横向扩展
- 主从复制,读写分离的一些缺点
- 如何保证高可用?(这个后面在哨兵中讲解)
- 每个redis都是全量数据,比较浪费内存(后面cluster有解决方案)