{"msg":"操作成功","code":200,"data":{"createBy":"admin","createTime":"2020-10-09 13:29:44","updateBy":"admin","updateTime":"2020-10-09 13:29:44","remark":null,"id":35,"articleTitle":"Redis（二）之Redis持久化方式","articleUrl":"redis_persistentence","articleThumbnail":"https://www.asumimoe.com/imgfiles/20220908/828bf19b43f2452d8540393410257d30.png","articleFlag":"1","draftStatus":"1","reprintStatement":"0","articleSummary":"在Redis中，实现高可用的技术主要包括持久化、复制、哨兵和集群，持久化是最简单的高可用方法(有时甚至不被归为高可用的手段)，主要作用是数据备份，即将数据存储在硬盘，保证数据不会因进程退出而丢失。","articleContent":"## 一、Redis高可用概述\n\n在介绍Redis高可用之前，先说明一下在Redis的语境中高可用的含义。\n\n我们知道，在web服务器中，高可用是指服务器可以正常访问的时间，衡量的标准是在多长时间内可以提供正常服务（99.9%、99.99%、99.999%  等等）。但是在Redis语境中，高可用的含义似乎要宽泛一些，除了保证提供正常服务(如主从分离、快速容灾技术)，还需要考虑数据容量的扩展、数据安全不会丢失等。\n\n在Redis中，实现高可用的技术主要包括持久化、复制、哨兵和集群，下面分别说明它们的作用，以及解决了什么样的问题。\n\n1. 持久化：持久化是最简单的高可用方法(有时甚至不被归为高可用的手段)，主要作用是数据备份，即将数据存储在硬盘，保证数据不会因进程退出而丢失。\n2. 复制：复制是高可用Redis的基础，哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份，以及对于读操作的负载均衡和简单的故障恢复。缺陷：故障恢复无法自动化；写操作无法负载均衡；存储能力受到单机的限制。\n3. 哨兵：在复制的基础上，哨兵实现了自动化的故障恢复。缺陷：写操作无法负载均衡；存储能力受到单机的限制。\n4. 集群：通过集群，Redis解决了写操作无法负载均衡，以及存储能力受到单机限制的问题，实现了较为完善的高可用方案。\n\n## 二、Redis持久化概述\n\n持久化的功能：Redis是内存数据库，数据都是存储在内存中，为了避免进程退出导致数据的永久丢失，需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘；当下次Redis重启时，利用持久化文件实现数据恢复。除此之外，为了进行灾难备份，可以将持久化文件拷贝到一个远程位置。\n\nRedis持久化分为RDB持久化和AOF持久化**：前者将当前数据保存到硬盘，后者则是将每次执行的写命令保存到硬盘（类似于MySQL的binlog）；**由于AOF持久化的实时性更好，即当进程意外退出时丢失的数据更少，因此AOF是目前主流的持久化方式，不过RDB持久化仍然有其用武之地。\n\n下面依次介绍RDB持久化和AOF持久化；由于Redis各个版本之间存在差异，如无特殊说明，以Redis5.0为准。\n\n## 三、RDB持久化\n\nRDB持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化)，保存的文件后缀是rdb；当Redis重新启动时，可以读取快照文件恢复数据。\n\n### 触发条件\n\nRDB持久化的触发分为手动触发和自动触发两种。\n\n1. 手动触发\n\n   save和bgsave命令都会生产RDB文件。\n\n   save命令会阻塞Redis服务器进程，直到RDB文件创建完毕为止，在Redis服务器阻塞期间，服务器不能处理任何命令请求。\n\n   而bgsave命令会创建一个子进程，由子进程负责创建RDB文件，父进程（即Redis主进程）则继续处理请求。\n\n   ```bash\n   127.0.0.1:6379> set mystr hello\n   OK\n   127.0.0.1:6379> save\n   OK\n   127.0.0.1:6379> set mystr1 world\n   OK\n   127.0.0.1:6379> bgsave\n   Background saving started\n   ```\n\n   bgsave命令执行过程中，只有fork子进程时会阻塞服务器，而对于save命令，整个过程都会阻塞服务器，因此save已基本被废弃，线上环境要杜绝save的使用；后文中也将只介绍bgsave命令。此外，在自动触发RDB持久化时，Redis也会选择bgsave而不是save来进行持久化；下面介绍自动触发RDB持久化的条件。\n\n2. 自动触发\n\n   **save m n**\n\n   自动触发最常见的情况是在配置文件中通过save m n，指定当m秒内发生n次变化时，会触发bgsave。\n\n   例如，查看redis的默认配置文件(Linux下为redis根目录下的redis.conf)，可以看到如下配置信息：\n\n   ```shell\n   #   save <seconds> <changes>\n   #\n   #   Will save the DB if both the given number of seconds and the given\n   #   number of write operations against the DB occurred.\n   #\n   #   In the example below the behaviour will be to save:\n   #   after 900 sec (15 min) if at least 1 key changed\n   #   after 300 sec (5 min) if at least 10 keys changed\n   #   after 60 sec if at least 10000 keys changed\n   #\n   #   Note: you can disable saving completely by commenting out all \"save\" lines.\n   #\n   #   It is also possible to remove all the previously configured save\n   #   points by adding a save directive with a single empty string argument\n   #   like in the following example:\n   #\n   #   save \"\"\n   \n   save 900 1\n   save 300 10\n   save 60 10000\n   ```\n\n   其中save 900 1的含义是：当时间到900秒时，如果redis数据发生了至少1次变化，则执行bgsave；save 300 10和save 60 10000同理。当三个save条件满足任意一个时，都会引起bgsave的调用。\n\n   **save m n的实现原理**\n\n   Redis的save m n，是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。\n\n   serverCron是Redis服务器的周期性操作函数，默认每隔100ms执行一次；该函数对服务器的状态进行维护，其中一项工作就是检查 save m n 配置的条件是否满足，如果满足就执行bgsave。\n\n   dirty计数器是Redis服务器维持的一个状态，记录了上一次执行bgsave/save命令后，服务器状态进行了多少次修改(包括增删改)；而当save/bgsave执行完成后，会将dirty重新置为0。\n\n   例如，如果Redis执行了set mystr helloworld，则dirty值会+1；如果执行了sadd myset v1 v2 v3，则dirty值会+3；注意dirty记录的是服务器进行了多少次修改，而不是客户端执行了多少修改数据的命令。\n\n   lastsave时间戳也是Redis服务器维持的一个状态，记录的是上一次成功执行save/bgsave的时间。\n\n   save m n的原理如下：每隔100ms，执行serverCron函数；在serverCron函数中，遍历save m n配置的保存条件，只要有一个条件满足，就进行bgsave。对于每一个save m n条件，只有下面两条同时满足时才算满足：\n\n   （1）当前时间-lastsave > m\n\n   （2）dirty >= n\n\n### RDB文件\n\n1. 存储路径\n\n   RDB文件的存储路径既可以在启动前配置，也可以通过命令动态设定。\n\n   配置：dir配置指定目录，dbfilename指定文件名。默认是Redis根目录下的dump.rdb文件。\n\n   动态设定：Redis启动后也可以动态修改RDB存储路径，在磁盘损害或空间不足时非常有用；执行命令为config set dir {newdir}和config set dbfilename {newFileName}。\n\n2. 压缩\n\n   Redis默认采用LZF算法对RDB文件进行压缩。虽然压缩耗时，但是可以大大减小RDB文件的体积，因此压缩默认开启；可以通过命令关闭：\n\n   ```bash\n   config set rdbcompression no\n   ```\n\n   需要注意的是，RDB文件的压缩并不是针对整个文件进行的，而是对数据库中的字符串进行的，且只有在字符串达到一定长度(20字节)时才会进行。\n\n### 启动时加载\n\nRDB文件的载入工作是在服务器启动时自动执行的，并没有专门的命令。但是由于AOF的优先级更高，因此当AOF开启时，Redis会优先载入AOF文件来恢复数据；只有当AOF关闭时，才会在Redis服务器启动时检测RDB文件，并自动载入。服务器载入RDB文件期间处于阻塞状态，直到载入完成为止。\n\nRedis载入RDB文件时，会对RDB文件进行校验，如果文件损坏，则日志中会打印错误，Redis启动失败。\n\n### RDB常用配置总结\n\n- save m n：bgsave自动触发的条件；如果没有save m n配置，相当于自动的RDB持久化关闭，不过此时仍可以通过其他方式触发\n- stop-writes-on-bgsave-error  yes：当bgsave出现错误时，Redis是否停止执行写命令；设置为yes，则当硬盘出现问题时，可以及时发现，避免数据的大量丢失；设置为no，则Redis无视bgsave的错误继续执行写命令，当对Redis服务器的系统(尤其是硬盘)使用了监控时，该选项考虑设置为no\n- rdbcompression yes：是否开启RDB文件压缩\n- rdbchecksum yes：是否开启RDB文件的校验，在写入文件和读取文件时都起作用；关闭checksum在写入文件和启动文件时大约能带来10%的性能提升，但是数据损坏时无法发现\n- dbfilename dump.rdb：RDB文件名\n- dir ./：RDB文件和AOF文件所在目录\n\n## 四、AOF持久化\n\nRDB持久化是将进程数据写入文件，而AOF持久化(即Append Only File持久化)，则是将Redis执行的每次写命令记录到单独的日志文件中（有点像MySQL的binlog）；当Redis重启时再次执行AOF文件中的命令来恢复数据。\n\n与RDB相比，AOF的实时性更好，因此已成为主流的持久化方案。\n\n### 开启AOF\n\nRedis服务器默认开启RDB，关闭AOF；要开启AOF，需要在配置文件中配置：\n\nappendonly yes\n\n### 执行流程\n\n由于需要记录Redis的每条写命令，因此AOF不需要触发，下面介绍AOF的执行流程。\n\nAOF的执行流程包括：\n\n- 命令追加(append)：将Redis的写命令追加到缓冲区aof_buf；\n- 文件写入(write)和文件同步(sync)：根据不同的同步策略将aof_buf中的内容同步到硬盘；\n- 文件重写(rewrite)：定期重写AOF文件，达到压缩的目的。\n\n1. 命令追加（append）\n\n   Redis先将写命令追加到缓冲区，而不是直接写入文件，主要是为了避免每次有写命令都直接写入硬盘，导致硬盘IO成为Redis负载的瓶颈。\n\n   命令追加的格式是Redis命令请求的协议格式，它是一种纯文本格式，具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点；具体格式略。在AOF文件中，除了用于指定数据库的select命令（如select 0 为选中0号数据库）是由Redis添加的，其他都是客户端发送来的写命令。\n\n2. 文件写入（write）和文件同步（sync）\n\n   Redis提供了多种AOF缓存区的同步文件策略，策略涉及到操作系统的write函数和fsync函数，说明如下：\n\n   为了提高文件写入效率，在现代操作系统中，当用户调用write函数将数据写入文件时，操作系统通常会将数据暂存到一个内存缓冲区里，当缓冲区被填满或超过了指定时限后，才真正将缓冲区的数据写入到硬盘里。这样的操作虽然提高了效率，但也带来了安全问题：如果计算机停机，内存缓冲区中的数据会丢失；因此系统同时提供了fsync、fdatasync等同步函数，可以强制操作系统立刻将缓冲区中的数据写入到硬盘里，从而确保数据的安全性。\n\n   AOF缓存区的同步文件策略由参数appendfsync\n\n   - always：命令写入aof_buf后立即调用系统fsync操作同步到AOF文件，fsync完成后线程返回。这种情况下，每次有写命令都要同步到AOF文件，硬盘IO成为性能瓶颈，Redis只能支持大约几百TPS写入，严重降低了Redis的性能；即便是使用固态硬盘（SSD），每秒大约也只能处理几万个命令，而且会大大降低SSD的寿命。\n   - no：命令写入aof_buf后调用系统write操作，不对AOF文件做fsync同步；同步由操作系统负责，通常同步周期为30秒。这种情况下，文件同步的时间不可控，且缓冲区中堆积的数据会很多，数据安全性无法保证。\n   - everysec：命令写入aof_buf后调用系统write操作，write完成后线程返回；fsync同步文件操作由专门的线程每秒调用一次。**everysec是前述两种策略的折中，是性能和数据安全性的平衡，因此是Redis的默认配置，也是我们推荐的配置。**\n\n3. 文件重写（rewrite）\n\n   随着时间流逝，Redis服务器执行的写命令越来越多，AOF文件也会越来越大；过大的AOF文件不仅会影响服务器的正常运行，也会导致数据恢复需要的时间过长。\n\n   文件重写是指定期重写AOF文件，减小AOF文件的体积。需要注意的是，**AOF重写是把Redis进程内的数据转化为写命令，同步到新的AOF文件；不会对旧的AOF文件进行任何读取、写入操作!**\n\n   关于文件重写需要注意的另一点是：对于AOF持久化来说，文件重写虽然是强烈推荐的，但并不是必须的；即使没有文件重写，数据也可以被持久化并在Redis启动的时候导入；因此在一些实现中，会关闭自动的文件重写，然后通过定时任务在每天的某一时刻定时执行。\n\n### 启动时加载\n\n前面提到过，当AOF开启时，Redis启动时会优先载入AOF文件来恢复数据；只有当AOF关闭时，才会载入RDB文件恢复数据。\n\n当AOF开启，但AOF文件不存在时，即使RDB文件存在也不会加载。\n\n1. 文件校验\n\n   与载入RDB文件类似，Redis载入AOF文件时，会对AOF文件进行校验，如果文件损坏，则日志中会打印错误，Redis启动失败。但如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整)，且aof-load-truncated参数开启，则日志中会输出警告，Redis忽略掉AOF文件的尾部，启动成功。aof-load-truncated参数默认是开启的：\n\n   ```bash\n   127.0.0.1:6379> config get aof-load-truncated\n   1) \"aof-load-truncated\"\n   2) \"yes\"\n   ```\n\n2. 伪客户端\n\n   因为Redis的命令只能在客户端上下文中执行，而载入AOF文件时命令是直接从文件中读取的，并不是由客户端发送；因此Redis服务器在载入AOF文件之前，会创建一个没有网络连接的客户端，之后用它来执行AOF文件中的命令，命令执行的效果与带网络连接的客户端完全一样。\n\n### AOF常用配置总结\n\n- appendonly no：是否开启AOF\n- appendfilename \"appendonly.aof\"：AOF文件名\n- dir ./：RDB文件和AOF文件所在目录\n- appendfsync everysec：fsync持久化策略\n- no-appendfsync-on-rewrite no：AOF重写期间是否禁止fsync；如果开启该选项，可以减轻文件重写时CPU和硬盘的负载（尤其是硬盘），但是可能会丢失AOF重写期间的数据；需要在负载和安全性之间进行平衡\n- auto-aof-rewrite-percentage 100：文件重写触发条件之一\n- auto-aof-rewrite-min-size 64mb：文件重写触发提交之一\n- aof-load-truncated yes：如果AOF文件结尾损坏，Redis启动时是否仍载入AOF文件\n\n## RDB与AOF的优缺点以及方案选择\n\n### RDB和AOF的优缺点\n\n**RDB持久化**\n\n优点：RDB文件紧凑，体积小，网络传输快，适合全量复制；恢复速度比AOF快很多。当然，与AOF相比，RDB最重要的优点之一是对性能的影响相对较小。\n\n缺点：RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化，而在数据越来越重要的今天，数据的大量丢失很多时候是无法接受的，因此AOF持久化成为主流。此外，RDB文件需要满足特定格式，兼容性差（如老版本的Redis不兼容新版本的RDB文件）。\n\n**AOF持久化**\n\n与RDB持久化相对应，AOF的优点在于支持秒级持久化、兼容性好，缺点是文件大、恢复速度慢、对性能影响大。\n\n### 持久化策略选择\n\n下面分场景来讨论持久化策略的选择，下面的讨论也只是作为参考，实际方案可能更复杂更具多样性。\n\n1. 如果Redis中的数据完全丢弃也没有关系（如Redis完全用作DB层数据的cache），那么无论是单机，还是主从架构，都可以不进行任何持久化。\n\n2. 在单机环境下（对于个人开发者，这种情况可能比较常见），如果可以接受十几分钟或更多的数据丢失，选择RDB对Redis的性能更加有利；如果只能接受秒级别的数据丢失，应该选择AOF。\n\n3. 但在多数情况下，我们都会配置主从环境，slave的存在既可以实现数据的热备，也可以进行读写分离分担Redis读请求，以及在master宕掉后继续提供服务。\n\n   在这种情况下，一种可行的做法是：\n\n   master：完全关闭持久化（包括RDB和AOF），这样可以让master的性能达到最好\n\n   slave：关闭RDB，开启AOF（如果对数据安全要求不高，开启RDB关闭AOF也可以），并定时对持久化文件进行备份（如备份到其他文件夹，并标记好备份的时间）；然后关闭AOF的自动重写，然后添加定时任务，在每天Redis闲时（如凌晨12点）调用bgrewriteaof。\n\n   这里需要解释一下，为什么开启了主从复制，可以实现数据的热备份，还需要设置持久化呢？因为在一些特殊情况下，主从复制仍然不足以保证数据的安全，例如：\n\n   - master和slave进程同时停止：考虑这样一种场景，如果master和slave在同一栋大楼或同一个机房，则一次停电事故就可能导致master和slave机器同时关机，Redis进程停止；如果没有持久化，则面临的是数据的完全丢失。\n   - master误重启：考虑这样一种场景，master服务因为故障宕掉了，如果系统中有自动拉起机制（即检测到服务停止后重启该服务）将master自动重启，由于没有持久化文件，那么master重启后数据是空的，slave同步数据也变成了空的；如果master和slave都没有持久化，同样会面临数据的完全丢失。需要注意的是，即便是使用了哨兵(关于哨兵后面会有文章介绍)进行自动的主从切换，也有可能在哨兵轮询到master之前，便被自动拉起机制重启了。因此，应尽量避免“自动拉起机制”和“不做持久化”同时出现。\n\n4. 异地灾备：上述讨论的几种持久化策略，针对的都是一般的系统故障，如进程异常退出、宕机、断电等，这些故障不会损坏硬盘。但是对于一些可能导致硬盘损坏的灾难情况，如火灾地震，就需要进行异地灾备。例如对于单机的情形，可以定时将RDB文件或重写后的AOF文件，通过scp拷贝到远程机器，如阿里云、AWS等；对于主从的情形，可以定时在master上执行bgsave，然后将RDB文件拷贝到远程机器，或者在slave上执行bgrewriteaof重写AOF文件后，将AOF文件拷贝到远程机器上。一般来说，由于RDB文件文件小、恢复快，因此灾难恢复常用RDB文件；异地备份的频率根据数据安全性的需要及其他条件来确定，但最好不要低于一天一次。\n\n**转载自：**[https://www.cnblogs.com/kismetv/p/9137897.html](https://www.cnblogs.com/kismetv/p/9137897.html)","categoryId":2,"viewCount":961,"categoryName":"中间件","author":"球接子","authorAvatar":null,"tagIds":[1,14,17],"tagNames":["Redis","中间件","数据库"]}}