Redis 事务和主从复制

目录

1. 事务

1.1 什么是事务

1.2 事务操作

2.主从复制

2.1 配置

2.2 断开复制 

2.3 传输延迟

3 拓扑

3.1 一主一从

3.2 一主多从

3.3 树形主从结构

4.原理

4.1 复制过程

4.2 数据同步

4.3 psync 运行流程

4.4 全量复制

4.5 部分复制

4.6 实时复制


1. 事务

1.1 什么是事务

Redis 事务和 MySQL 的事务概念上是类似的,都是把一系列操作打包到一起,让这一组能够批量执行,Redis 的事务和 MySQL 事务的区别:

1)不保证原子性:由于 Redis 没有回滚机制,因此,只能让这些操作批量执行,不能做到一个失败就恢复到初始状态

2)不具备一致性:由于 Redis 没有约束,没有回滚机制,事务执行过程中如果某个修改操作失败,可能就会出现不一致的情况

3)不具备持久性:Redis 本身就是内存数据库,数据都是存储在内存中,Redis 中的持久化机制与事务没有任何关系

4)不具备隔离性:Redis 是一个单线程模型的服务器程序,所有的请求都是串行执行的

Redis 事务本质上是在服务器上搞了一个事务队列,每次客户端在事务中进行一个操作,都会把命令先发给服务器,放到事务队列中,在收到 EXEC 命令之后,才真正执行队列中的所有操作

1.2 事务操作

MULTI

开启一个事务,执行成功返回 OK

127.0.0.1:6379> MULTI
OK

EXEC

真正执行事务

每次添加一个操作,都会提示 QUEUED,此时命名已经进入客户端的队列中了,当执行 EXEC 的时候,客户端才会把上述操作发送给服务器

DISCARD

放弃当前事务,直接清空事务队列,之前的操作都不会真正执行

WATCH

在执行事务的时候,如果某个事务中修改了值哦,被别的客户端修改了,此时就容易出现数据不一致的问题

客户端1

客户端2

客户端1

上述出入的命令中,是客户端限1先执行 set key1 111,然后客户端2后执行 set key1 222,但是从实际的执行时间看,时客户端2先执行,然后客户端1后执行,此时,当前的操作可能存在风险

watch 命令就可以解决这个问题

1)当开启事务的时候,如果对 watch 监控的 key 进行修改,就会记录当前 key 的版本号(类似于 CAS 中的 ABA 问题)

2)在真正提交事务的时候,如果发现当前服务器上 key 的版本号已经超过了事务开始时的版本号,就会让事务执行失败,也就是事务中的所有操作都不执行

例如: 

初始 key1 111

客户端1先执行

开始监控 key1,给 key1 分配版本号,并记录这个版本号,开启事务,然后进行修改(此处还没有开始进行修改),假设 key1 的版本号是0

注意:watch 必须搭配事务使用,并且必须再 multi 之前使用

客户端2再执行

此时就该成功,key1 的版本号从 0 变为 1

客户端1再执行

此时对比版本号发现 key1 的版本号不一致,说明其他客户端修改了 key1,因此事务被取消,这次提交的所有命令都不执行

2.主从复制

在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他服务器,从而满足故障恢复和负载均衡等需求,在 Redis 中也是如此,在 Redis 中存在若干个节点,有的是主节点,有的是从节点,例如,有三个节点,其中就可以把一个作为主节点,其他两个作为从节点(从节点上的数据要和主节点保持一致),当主节点进行数据修改,也会同步到从节点上去

Redis 主从模式中,从节点上的数据是不允许修改,只能读取数据,后续如果由客户端来读取数据,就可以在三个节点中挑选一个节点来读取数据

2.1 配置

建立复制

参与复制的 Redis 示例划分为主节点(master)和从节点(slave),每个从节点只能有一个主节点,而一个主节点可以同时具有多个从节点,复制的数据是单向的,只能由主节点到从节点

在配置文件中加入 slaveof  {masterHost} {masterPort} 随 Redis 启动⽣效

将 redis.conf 配置文件复制⼀份 redis-slave.conf,并且修改其 daemonize 为 yes 和端口号,并配置主从结构

# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6380

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes

#加上主从结构的配置
slaveof 127.0.0.1 6379

通过 redis-cli 可以连接主 Redis实例,通过 redis-cli -p 6380 连接从 Redis

127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"

127.0.0.1:6380> get hello
"world"

从上述结果中可以看到复制已经工作了,针对 6379 的修改都可以同步到从节点 6380 中

可以通过 info replication 命令查看复制相关状态

1)主节点 6379 复制状态信息

# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=2061,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=2061,lag=1
master_replid:be769cc2e9d35ed551b27ab09fa5608213862e88
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2061
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2061

主节点中的 offset 就相当于是从节点和主节点之间同步数据的进度,主节点上会收到源源不断的"修改请求",从节点就需要从主节点上同步数据(从节点和主节点之间的数据同步不是瞬间完成的)

2)从节点 6380 复制状态信息

# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:2131
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:be769cc2e9d35ed551b27ab09fa5608213862e88
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2131
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2131

2.2 断开复制 

slave 命令不仅可以建立复制,还可以在从节点执行 slave no one 来断开与主节点的复制关系,从节点断开复制后并不会抛弃原有数据,只是无法在获取主节点上的数据变化

断开复制的主要流程:

1)断开与主节点复制关系

2)从节点晋升为主节点

通过 slaveof 命令还可以实现切主操作,将当前从节点的数据源切换到另一个直接点,执行 slaveof {newMasterIp} {newMasterPort} 命令即可

2.3 传输延迟

主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis 提供了 repl-disable-tcp-nodelay 参数用于控制是否关闭 TCP_NODELAY,默认为 no

1)当关闭时,主节点产生的命令数据无论大小都会及时的发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗,适用于主从之间的网络环境良好的场景

2)当开启时,主节点会合并较小的 TCP 数据包从而节省带宽,默认发送时间间隔取决于 Linux 的内核,一般默认为 40ms,这种配置节省了带宽但增加了主从之间的延迟,适用于主从网络环境复杂的场景

3 拓扑

Redis 的复制拓扑结构可以支持单层或者多层复制关系,根据拓扑的复杂性可以分为三种:一主一从、一主多从、树状主从结构

3.1 一主一从

一主一从是最简单的复制拓扑结构,如果写数据请求太多,会给主节点造成一些压力,因此可以通过关闭主节点的 AOF,只在从节点上开启 AOF,这种结构有一个缺陷,当主节点挂了,不能让主节点自动启动,如果自动启动,此时没有 AOF 文件,就会丢失数据,进一步的主从同步会把从节点的数据也给删除了

3.2 一主多从

在实际开发中,读请求远远大于写请求,一主多从使得应用端可以利用多个从节点实现读写分离,对于读比较大的场景,可以把读命令负载均衡到不同的从节点来分担压力,但是当主节点的数据发生变化,就会把改变的数据同时同步给所有的从节点,而随着从节点个数的增加,同一条数据就需要传输多次

3.3 树形主从结构

树形主从结构使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续往下层复制,主节点不需要那么高的网卡带宽了,但是一旦数据修改了,同步的延时是比刚才更长

4.原理

4.1 复制过程

主从节点建立复制流程图

1)从节点先保存主节点的 IP 和端口

2) 建立 TCP 的连接(三次握手),验证通信双方是否能正确读写数据

3)从节点发送 ping 命令,主节点返回 pong 命令,建立连接成功

4)权限验证,如果主节点开启了密码,则需要密码验证,如果验证失败,从节点的复制将会停止

5)同步数据集,这里分为两种:全量同步和部分同步

6)当从节点复制了主节点的所有数据之后,针对之后的修改命令,主节点会持续的把命令发送给从节点,从节点执行修改命令,保证主从数据的一致性

4.2 数据同步

Redis 提供了 psync 命令来完成数据同步的过程,psync 不需要手动执行,Redis 服务器会在建立号主从同步关系之后,自动执行 psync,从节点负责执行 psync,从节点从主节点这边拉取数据

PSYNC 语法格式

PSYNC replicationid offset

如果 replicationid 设为 ?并且 offset 设为 -1,此时就是尝试进行全量复制

如果 replicationid 设为了具体的数值,则是尝试进行部分复制

1 replicationid / replid(复制 id)

主节点的复制 id,主节点重新启动,或者从节点晋级成为主节点,都会生成一个 replicationid(同一个节点,每次重启,生成的 replicationid 也会变化),从节点和主节点建立连接之后,就会获取到主节点的 replicationid

 关于 master_replid 和 master_replid2

每个节点需要记录两组  master_replid ,例如当前有两个节点 A 和 B,A为主节点,B为从节点,此时 B 就会记录 A 的 master_replid,如果网路出现抖动,B 认为 A 挂了,B 就会自己成为直接点,于是 B 给自己分配了新的  master_replid,此时就会使用  master_replid2 来保存之前 A 的  master_replid

后续如果网络恢复了,B 就可以根据  master_replid2 找回之前的主节点

后续如果网络没有恢复,B 就按照新的  master_replid 自称一派,继续处理后续的数据 

2 offset(偏移量)

主节点和从节点都会维护偏移量,主节点的偏移量,主节点上会收到很多修改操作的命令,每个命令占几个字符,主节点会把这些修改命令,每个命令的字节数进行累加;从节点的偏移量描述了,现在从节点的数据同步到哪了,如果主从节点的偏移量一样,就认为数据一致了

4.3 psync 运行流程

1)从节点发送 psync 命令给主节点,replid 和 offset 的默认值分别是 ?和 -1

2)主节点根据 psync 参数和自身数据情况决定响应结果:

如果是 1,则从节点需要进行全量复制流程

如果是 2,从节点进行部分复制流程

如果是 3,说明 Redis 主节点版本过低,不支持 psync 命令,从节点可以使用 sync 命令进行全量复制

4.4 全量复制

当主从第一次建立复制时必须经历的阶段

全量复制流程

1)从节点发送 psync 命令给主节点进行数据同步,由于时第一次进行复制,从节点没有主节点的 ID 和偏移量,所以发送 psync ?-1

2) 主节点根据命令解析出进行全量复制,回复 + FULLRESYNC 响应

3)从节点接收主节点的信息并保存

4)主节点执行 bgsave 进行 RDB 文件持久化

5)主节点发送 RDB 文件给从节点,从节点保存 RDB 数据到本地硬盘

6)主节点将从生成 RDB 到接收完成期间可能会有写操作,主节点将这些写入缓冲区中,等从节点保存完 RDB 文件后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照 RDB 的二进制格式追加写入到从节点收到的 RDB 文件中,保持主从一致

7)从节点清空自身原有的旧数据,保证只有一个 RDB 文件

8)从节点加载 RDB 文件得到与主节点一致的数据

9)如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进行 bgrewrite 操作,得到最接近的 AOF 文件

注意:

默认情况下,进行全量复制需要主节点生成 RDB 文件到主节点的磁盘中,再把磁盘上的 RDB 文件发送给从节点

在 Redis 2.8.18 开始支持无磁盘复制,主节点在执行 RDB 生成流程时,不会生成 RDB 文件到磁盘中,而是直接把生成的 RDB 数据通过网路发送给从节点,这样就节省了一系列的写硬盘和读硬盘的操作开销

4.5 部分复制

部分复制主要是 Redis 针对全量复制过高开销做出的一种优化措施,当从节点正在复制主节点时,如果出现命令丢失等异常情况,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区存在数据直接发送给从节点,这样就可以保证主从复制的一致性,补发的数据远远小于全量数据,开销很小

部分复制流程

1)当主从节点之间出现网络中断时,如果超过了 repl-timeout 时间,主节点会认为从节点故障并中断复制连接

2)主从连接中断期间,主节点依旧响应命令,但这些复制命令都因网络中断无法发送给从节点,所以这些命令放在积压缓冲区中

3)当主从节点网络恢复后,从节点再次连上主节点

4)从节点将之前保存的 replicationId 和偏移量作为 psync 的参数发送给主节点,请求进行部分复制

5)主节点收到 psync 请求后,进行验证,随后根据 offset 去积压缓冲区查找合适的数据,并响应+CONTINUE 给从节点

6)主节点将需要从节点同步的数据发送给从节点,完成主从一致性

4.6 实时复制

当从节点已经和主节点同步好了数据(此时,从节点和主节点的数据是一致的),但是之后主节点会不断收到新的修改数据的请求,从节点和主节点之间会建立 TCP 的长连接,然后主节点把自己收到的修改数据的请求,通过上述连接,发送给从节点,从节点再根据这些修改请求,修改内存中的数据

这样的长连接需停药通过心跳包的方式来维护连接状态

主节点默认每隔 10s 给从节点发送一个 ping 命令,从节点收到就返回 pong

从节点默认每隔 1s 就给主节点发起一个特定的请求,就会上报当前从节点复制数据的进度

如果主节点发现从节点通信延迟超过 repl-timeout 配置的值(默认60s),则判定从节点下线,断开复制客户端连接,从节点恢复连接后,心跳机制继续进行

总结:

主从复制的特点:

1)Redis 通过复制功能实现主节点的多个副本

2)主节点⽤来写,从节点用来读,这样做可以降低主节点的访问压力

3)复制⽀持多种拓扑结构,可以在适当的场景选择合适的拓扑结构

4)复制分为全量复制,部分复制和实时复制

5)主从节点之间通过心跳机制保证主从节点通信正常和数据⼀致性

主从复制配置的过程:

1)主节点配置不需要改动

2)从节点在配置⽂件中加⼊ slaveof 主节点端口的形式即可

主从复制的缺点:

1)从机多了复制数据的延时非常明显

2)主机挂了,从机不会升级成主机,只能通过⼈工干预的方式恢复

  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值