1、概念
主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。数据的复制是单向的,只能由主服务器到从服务器。
通过主从复制,我们就可以随机的从服务器上读数据,从而大大降低了主服务器的压力,提高系统的性能,当主服务器正常关闭的时候,从服务器也可以待机,要是主服务器非正常关闭的话,也可以切换为其中的一台从服务器代替原来的主服务器,(作为灾备)以保证系统继续正常稳定运行,所以也有利于系统运行的安全性。
2、原理
1)主从复制理论
① 若启动一个Slave机器进程,则它会向Master机器发送一个sync_command命令,请求同步连接
② 无论是第一次连接还是重新连接,Master机器都会启动一个后台进程,将数据快照(RDB)保存到
数据文件中(执行rdb操作),同时Master还会记录修改数据的所有命令并缓存在数据文件中。
③ 后台进程完成缓存操作之后,Master机器就会向Slave机器发送数据文件,Slave端机器将数据
文件保存到硬盘上,然后将其加载到内存中,接着Master机器就会将修改数据的所有操作一并发送给Slave端机器。若Slave出现故障导致宕机,则恢复正常后会自动重新连接。
④ Master机器收到slave端机器的连接后,将其完整的数据文件发送给Slave端机几器,如果Mater同时收到多个slave发来的
同步请求则Master会在后台启动一个进程以保存数据文件,然后将其发送给所有的Slave端机器,确保所有的Slave端机器都正常。
1)主从复制大致流程
1、从-》主发送sync同步数据请求
2、主redis 会fork 一个子进程,然后产生RDB文件(完全备份)的过程
2.1客户端还在持续的写入Redis
3、RDB文件持久化完成后,主redis会将RDB文件和缓存起来的命令推送给从服务器
4、复制、推送完成后,主redis会持续的同步操作命令->利用AOF(增备的部分)持久化功能
5、在下一台从REDIS接入主从复制集群之前,会持续利用aof的方式同步数据给从redi
3、搭建Redis主从复制
( 1)安装Redis
三台服务器都需要安装
#按需求关闭安全策略
systemctl stop firewalld
systemctl disable firewalld
setenforce o
yum -y install gcc gcc-c++ make
cd / opt
wget -p /opt http://download.redis.io/releases/redis-5.0.9.tar.gz
tar -zxvf redis-5.0 .9.tar.gz
cd redis-5.0 .9
make && make PREFIX=/usr/ local/ redis install
#Redis源码包中直接提供了makefile文件直接执行make与make install命令进行安装
cd / opt / redis-5.o.9/utils/
./install server.sh
#回车,直到出现以下选项,手动修改为"/usr/local/redis/bin/redis-server"Please select the redis executable path [/usr/local/bin/redis-serverI
/usr/ local/ redis/bin/ redis-server
ln -s /usr/ local/redis/bin/*/usr/ local/bin/
#检查服务状态
Netstat -natp l grep “redis”
(2)修改Redis配置文件
①Master节点
vim /etc/ redis/ 6379.conf
bind 127.0.0.0.1 192.168.10.20
#70行,修改监听地址为
daemonize yes
#137行,开启守护进程
logfile /var/ 1oglredis_6379.1og
#172行,指定日志文件目录
dir /var/ lib/ redis/ 6379
#264行,指定工作目录
appendonly yes
#700行,开启AOF持久化功能
/etc/ init.d/ redis_6379 restart
#重启服务使配置生效
(3)验证主从效果
主节点输入
tail -f /var/ log / redis_6379.log
#Replication
role:master
connected_slaves : 2
slave0:ip=192.168.10.31,port=6379,state=online,offset=42,lag=1
slave1:ip=192.168.10.21,port=6379,state=online,offset=42,lag=1
#master启动时生成的40位16进制的随机字符串,用来标识master节点
master_replid:e920ac8821584bd21f9edbff2eeef48eabd3499c
#切换主从的时候master节点标识会有更改
master_replid2:0000000000000000000000000000000000000000
#复制流中的一个偏移量,master处理完写入命令后,会把命令的字节长度做累加记录,统计在该字段。该字段也是实现部分复制的关键字段。
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
#无论主从,都表示自己上次主实例repid1和复制偏移量;用于兄弟实例或级联复制,主库故障切换bsvlcvs
4、客户端配置实现读写分离
SpringBoot 操作Redis主从复制
1. 主从复制配置 application.yml
2. 主要代码
RedisTempla
/** * 自定义相关的主从 template * * @author xgk */ @Configuration @EnableConfigurationProperties({RedisMasterSlavesProperties.class}) public class RedisConfig { /** * Master主机 template * * @param properties 配置 */ @Bean("redisMasterTemplate") public RedisTemplateredisMasterTemplate(RedisMasterSlavesProperties properties) { RedisTemplate redisTemplate = new RedisTemplate<>(); // master RedisMaster master = properties.getRedisMaster(); // 连接工厂 RedisConnectionFactory factory = connectionFactory(master.getHost(), master.getPort(), master.getPassword()); redisTemplate.setConnectionFactory(factory); // 序列化 return serializerTemplate(redisTemplate); } /** * Slaves从机 template * * @param properties * @return */ @Bean("redisSlaveTemplateList") public List > redisSlaveTemplateList(RedisMasterSlavesProperties properties) { List > redisTemplates = new ArrayList<>(); for (RedisSlave slave : properties.getRedisSlaves()) { RedisTemplate redisTemplate = new RedisTemplate<>(); // 连接工厂 RedisConnectionFactory factory = connectionFactory(slave.getHost(), slave.getPort(), slave.getPassword()); redisTemplate.setConnectionFactory(factory); // 序列化并放置集合 redisTemplates.add(serializerTemplate(redisTemplate)); } return redisTemplates; } ... }
RedisUtils
/** * 主机Master 只进行写操作, 从机Slave 只进行读操作 * * @author xgk */ @Component public class RedisUtils { // 主机 @Autowired @Qualifier("redisMasterTemplate") public RedisTemplateredisMasterTemplate; // 从机 @Autowired @Qualifier("redisSlaveTemplateList") public List > redisSlaveTemplateList; private int slaveIndex = 0; // 选择用那台从机(通过取余方式决定) private int buildSlaveIndex() { this.slaveIndex++; return this.slaveIndex % 2; } // 定义的写操作方法 ------------------------------------------ public boolean set(String key, Object value) { try { redisMasterTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } // 定义的读操作方法 ------------------------------------------ public Object get(String key) { return key == null ? null : redisSlaveTemplateList.get(buildSlaveIndex()).opsForValue().get(key); } ... }
5、主从复制的缺点
主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预。
因为只有主节点能进行写操作,一旦主节点宕机,整个服务就无法使用。当然此时从节点仍可以进行读操作,但是对于整个服务流程来说,是无法使用的。
Master的写的压力难以降低。
如果写操作比较多,那么只有一个主节点的话,无法分担压力。
主节点存储能力受到单击限制。
主节点只能有一个,因此单节点内存大小不会太大,因此存储数据量受限。
主从数据同步,可能产生部分的性能影响甚至同步风暴。
风暴问题,对于任何集群分布式来说都存在,要合理分布节点。
注意点:
1)主从数据同步的延迟问题?
2)如何保证读写的数据一致性?
问:为什么会出现不一致?
1.延迟
答:主从同步有时延,这个时延期间读从库,可能读到不一致的数据。
如上图:
(1)服务发起了一个写请求
(2)服务又发起了一个读请求,此时同步未完成,读到一个不一致的脏数据
(3)数据库主从同步最后才完成
画外音:任何数据冗余,必将引发一致性问题。
2.宕机
我们知道Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。特点是先加锁成功后,后续的加锁命令会失败,一般用来做分布式锁,但是在系统宕机时,会存在问题。
以Redission分布式锁为例,就是如果你对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master slave实例。
假设客户端1对master写了一条锁数据,正往slave实例同步,尚未完成,但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。
接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。此时就会导致多个客户端对一个分布式锁完成了加锁。这时系统在业务上一定会出现问题,导致脏数据的产生。所以这个就是redis cluster,或者是redis master-slave架构的主从异步复制导致的redis分布式锁的最大缺陷:在redis master实例宕机的时候,可能导致多个客户端同时完成加锁。
如何解决:
参考:
redis面试:由于主从延迟导致读取到过期数据怎么处理_OceanStar的学习笔记的博客-CSDN博客_redis主从延迟
(2条消息) redis 主从库如何实现数据一致?_wzp、、、的博客-CSDN博客_redis 主从数据一致性实现