、事务与主从复制全解析----《Hello Redis!》(5))
文章目录前言持久化RDBAOF混合持久化事务主从复制前言在前面的系列文章中我们已经完成了 Redis 基础入门、五大核心数据类型、C 客户端编程实战掌握了 Redis 的基础使用与代码落地能力。而想要将 Redis 真正应用到生产环境、高并发分布式系统中数据安全保障、命令原子性控制、服务高可用部署是必须攻克的三大核心难题。本文将进入 Redis进阶核心篇章聚焦三大生产级关键技术持久化机制详解 RDB 快照、AOF 日志、混合持久化三种方案解决 Redis 重启数据丢失问题兼顾数据安全性与恢复效率事务控制讲解 Redis 弱事务特性、MULTI/EXEC/WATCH命令实现理解乐观锁在 Redis 中的应用主从复制从单点问题切入掌握主从架构搭建、全量 / 增量同步原理、拓扑结构优化实现 Redis 读性能扩展与服务高可用。本文从原理、配置、命令、实战、优缺点全方位拆解帮你构建完整的 Redis 生产级知识体系从容应对数据安全、服务扩容、故障容错等真实业务场景。持久化Redis实现持久化有两种策略:1.RDB(属于定期备份)2.AOF(属于实时备份)RDBRedis服务器默认都是有开启rdb的RDB定期把Redis内存中的所有数据都给写入硬盘中生成一个快照–后续就可以使用这个快照来进行回滚这里的定期备份有两种方式:(自动触发是一直生效的!)1.手动触发通过手动执行特定的命令(save或者bgsave)来触发快照生成save:一般不建议用因为redis会只进行快照生成这个任务会阻塞redis其他客户端的命令bgsave:通过多进程的方式进行快照生成不会影响Redis服务器处理其他客户端的请求和命令–save命令的话是直接在当前进程中往刚才同一个文件中写入数据2.自动触发a.在Redis配置文件中可以设置Redis每产生多少次修改就触发一次修改详细步骤:打开redis.conf在里找save eg:save 300 10这就表示如果 300 秒内至少有 10 个 key 被修改了就触发(如果写成save 的话就表明关闭自动触发这个功能)b.redis进行主从复制时主节点会自动生成rdb快照然后把rdb快照文件内容传输给从节点c.shutdown来关闭Redis时会触发(注意:暴力手段是不会触发的比如:kill -9)–但是要注意:生成一次rdb快照的成本是比较高的所以不能让这个操作执行的太频繁!!!Redis用RDB持久化生成的全量数据备份文件叫做dump.rdb(属于是经过压缩后生成的二进制文件–虽然压缩消耗cpu但是可以大幅降低文件的体积)其所在的位置是redis.conf里面的dir选项决定的可以用Redis自带的rdb文件的检查工具去检查其完整性:redis-check-rdb在执行生成快照时:会把要生成的快照数据先保存到一个临时文件里当这个快照生成完毕时再删除之前的rdb文件把新生成的临时的rdb文件名字改成刚才的dump.rdbredis每次启动时都会1.尝试加载配置目录里的dump.rdb文件把里面的快照数据恢复到内存2.如果发现文件格式损坏数据异常(比如网络传输啥都可能会导致文件损坏)的话Redis可能会直接拒绝启动–如果坏的只是文件的末尾一般是可以启动的(但是数据会出问题)如果是中间位置坏了的话会直接拒绝启动注意:如果执行flushall的话Redis触发RDB持久化时会把空的内存数据把直接的RDB文件给覆盖了的!引申:Linux文件系统主要分为三大部分:1.超级块(存放一些管理信息) 2.inode区(存放inode节点) 3.block区(存放文件的数据内容)修改完配置文件一般需要重启对应的服务器才会生效(除非是用命令的方式修改的)RDB的优点:Redis在重启时RDB恢复数据的速度快于AOF的原因:因为RDB使用的是二进制的方式组织数据在恢复数据时直接拷贝到内存就完事了AOF则是存的命令日志(纯文本形式的字符串)需要进行一系列的字符串的切分相较于AOFRDB 是一个紧凑压缩的二进制文件代表 Redis 在某个时间点上的数据快照。非常适用于备份全量复制等场景。RDB的缺点:RDB最大的问题就是不是实时保存数据–在两次生成快照的间隔间的数据可能会因为重启而丢失而且老版本的redis的rdb文件放到新版本的redis中不一定能识别(AOF则出现这种情况的概率很小)–的确需要redis版本升级的话:就需要遍历旧的redis中的所有key然后把数据插入到新的redis服务器里AOFAOF 以独立日志的方式记录每次写命令(通过一些特殊符号作为分隔符)Redis 重启时会重新执行 AOF 文件中的命令以此达到恢复数据的目的。AOF这样让工作线程先把数据写入内存中的缓冲区积累多了后再统一写入硬盘–大大降低了写硬盘的次数(写硬盘的效率跟写入硬盘数据的多少一般没有多大关系但是跟写入硬盘的次数的关系很大)而且AOF每次把新的操作写入到原有文件的末尾属于顺序写入(硬盘上读写数据如果是顺序读写的会比随机访问快很多)关于上面的rewrite是AOF的重写机制redis里面有个机制能针对AOF文件进行内容的整理删除里面冗余的操作合并一些操作来让AOF文件变小AOF 的主要作用是解决了数据持久化的实时性问题目前已经是 Redis 持久化的主流方式但是缓冲区没来的及写入硬盘的数据丢失的风险也是很大的–刷盘频率的调整要去redis.conf里面取搞:里面的appendfsync选项always(每写一条命令就立刻刷盘)everysec(每秒刷一次盘)no(不主动刷盘)AOF默认一般是关闭的需要把redis.conf里面那个修改成appendonly yes才行AOF开启后RD就不生效了–redis重新启动时就是读取AOF文件(默认叫appendonly.aof)中的内容来恢复数据了关于AOF重写机制的详细流程:重写时子进程只需要把内存中当前的数据取出来以AOF的格式写入到一个新的AOF文件中–不用关心原来AOF文件里有啥创建子进程的一瞬间子进程就继承了当前父进程的内存状态–父进程fork之后收到的数据会搞两份aof_buf是刷新到旧AOF文件里面的aof_rewrite_buf是等子进程写完后刷到新AOF文件里的这时再用新的AOF替换旧的AOF–aof_buf时完全有必要的因为重写到一半如果服务器挂了重写就终止了到头来还是要用旧AOF文件AOF的重写过程可以手动触发或者自动触发:手动触发:用bgrewriteaof命令–如果当前redis已经在进行AOF的重写了的话–此时就会直接返回–如果当前redis正在生成RDB文件快照的话–会等RDB快照生成完毕之后再进行AOF重写自动触发:看redis.conf配置文件里的auto-aof-rewrite-min-size(触发重写时AOF的最小文件大小)和auto-aof-rewrite-percentage(当前AOF占用大小相较上次重写时增加的比例)混合持久化开启混合持久化:需要在redis.conf中aof-use-rdb-preamble yes混合持久化的运作:按照AOF的方式将每个操作都记录进文件在触发AOF重写之后就会把当前内存的状态按照RDB的二进制格式写入到新的AOF文件中;后续再进程操作的话仍然是按照AOF文本的方式追加到文件的后面如果Redis上同时存在AOF文件和RDB快照的话–此时以AOF为主(因为AOF中包含的数据比RDB的更全)事务如果Redis按照集群模式部署的话是不支持事务的!Redis事务这里任何能实现的效果都能通过lua脚本进行替代!Redis事务的特性:弱原子性:把多个操作打包在一起要么全都执行要么全都不执行(不存在有一个执行失败就回滚)不具备一致性 不具备持久性 不涉及隔离性(因为是单线程模型所有的请求都是串行执行的)–Redis事务其实就是为了打包命令一次性执行防止别人插队Redis中的事务是通过队列进行实现的:(每个客户端一个队列)开启事务时此时客户端输入的命令就会进入服务器的这个队列中;当遇到执行事务的命令时就会把队列中的这些任务都按照顺序依次执行Redis中有关事务的命令:1.开启事务:multi2.执行事务:exec(执行完事务之后这个事务就关闭了)3.放弃当前事务:discard--如果事务途中服务器关闭了就相当于discard了4.watch key事务才用的到这个–在开启事务前执行监控如果key被其他客户端修改了的话事务里所有操作都会失效,exec时返回nil(非这个key的也会这个事务完全不含key操作时也会)–watch是通过观察key的版本号来判断key是否被修改了的!–watch在exec或者discard之后会自动结束5.unwatch:取消这个客户端的所有的监控引申:Redis命令里面没有能进行条件判定的但是支持lua脚本,可以用这个来进行条件判定引申:关于乐观锁和悲观锁乐观锁:预期接下来锁冲突的概率很低–比如这里的watch悲观锁:预期接下来锁冲突的概率很高主从复制单点问题:如果某个服务器程序只有一个节点(也就是说只有一个物理服务器)就会有1.可用性问题 2.性能/支持的并发量比较有限所以就需要引入分布式系统去解决这个单点问题在分布式系统中希望使用多个服务器来部署redis存在以下几种redis的部署方式:1.主从模式 2.主从哨兵模式 3.集群模式主从模式:就是有一个主节点和若干个从节点在主节点中保存了一堆数据从节点会主动建立连接发起复制请求然后主节点收到请求后生成快照把快照发给从节点注意:从节点上的数据不允许修改只允许读取!客户端进行读取操作都是从从节点那读取的如果挂掉某个从节点没啥影响 但是如果挂掉的是主节点如果不处理的话影响就很大了真正的分布式:主节点和不同从节点要跑在不同的服务器上才对但是下面进行模拟的话就用一个服务器启动多个不同端口的redis-server来凑合下就行了主从模式主要是针对读操作进行的并发量和可用性的提高在一个服务器上怎么模拟主从模式:(有三种方法)1.在从节点的配置文件里面认大哥(eg:slaveof 127.0.0.1 6379)然后启动多个不同端口的redis-server就行了2.在redis-server启动时加入--slaveof 127.0.0.1 63793.启动后运行命令:slaveof 127.0.0.1 6379–注意:这三个redis服务器的工作目录要区分开(修改配置文件中的dir选项)不然用service redis-server start方式启动时aof文件的权限不够 只能用redis-server以及他们的aof文件会混写在一起修改这个工作目录的方法:先把之前的服务器停止,然后删除之前工作目录下的aof文件或者修改aof文件所属的用户再重新指定工作目录即可从节点解除主从同步:slave no one–但是里面已经同步了的数据还是在的主节点和从节点是怎么保持联系的用netstat -anp来看就知道了–从节点是在底层相当于客户端主动向主节点发起TCP连接Redis中执行info replication可以看到主节点和从节点是怎么传输数据的:通过看主节点和从节点的offset就能知道他俩的数据是否一致了(主节点的offset的维护:把命令的字节数累加到offset里)还有个是repl_backlog_active(积压缓冲区),用处就是实现部分同步:主节点会把最近发生的数据修改不仅发给从节点还会往积压缓冲区里发一份来让从节点重连后可以部分同步主从同步时分为全量同步和增量同步:全量同步就是在从节点第一次连接主节点时发生的,增量同步就是通过那个积压缓冲区实现的关于积压缓冲区:就是内存中的一个简单的环形队列–记录最近一段时间修改的数据(但是它的总量有限如果存不下了就会把之前的旧数据给删掉)把主节点AOF持久化关掉让从节点来进行AOF备份虽然能让主节点压力变小但是会有一个非常严重的问题:主节点挂了之后自动重启的话会把主节点全空的状态同步给从节点改进方法:当主节点挂了之后让主节点从从节点那获取AOF的文件然后再启动执行slave命令时主从节点在底层干什么:从节点会1.保存主节点信息(保存主节点的ip和端口)2.主从建立连接(TCP–这样来验证通信双方是否能正确读写数据)3.发送ping命令(验证主节点是否能正常工作)4.权限验证(如果主节点设置了密码的话会有这步)5.同步数据集(从节点会先发psync replid offset主节点判断replication id是不是自己的然后再同步)6.命令持续复制深度理解psync:这个命令是Redis自动执行的不需要手动操作(也就主从复制时会用到)如果offset填-1就是获取全量数据;填具体的正整数就是从当前偏移量位置开始获取数据–但是并不是说从节点索要哪部分数据主节点就一定会给哪部分数据的(不方便给部分数据的话就会给全量数据)FULLRESYNC表示给从节点进行全量复制CONTINEU:表示给从节点进行部分复制-ERR表示Redis主节点版本过低不支持psync命令–如果replicationld不是自己的话那就必须给它全量复制–如果offset超出积压缓冲区的范围的话也必须给它全量复制才行全量复制的流程:(1从节点发送 psync 命令给主节点进行数据同步由于是第一次进行复制从节点没有主节点的运行 ID 和复制偏移量所以发送 psync ? -1。(2主节点根据命令解析出要进行全量复制回复 FULLRESYNC 响应。(3从节点接收主节点的运行信息进行保存。(4主节点执行 bgsave 进行 RDB 文件的持久化需要重新生成!不能用原来的RDB文件(5主节点发送 RDB 文件给从节点从节点保存 RDB 数据到本地硬盘。(6主节点将从生成 RDB 到接收完成期间执行的写命令写入缓冲区中等从节点保存完 RDB 文件后主节点再将缓冲区内的数据补发给从节点补发的数据仍然按照 rdb 的二进制格式追加写入到收到的 rdb 文件中。保持主从一致性。(7从节点清空自身原有旧数据。(8从节点加载 RDB 文件得到与主节点一致的数据。(9如果从节点加载 RDB 完成之后并且开启了 AOF 持久化功能它会进行 bgrewrite 操作得到最近的 AOF 文件。引申:主节点进行全量复制时可以支持无硬盘模式(主节点生成的rdb二进制数据不直接保存到文件中而是直接进行网络传输)–这样就省了主节点一系列的读写硬盘操作,从节点就省了写入硬盘然后再加载的过程了什么时候会进行全量复制:1.首次和主节点进行数据同步 2.主节点不方便进行部分复制的时候什么时候进行部分复制:1.从节点已经从主节点上复制过数据了因为网络抖动或从节点重启了时会先看看能不呢进行部分复制什么时候进行实时复制:主节点和从节点已经同步好数据建立TCP长连接之后的同步操作关于实时复制:主从节点之间建立好TCP长连接之后主节点会把自己收到的修改数据的请求通过这个连接发给从节点从节点也会依据这个请求去修改内存中的数据–这个传递过程导致的延时时间的话跟层级有关在实时复制时检验连接是不是一直处于可用状态–心跳包机制主节点会每隔10s给从节点发送一个ping命令,从节点收到就返回pong–超过60s都没有收到的话就会认为连接断了从节点每隔1s就会给主节点发送一个请求来上报当前从节点复制数据的进度–注意:这些具体时间60s 10s 1s都是可以在配置文件中修改的关于replication id:是主节点启动时或者从节点晋升成主节点时生成的(即便是同一个主节点每次重启生成的replication id都是不同的)关于info replication里面还有个master_replid2:从节点晋升成主节点时会把自己的以前的主节点的replid记到这里面–后续想回去的话可以手动干预replid2来回去关于runid和replid:这俩不是一个东西!runid是Redis进程的id在哨兵那才有用到(看进程是不是挂了)两种主从结构:扁平化结构这个结构的话:每同步一条数据主节点需要挨个传输给从节点–就对网卡带宽的要求特别高2.树状拓扑结构这个结构的话:进行数据修改时同步的延时比第一个结构要长引申:如果使用service redis-server start启动的话必须用service redis-server stop来停止–用kill -9去停止的话这个进程被杀死后会自动重启–因为会有另一个进程专门去监控指定的服务器进行的运行状态引申:TCP内部支持nagle算法(默认是开启的)(就是针对小的tcp数据包进行合并来减少包的个数)开启了就会增加tcp的传输延迟,节省了网络带宽;关闭了就会减少tcp的传输延迟,增加了网络带宽主从同步中关闭tcp的nagle算法的步骤:repl-disable-tcp-nodelay