Redis事务和主从复制

张开发
2026/4/9 1:26:34 15 分钟阅读

分享文章

Redis事务和主从复制
什么是事务Redis 的事务和MySQL的事务概念上是类似的.都是把⼀系列操作绑定成⼀组.让这⼀组能够批量执 ⾏.但是注意体会Redis的事务和MySQL事务的区别:弱化的原⼦性: redis没有回滚机制.只能做到这些操作批量执⾏. 不能做到⼀个失败就恢复到 初始状态.不保证⼀致性: 不涉及约束. 也没有回滚.MySQL的⼀致性体现的是运⾏事务前和运⾏后,结果都 是合理有效的,不会出现中间⾮法状态.不需要隔离性: 也没有隔离级别,因为不会并发执⾏事务(redis单线程处理请求).不需要持久性: 是保存在内存的.是否开启持久化,是redis-server⾃⼰的事情,和事务⽆关.Redis 事务本质上是在服务器上搞了⼀个事务队列.每次客⼾端在事务中进⾏⼀个操作,都会把命令先 发给服务器,放到事务队列中(但是并不会⽴即执⾏)⽽是会在真正收到EXEC命令之后,才真正执⾏队列中的所有操作因此,Redis的事务的功能相⽐于MySQL来说,是弱化很多的.只能保证事务中的这⼏个操作是连续 的, 不会被别的客⼾端加塞,仅此⽽已.事务操作MULTI开启⼀个事务.执⾏成功返回OK实例127.0.0.1:6379 MULTI OKEXEC真正执⾏事务实例127.0.0.1:6379 MULTI OK 127.0.0.1:6379 set k1 1 QUEUED 127.0.0.1:6379 set k2 2 QUEUED 127.0.0.1:6379 set k3 3 QUEUED 127.0.0.1:6379 EXEC 1) OK 2) OK 3) OK每次添加⼀个操作,都会提⽰QUEUED,说明命令已经进⼊客⼾端的队列了. 真正执⾏EXEC的时候,客⼾端才会真正把上述操作发送给服务器. 此时就可以获取到上述key的值了.127.0.0.1:6379 get k1 1 127.0.0.1:6379 get k2 2 127.0.0.1:6379 get k3 3DISCARD放弃当前事务.此时直接清空事务队列.之前的操作都不会真正执⾏到.实例127.0.0.1:6379 MULTI OK 127.0.0.1:6379 set k1 1 QUEUED 127.0.0.1:6379 set k2 2 QUEUED 127.0.0.1:6379 DISCARD OK 127.0.0.1:6379 get k1 (nil) 127.0.0.1:6379 get k2 (nil)WATCH在执⾏事务的时候,如果某个事务中修改的值,被别的客⼾端修改了,此时就容易出现数据不⼀致的问 题.实例# 客⼾端 1 先执⾏ 127.0.0.1:6379 MULTI OK 127.0.0.1:6379 set key 100 QUEUED # 客⼾端 2 再执⾏ 127.0.0.1:6379 set key 200 OK # 客⼾端 1 最后执⾏ 127.0.0.1:6379 EXEC 1) OK 127.0.0.1:6379 get key 100此时,key的值是多少呢??从输⼊命令的时间看,是客⼾端1 先执⾏的set key 100.客⼾端2 后执⾏的set key 200. 但是从实际的执⾏时间看,是客⼾端2 先执⾏的,客⼾端1 后执⾏的.这个时候,其实就容易引起歧义. 因此,即使不保证严格的隔离性,⾄少也要告诉⽤⼾,当前的操作可能存在⻛险watch命令就是⽤来解决这个问题的.watch在该客⼾端上监控⼀组具体的key.当开启事务的时候,如果对watch的key进⾏修改,就会记录当前key的版本号.(版本号是个简单 的整数,每次修改都会使版本变⼤.服务器来维护每个key的版本号情况)在真正提交事务的时候,如果发现当前服务器上的key的版本号已经超过了事务开始时的版本号,就 会让事务执⾏失败.(事务中的所有操作都不执⾏)实例客⼾端1先执⾏127.0.0.1:6379 watch k1 # 开始监控 k1 OK 127.0.0.1:6379 MULTI OK 127.0.0.1:6379 set k1 100 # 进⾏修改, 从服务器获取 k1 版本号是 0. 记录 k1 的版本号.(没真修改呢, 版本号不变) QUEUED 127.0.0.1:6379 set k2 1000 QUEUED只是⼊队列,但是不提交事务执⾏.客⼾端2再执⾏127.0.0.1:6379 set k1 200 # 修改成功, 使服务器端的 k1 的版本号 0 - 1 OK客⼾端1再执⾏真正执⾏修改操作 , 此时对⽐版本发现 , 客⼾端的 k1 的版 本号是 0, 服务器上的版本号是 1, 版本不⼀致 ! 说明有其他客⼾端在事务中间修改了 k1 !!!127.0.0.1:6379 EXEC (nil) 127.0.0.1:6379 get k1 200 127.0.0.1:6379 get k2 (nil)此时说明事务已经被取消了.这次提交的所有命令都没有执⾏UNWATCH取消对key的监控. 相当于WATCH的逆操作.此处不做演⽰主从复制在分布式系统中为了解决单点问题通常会把数据复制多个副本部署到其他服务器满⾜故障恢 复和负载均衡等需求。Redis也是如此它为我们提供了复制的功能实现了相同数据的多个Redis副本。复制功能是⾼可⽤Redis的基础哨兵和集群都是在复制的基础上构建的配置建⽴复制参与复制的Redis实例划分为主节点master和从节点slave。每个从结点只能有⼀个主节点 ⽽⼀个主节点可以同时具有多个从结点。复制的数据流是单向的只能由主节点到从节点。配置复制的⽅式有以下三种在配置⽂件中加⼊ slaveof {masterHost} {masterPort} 随Redis启动⽣效。在 redis-server 启动命令时加⼊ --slaveof {masterHost} {masterPort} ⽣效。直接使⽤redis命令slaveof {masterHost} {masterPort} ⽣效。断开复制slaveof 命令不但可以建⽴复制还可以在从节点执⾏slaveof no one来断开与主节点复制关系。断开复制主要流程1断开与主节点复制关系。2从节点晋升为主节点。从节点断开复制后并不会抛弃原有数据只是⽆法再获取主节点上的数据变化通过slaveof命令还可以实现切主操作将当前从节点的数据源切换到另⼀个主节点。执⾏ slaveof {newMasterIp} {newMasterPort} 命令即可切主操作主要流程1断开与旧主节点复制关系。2与新主节点建⽴复制关系。3删除从节点当前所有数据。4从新主节点进⾏复制操作。安全性对于数据⽐较重要的节点主节点会通过设置requirepass参数进⾏密码验证这时所有的客⼾ 端访问必须使⽤auth命令实⾏校验。从节点与主节点的复制连接是通过⼀个特殊标识的客⼾端来完 成因此需要配置从节点的masterauth参数与主节点密码保持⼀致这样从节点才可以正确地连接到 主节点并发起复制流程。只读默认情况下从节点使⽤slave-read-onlyyes配置为只读模式。由于复制只能从主节点到从节 点对于从节点的任何修改主节点都⽆法感知修改从节点会造成主从数据不⼀致。所以建议线上不 要修改从节点的只读模式传输延迟主从节点⼀般部署在不同机器上复制时的⽹络延迟就成为需要考虑的问题Redis为我们提供 了repl-disable-tcp-nodelay 参数⽤于控制是否关闭TCP_NODELAY默认为no即开启tcp nodelay 功能说明如下当关闭时主节点产⽣的命令数据⽆论⼤⼩都会及时地发送给从节点这样主从之间延迟会变⼩ 但增加了⽹络带宽的消耗。适⽤于主从之间的⽹络环境良好的场景如同机房部署。当开启时主节点会合并较⼩的TCP数据包从⽽节省带宽。默认发送时间间隔取决于Linux的内 核⼀般默认为40毫秒。这种配置节省了带宽但增⼤主从之间的延迟。适⽤于主从⽹络环境复杂的场景如跨机房部署拓扑Redis 的复制拓扑结构可以⽀持单层或多层复制关系根据拓扑复杂性可以分为以下三种⼀主⼀ 从、⼀主多从、树状主从结构⼀主⼀从结构⼀主⼀从结构是最简单的复制拓扑结构⽤于主节点出现宕机时从节点提供故障转移⽀持如图 所⽰。当应⽤写命令并发量较⾼且需要持久化时可以只在从节点上开启AOF这样既可以保证数据 安全性同时也避免了持久化对主节点的性能⼲扰。但需要注意的是当主节点关闭持久化功能时如果主节点宕机要避免⾃动重启操作⼀主多从结构⼀主多从结构星形结构使得应⽤端可以利⽤多个从节点实现读写分离如图所⽰。对于读⽐重较⼤的场景可以把读命令负载均衡到不同的从节点上来分担压⼒。同时⼀些耗时的读命令可以指定⼀台专⻔的从节点执⾏避免破坏整体的稳定性。对于写并发量较⾼的场景多个从节点会导致主节点写命令的多次发送从⽽加重主节点的负载树形主从结构树形主从结构分层结构使得从节点不但可以复制主节点数据同时可以作为其他从节点的主 节点继续向下层复制。通过引⼊复制中间层可以有效降低住系欸按负载和需要传送给从节点的数据量如图所⽰。数据写⼊节点A之后会同步给B和C节点B节点进⼀步把数据同步给D和E节 点。当主节点需要挂载等多个从节点时为了避免对主节点的性能⼲扰可以采⽤这种拓扑结构原理主从节点建⽴复制流程图1 保存主节点master的信息开始配置主从同步关系之后从节点只保存主节点的地址信息此时建⽴复制流程还没有开始 在从节点6380执⾏info replication可以看到如下信息master_host: 127.0.0.1 master_port: 6379 master_link_status: down从统计信息可以看出主节点的ip和port被保存下来但是主节点的连接状master_link_status是下线状态。2从节点slave内部通过每秒运⾏的定时任务维护复制相关逻辑当定时任务发现存在新的主节点后会尝试与主节点建⽴基于TCP的⽹络连接。如果从节点⽆法建⽴连接定时任务会⽆限重试直到连接成功或者⽤⼾停⽌主从复制3发送ping命令。连接建⽴成功之后从节点通过ping命令确认主节点在应⽤层上是⼯作良好的。 如果ping命令的结果pong回复超时从节点会断开TCP连接等待定时任务下次重新建⽴连接4权限验证。如果主节点设置了 requirepass 参数则需要密码验证从节点通过配 masterauth 参数来设置密码。如果验证失败则从节点的复制将会停⽌。5同步数据集。对于⾸次建⽴复制的场景主节点会把当前持有的所有数据全部发送给从节点这步操作基本是耗时最⻓的所以⼜划分称两种情况全量同步和部分同步下⼀节重点介绍6命令持续复制。当从节点复制了主节点的所有数据之后针对之后的修改命令主节点会持续的把命令发送给从节点从节点执⾏修改命令保证主从数据的⼀致性数据同步psyncRedis 使⽤psync命令完成主从数据同步同步过程分为全量复制和部分复制全量复制⼀般⽤于初次复制场景Redis早期⽀持的复制功能只有全量复制它会把主节点全部数据⼀次性发送给从节点当数据量较⼤时会对主从节点和⽹络造成很⼤的开销。部分复制⽤于处理在主从复制中因⽹络闪断等原因造成的数据丢失场景当从节点再次连上主节点后如果条件允许主节点会补发数据给从节点。因为补发的数据远⼩于全量数据可以有效避免全量复制的过⾼开销。PSYNC的语法格式PSYNC replicationid offset如果replicationid 设为?并且offset设为-1 此时就是在尝试进⾏全量复制.如果replicationid offset 设为了具体的数值,则是尝试进⾏部分复制.replicationid/replid (复制id)主节点的复制id.主节点重新启动,或者从节点晋级成主节点,都会⽣成⼀个replicationid.(同⼀个节 点,每次重启,⽣成的replicationid也会变化).从节点在和主节点建⽴连接之后,就会获取到主节点的replicationid通过info replication即可看到replicationid127.0.0.1:6379 info replication # Replication role:master connected_slaves:0 master_replid:1da596acecf5a34b4b2aae45bd35be785691ae69 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0关于master_replid和master_replid2每个节点需要记录两组master_replid.这个设定解决的问题场景是这样的:⽐如当前有两个节点A和B,A为master,B为slave. 此时B就会记录A的master_replid. 如果⽹络出现抖动,B以为A挂了,B⾃⼰就会成为主节点.于是B给⾃⼰分配了新的master_replid. 此时就会使⽤master_replid2来保存之前A的master_replid.后续如果⽹络恢复了,B就可以根据master_replid2找回之前的主节点.后续如果⽹络没有恢复,B就按照新的master_replid⾃成⼀派,继续处理后续的数据offset (偏移量)参与复制的主从节点都会维护⾃⾝复制偏移量。主节点master在处理完写⼊命令后会把命令的字节⻓度做累加记录统计信息在info replication中的master_repl_offset指标中从节点slave每秒钟上报⾃⾝的复制偏移量给主节点因此主节点也会保存从节点的复制偏移量从节点在接受到主节点发送的命令后也会累加记录⾃⾝的偏移量。统计信息在info replication中的 slave_repl_offset 指标中复制偏移量的维护如图所⽰。通过对⽐主从节点的复制偏移量可以判断主从节点数据是否⼀致这个偏移量,就相当于学习进度.累加从偏移量⽐如⽼师这边准备了10个课件的内容.助教要想给同学答疑,也就需要学习完这10个课件的内容.这个偏移量就是当前助教学到了第⼏个课件了psync运⾏流程1从节点发送psync命令给主节点replid和offset的默认值分别是?和-1.2主节点根据psync参数和⾃⾝数据情况决定响应结果如果回复FULLRESYNCreplidoffset则从节点需要进⾏全量复制流程。如果回复CONTINEU从节点进⾏部分复制流程。如果回复-ERR说明Redis主节点版本过低不⽀持psync命令。从节点可以使⽤sync命令进⾏ 全量复制。psync⼀般不需要⼿动执⾏.Redis会在主从复制模式下⾃动调⽤执⾏.sync会阻塞redisserver处理其他请求.psync则不会全量复制全量复制是Redis最早⽀持的复制⽅式也是主从第⼀次建⽴复制时必须经历的阶段。全量复制的运⾏流程如图所⽰1从节点发送psync命令给主节点进⾏数据同步由于是第⼀次进⾏复制从节点没有主节点的运 ⾏ID和复制偏移量所以发送psync ? -1。2主节点根据命令解析出要进⾏全量复制回复FULLRESYNC响应。3从节点接收主节点的运⾏信息进⾏保存。4主节点执⾏bgsave进⾏RDB⽂件的持久化。5主节点发送RDB⽂件给从节点从节点保存RDB数据到本地硬盘。6主节点将从⽣成RDB到接收完成期间执⾏的写命令写⼊缓冲区中等从节点保存完RDB⽂件 后主节点再将缓冲区内的数据补发给从节点补发的数据仍然按照rdb的⼆进制格式追加写⼊到收到的rdb⽂件中.保持主从⼀致性。7从节点清空⾃⾝原有旧数据。8从节点加载RDB⽂件得到与主节点⼀致的数据。9如果从节点加载RDB完成之后并且开启了AOF持久化功能它会进⾏bgrewrite操作得到最 近的AOF⽂件通过分析全量复制的所有流程我们会发现全量复制是⼀件⾼成本的操作主节点bgsave的间 RDB在⽹络传输的时间从节点清空旧数据的时间从节点加载RDB的时间等。所以⼀般应该尽可能避免对已经有⼤量数据集的Redis进⾏全量复制。有磁盘复制vs⽆磁盘复制(diskless)默认情况下,进⾏全量复制需要主节点⽣成RDB⽂件到主节点的磁盘中,再把磁盘上的RDB ⽂件通过发送给从节点.Redis 从2.8.18版本开始⽀持⽆磁盘复制.主节点在执⾏RDB⽣成流程时,不会⽣成RDB⽂件到磁盘中了,⽽是直接把⽣成的RDB数据通过⽹络发送给从节点.这样就节省了⼀系列的写硬盘和读硬盘的操作开销.部分复制部分复制主要是Redis针对全量复制的过⾼开销做出的⼀种优化措施使⽤psync replicationId offset命令实现。当从节点正在复制主节点时如果出现⽹络闪断或者命令丢失等异常情况时从节点会向主节点要求补发丢失的命令数据如果主节点的复制积压缓冲区存在数据则直接发送给从节点 这样就可以保持主从节点复制的⼀致性。补发的这部分数据⼀般远远⼩于全量数据所以开销很⼩。 整体流程如图所⽰1当主从节点之间出现⽹络中断时如果超过repl-timeout时间主节点会认为从节点故障并终端 复制连接。2主从连接中断期间主节点依然响应命令但这些复制命令都因⽹络中断⽆法及时发送给从节点所 以暂时将这些命令滞留在复制积压缓冲区中。3当主从节点⽹络恢复后从节点再次连上主节点。4从节点将之前保存的replicationId和复制偏移量作为psync的参数发送给主节点请求进⾏部分 复制。5主节点接到psync请求后进⾏必要的验证。随后根据offset去复制积压缓冲区查找合适的数据 并响应CONTINUE给从节点。6主节点将需要从节点同步的数据发送给从节点最终完成⼀致性。复制积压缓冲区复制积压缓冲区是保存在主节点上的⼀个固定⻓度的队列默认⼤⼩为1MB当主节点有连接的从节点slave时被创建这时主节点master响应写命令时不但会把命令发送给从节点还会写⼊复制积压缓冲区如图所⽰由于缓冲区本质上是先进先出的定⻓队列所以能实现保存最近已复制数据的功能⽤于部分复制和复制命令丢失的数据补救。复制缓冲区相关统计信息可以通过主节点的info replication中127.0.0.1:6379 info replication # Replication role:master ... repl_backlog_active:1 // 开启复制缓冲区 repl_backlog_size:1048576 // 缓冲区最⼤⻓度 repl_backlog_first_byte_offset:7479 // 起始偏移量计算当前缓冲区可⽤范围 repl_backlog_histlen:1048576 // 已保存数据的有效⻓度根据统计指标可算出复制积压缓冲区内的可⽤偏移量范围[repl_backlog_first_byte_offset, repl_backlog_first_byte_offset repl_backlog_histlen]这个相当于⼀个基于数组实现的环形队列.上述区间中的值就是数组下标如果当前从节点需要的数据,已经超出了主节点的积压缓冲区的范围,则⽆法进⾏部分复制,只 能全量复制了.实时复制主从节点在建⽴复制连接后主节点会把⾃⼰收到的修改操作,通过tcp⻓连接的⽅式,源源不断的传 输给从节点.从节点就会根据这些请求来同时修改⾃⾝的数据.从⽽保持和主节点数据的⼀致性另外,这样的⻓连接,需要通过⼼跳包的⽅式来维护连接状态.(这⾥的⼼跳是指应⽤层⾃⼰实现的⼼跳, ⽽不是TCP⾃带的⼼跳)1主从节点彼此都有⼼跳检测机制各⾃模拟成对⽅的客⼾端进⾏通信。2主节点默认每隔10秒对从节点发送ping命令判断从节点的存活性和连接状态。3从节点默认每隔1秒向主节点发送replconfack{offset}命令给主节点上报⾃⾝当前的复制偏移 量。如果主节点发现从节点通信延迟超过 repl-timeout 配置的值默认60秒则判定从节点下线断 开复制客⼾端连接。从节点恢复连接后⼼跳机制继续进⾏总结主从复制解决的问题:单点问题单个redis节点,可⽤性不⾼单个redis节点,性能有限主从复制的特点:Redis 通过复制功能实现主节点的多个副本。主节点⽤来写,从节点⽤来读.这样做可以降低主节点的访问压⼒.复制⽀持多种拓扑结构可以在适当的场景选择合适的拓扑结构。复制分为全量复制,部分复制和实时复制。主从节点之间通过⼼跳机制保证主从节点通信正常和数据⼀致性主从复制配置的过程:主节点配置不需要改动.从节点在配置⽂件中加⼊slaveof 主节点 ip 主节点端⼝ 的形式即可.主从复制的缺点:从机多了,复制数据的延时⾮常明显.主机挂了,从机不会升级成主机.只能通过⼈⼯⼲预的⽅式恢复.

更多文章