Redis 备份、恢复、读写分离

Redis数据备份与恢复
Redis所有数据都是保存在内存中。Redis数据备份可以定期地通过异步方式保存到磁盘上,该方式为半持久化模式,如果每一次数据变化都写入AOF文件里面。则称为全持久化模式,同时可以基于Redis主从复制实现Redis备份与恢复。
 
一、半持久化RDB模式
半持久化RDB模式也是Redis备份默认方式,是通过快照(snapshottint)完成的,当满足在redis.conf配置文件中设置的条件时,Redis会自动将内存中的所有数据进行快照并存储在硬盘上,完成数据备份。
Redis进行RDB快照的条件由用户配置文件中自定义,由两个参数构成:时间和改动的键的个数。当在指定的时间内被更改的键的个数大于指定的数值时就会进行快照。在配置文件中已经预置了以下3个条件。
save 9001        900s内有至少1个键被更改则进行快照
save 30010        300s内至少10个键被更改则进行快照
save 6010000        60s内至少10000个键被更改则进行快照
默认可以存在多个条件,条件之间是或的关系,只要满足其中一个条件,就会进行快照。如果想禁用自动快照,只需要将所有的save参数删除即可。Redis默认会将快照文件存储在Redis数据目录,默认文件名为dump.rdb文件,可以通过配置dir和dbfilename两个参数分别指定快照文件的存储路径和文件名。也可以在Redis命令行执行configget dir 获取Redis数据保存路径。
RDB的优点是:
1.RDB是一个非常紧凑(compact)的文件,它保存了redis在某个时间点上的数据集。这种文件非常适合用于进行备份
和灾难恢复。
2.生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
3.RDB 在恢复大数据集时的速度比 AOF的恢复速度要快。
RDB缺点:
1.如果你需要尽量避免在服务器故障时丢失数据,那么RDB不适合你。 虽然Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态,所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
2.每次保存 RDB 的时候,Redis 都要fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端;如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF重写的执行间隔有多长,数据的耐久性都不会有任何损失。
 
二、全持久化AOF模式
如果数据很重要无法承受任何损失,可以考虑使用AOF方式进行持久化,默认Redis没有开启AOF方式的全持久化模式。
在启动时Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入速度相较RDB会慢一些,开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。默认的文件名为appendonly.aof,可以通过appendfilename参数修改该名称。
Redis允许同时开启AOF和RDB,既保证了数据安全又使得进行备份等操作十分容易。此时重新启动Redis后Redis会使用AOF文件来恢复数据,因为AOF方式的持久化可能丢失的数据更少,可以在redis.conf中通过appendonly参数开启RedisAOF全持久化模式,代码如下:
appendonlyyes                                        开启AOF持久化功能
appendfilename"appendonly.aof"                AOF持久化保存文件名
auto-aof-rewrite-percentage100                        当AOF文件大小超过上一次重写时AOF文件大小的百分比时会再次进行重写
auto-aof-rewrite-min-size64MB                        允许重写的最小AOF文件大小配置写入AOF文件后,要求系统刷新硬盘缓存的机制
appendfsyncalways                                每次执行写入都会执行同步,最安全也最慢
# appendfsynceverysec                                每秒执行一次同步操作
# appendfsyncno                                不主动进行同步操作,完全交由操作系统来做,每30s一次,最快也最不安全
AOF优点
使用 AOF 持久化会让 Redis变得非常耐久:你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync会在后台线程执行,所以主线程可以继续努力地处理命令请求)。
AOF文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek ,即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。
Redis 可以在 AOF文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
AOF文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到FLUSHALL 执行之前的状态。
AOF缺点
对于相同的数据集来说,AOF文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快,即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
AOF 在过去曾经发生过这样的 bug :因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug。) 测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集,并通过重新载入这些数据来确保一切正常。虽然这种 bug 在 AOF文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。
 
Redis主从复制备份
和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。
 
一、全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
-  从服务器连接主服务器,发送SYNC命令;
- 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
-  从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
-  主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
-  从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
 
二、增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
 
三、Redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
 
四、注意点
如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致MasterIO剧增宕机。
Redis主从复制的配置十分简单,它可以使从服务器是主服务器的完全拷贝。需要清除Redis主从复制的几点重要内容:
1、Redis使用异步复制。但从Redis2.8开始,从服务器会周期性的应答从复制流中处理的数据量。
2、一个主服务器可以有多个从服务器。
3、从服务器也可以接受其他从服务器的连接。除了多个从服务器连接到一个主服务器之外,多个从服务器也可以连接到一个从服务器上,形成一个图状结构。
4、Redis主从复制不阻塞主服务器端。也就是说当若干个从服务器在进行初始同步时,主服务器仍然可以处理请求。
5、主从复制也不阻塞从服务器端。当从服务器进行初始同步时,它使用旧版本的数据来应对查询请求,假设你在redis.conf配置文件是这么配置的。否则的话,你可以配置当复制流关闭时让从服务器给客户端返回一个错误。但是,当初始同步完成后,需要删除旧的数据集和加载新的数据集,在这个短暂的时间内,从服务器会阻塞连接进来的请求。
6、主从复制可以用来增强扩展性,使用多个从服务器来处理只读的请求(比如,繁重的排序操作可以放到从服务器去做),也可以简单的用来做数据冗余。
7、使用主从复制可以为主服务器免除把数据写入磁盘的消耗:在主服务器的redis.conf文件中配置“避免保存”(注释掉所有“保存“命令),然后连接一个配置为“进行保存”的从服务器即可。但是这个配置要确保主服务器不会自动重启。
 
五、Redis大概主从同步是怎么实现的?
1、全量同步:
master服务器会开启一个后台进程用于将redis中的数据生成一个rdb文件,与此同时,服务器会缓存所有接收到的来自客户端的写命令(包含增、删、改),当后台保存进程处理完毕后,会将该rdb文件传递给slave服务器,而slave服务器会将rdb文件保存在磁盘并通过读取该文件将数据加载到内存,在此之后master服务器会将在此期间缓存的命令通过redis传输协议发送给slave服务器,然后slave服务器将这些命令依次作用于自己本地的数据集上最终达到数据的一致性。
2、部分同步:
从redis2.8版本以前,并不支持部分同步,当主从服务器之间的连接断掉之后,master服务器和slave服务器之间都是进行全量数据同步,但是从redis2.8开始,即使主从连接中途断掉,也不需要进行全量同步,因为从这个版本开始融入了部分同步的概念。部分同步的实现依赖于在master服务器内存中给每个slave服务器维护了一份同步日志和同步标识,每个slave服务器在跟master服务器进行同步时都会携带自己的同步标识和上次同步的最后位置。当主从连接断掉之后,slave服务器隔断时间(默认1s)主动尝试和master服务器进行连接,如果从服务器携带的偏移量标识还在master服务器上的同步备份日志中,那么就从slave发送的偏移量开始继续上次的同步操作,如果slave发送的偏移量已经不再master的同步备份日志中(可能由于主从之间断掉的时间比较长或者在断掉的短暂时间内master服务器接收到大量的写操作),则必须进行一次全量更新。在部分同步过程中,master会将本地记录的同步备份日志中记录的指令依次发送给slave服务器从而达到数据一致。
 
六、主从同步中需要注意几个问题
1、在上面的全量同步过程中,master会将数据保存在rdb文件中然后发送给slave服务器,但是如果master上的磁盘空间有效怎么办呢?那么此时全部同步对于master来说将是一份十分有压力的操作了。此时可以通过无盘复制来达到目的,由master直接开启一个socket将rdb文件发送给slave服务器。(无盘复制一般应用在磁盘空间有限但是网络状态良好的情况下)
2、主从复制结构,一般slave服务器不能进行写操作,但是这不是死的,之所以这样是为了更容易的保证主和各个从之间数据的一致性,如果slave服务器上数据进行了修改,那么要保证所有主从服务器都能一致,可能在结构上和处理逻辑上更为负责。不过你也可以通过配置文件让从服务器支持写操作。(不过所带来的影响还得自己承担哦。。。)
3、主从服务器之间会定期进行通话,但是如果master上设置了密码,那么如果不给slave设置密码就会导致slave不能跟master进行任何操作,所以如果你的master服务器上有密码,那么也给slave相应的设置一下密码吧(通过设置配置文件中的masterauth);
4、关于slave服务器上过期键的处理,由master服务器负责键的过期删除处理,然后将相关删除命令已数据同步的方式同步给slave服务器,slave服务器根据删除命令删除本地的key。
 
七、当主服务器不进行持久化时复制的安全性
在进行主从复制设置时,强烈建议在主服务器上开启持久化,当不能这么做时,比如考虑到延迟的问题,应该将实例配置为避免自动重启。为什么不持久化的主服务器自动重启非常危险呢?为了更好的理解这个问题,看下面这个失败的例子,其中主服务器和从服务器中数据库都被删除了。
设置节点A为主服务器,关闭持久化,节点B和C从节点A复制数据。这时出现了一个崩溃,但Redis具有自动重启系统,重启了进程,因为关闭了持久化,节点重启后只有一个空的数据集。节点B和C从节点A进行复制,现在节点A是空的,所以节点B和C上的复制数据也会被删除。当在高可用系统中使用RedisSentinel,关闭了主服务器的持久化,并且允许自动重启,这种情况是很危险的。比如主服务器可能在很短的时间就完成了重启,以至于Sentinel都无法检测到这次失败,那么上面说的这种失败的情况就发生了。如果数据比较重要,并且在使用主从复制时关闭了主服务器持久化功能的场景中,都应该禁止实例自动重启。
 
八、Redis主从复制是如何工作的
如果设置了一个从服务器,在连接时它发送了一个SYNC命令,不管它是第一次连接还是再次连接都没有关系。然后主服务器开始后台存储,并且开始缓存新连接进来的修改数据的命令。当后台存储完成后,主服务器把数据文件发送到从服务器,从服务器将其保存在磁盘上,然后加载到内存中。然后主服务器把刚才缓存的命令发送到从服务器。这是作为命令流来完成的,并且和Redis协议本身格式相同。你可以通过telnet自己尝试一下。在Redis服务器工作时连接到Redis端口,发送SYNC命令,会看到一个批量的传输,并且主服务器接收的每一个命令都会通过telnet会话重新发送一遍。当主从服务器之间的连接由于某些原因断开时,从服务器可以自动进行重连接。当有多个从服务器同时请求同步时,主服务器只进行一个后台存储。当连接断开又重新连上之后,一般都会进行一个完整的重新同步,但是从Redis2.8开始,只重新同步一部分也可以。
 
九、配置
主从复制的配置十分简单:把下面这行加入到从服务器的配置文件中即可。
slaveof 192.168.1.16379
当然你需要把其中的192.168.1.16379替换为你自己的主服务器IP(或者主机名hostname)和端口。另外你可以调用SLAVEOF命令,
主服务器就会开始与从服务器同步。
关于部分重新同步,还有一些针对复制内存缓冲区的优化参数。查看Redis介质中的Redis.conf示例获得更多信息。
使用repl-diskless-sync配置参数来启动无磁盘复制。使用repl-diskless-sync-delay参数来配置传输开始的延迟时间,以便等待
更多的从服务器连接上来。查看Redis介质中的Redis.conf示例获得更多信息。
 
十、只读从服务器
从Redis2.6开始,从服务器支持只读模式,并且是默认模式。这个行为是由Redis.conf文件中的slave-read-only 参数控制的,
可以在运行中通过CONFIGSET来启用或者禁用。
只读的从服务器会拒绝所有写命令,所以对从服务器不会有误写操作。但这不表示可以把从服务器实例暴露在危险的网络环境下,
因为像DEBUG或者CONFIG这样的管理命令还是可以运行的。不过你可以通过使用rename-command命令来为这些命令改名来增加安全性。
你可能想知道为什么只读限制还可以被还原,使得从服务器还可以进行写操作。虽然当主从服务器进行重新同步或者从服务器重启后,
这些写操作都会失效,还是有一些使用场景会想从服务器中写入临时数据的,但将来这个特性可能会被去掉。
 
十一、限制有N个以上从服务器才允许写入
从Redis2.8版本开始,可以配置主服务器连接N个以上从服务器才允许对主服务器进行写操作。但是,因为Redis使用的是异步主从复制,
没办法确保从服务器确实收到了要写入的数据,所以还是有一定的数据丢失的可能性。
这一特性的工作原理如下:
1、从服务器每秒钟ping一次主服务器,确认处理的复制流数量。
2、主服务器记住每个从服务器最近一次ping的时间。
3、用户可以配置最少要有N个服务器有小于M秒的确认延迟。
4、如果有N个以上从服务器,并且确认延迟小于M秒,主服务器接受写操作。
还可以把这看做是CAP原则(一致性,可用性,分区容错性)不严格的一致性实现,虽然不能百分百确保一致性,但至少保证了丢失的数据不会超过M秒内的数据量。
如果条件不满足,主服务器会拒绝写操作并返回一个错误。
min-slaves-to-write(最小从服务器数)
min-slaves-max-lag(从服务器最大确认延迟)
 
LAMP企业架构读写分离
MySQL读写分离的原理:让master数据库处理事务增加、修改、删除、更新操作(create、insert、delete、update),而让slave数据库处理select操作,MySQL读写分离前提是基于MySQL主从复制,这样可以保证在master上修改数据,slave同步之后,Web应用可以读取到slave端的数据。
实现MySQL读写分离可以基于第三方插件,也可以通过开发修改代码实现,常见读写分离的方式有四种:
MySQLproxy读写分离;Amoeba读写分离;Mycat读写分离;基于程序读写分离(效率很高,但实施难度大,需开发改代码)
     Amoeba是以MySQL为底层数据存储,并对WEB、APP应用提供MySQL协议接口的proxy。它集中地响应WEB应用的请求,依据用户事先设置的规则,将SQL请求发送到特定的数据库上执行,基于此可以实现负载均衡、读写分离、高可用性等需求。
     Amoeba相当于一个SQL请求的路由器,目的是为负载均衡、读写分离、高可用性提供机制,而不是完全实现它们。用户需要结合使用MySQL的Replication等机制来实现副本同步等功能。
     Mysql-Proxy是MySQL官方提供的mysql中间件服务,支持无数客户端连接,同时后端可连接若干台Mysql-Server服务器,MYSQL-Proxy自身基于MySQL协议,连接MYSQL-Proxy的客户端无需修改任何设置,跟正常连接MYSQL Server没有区别,无需修改程序代码。
MySQLProxy是App应用(客户端)与MYSQL Server之间的一个连接代理,MySQLProxy负责将APP应用的SQL请求根据转发规则,转发至相应的后端数据库,基于lua脚本,可以实现复杂的连接控制和过滤,从而实现数据读写分离和负载均衡的需求。
     Mysql-Proxy允许用户指定Lua脚本对SQL请求进行拦截,对请求进行分析与修改,还允许用户指定Lua脚本对服务器的返回结果进行修改,加入一些结果集或者去除一些结果集,对SQL的请求通常为读请求、写请求,基于Lua脚本,可以实现将SQL读请求转发至后端Slave服务器,将SQL写请求转发至后端Master服务器。
构建Mysql读写分离架构首先需要将两台MYSQL服务器配置为主(192.168.1.14)从(192.168.1.15)复制,配置192.168.1.16服务器上安装Mysql-Proxy服务即可,配置步骤如下:
1、下载MYSQL-Proxy软件版本,解压并重命名至/usr/local/mysql-proxy,命令如下:
wget http://ftp.ntu.edu.tw/pub/MySQL/Downloads/MySQL-Proxy/mysql-proxy-0.8.4-linux-el6-x86-64bit.tar.gz
useradd  -r mysql-proxy
tar  zxvf mysql-proxy-0.8.4-linux-el6-x86-64bit.tar.gz  -C /usr/local
mv /usr/local/mysql-proxy-0.8.4-linux-el6-x86-64bit  /usr/local/mysql-proxy
2、环境变量配置文件/etc/profile中加入如下代码保存退出,然后执行source/etc/profile使环境变量配置生效即可:
export  PATH=$PATH:/usr/local/mysql-proxy/bin/
3、启动MYSQL-Proxy中间件,命令如下:
mysql-proxy --daemon--log-level=debug --user=mysql-proxy --keepalive--log-file=/var/log/mysql-proxy.log --plugins="proxy"--proxy-backend-addresses="192.168.1.14:3306"--proxy-read-only-backend-addresses="192.168.1.15:3306"--proxy-lua-script="/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua"--plugins=admin --admin-username="admin"--admin-password="admin"--admin-lua-script="/usr/local/mysql-proxy/lib/mysql-proxy/lua/admin.lua"
4、Mysql-Proxy的相关参数详解如下:
--help-all                                                               :获取全部帮助信息;
--proxy-address=host:port                                     :代理服务监听的地址和端口,默认为4040;
--admin-address=host:port                                :管理模块监听的地址和端口,默认为4041;
--proxy-backend-addresses=host:port                 :后端mysql服务器的地址和端口;
--proxy-read-only-backend-addresses=host:port    :后端只读mysql服务器的地址和端口;
--proxy-lua-script=file_name                            :完成mysql代理功能的Lua脚本;
--daemon                                                          :以守护进程模式启动mysql-proxy;
--keepalive                                                          :在mysql-proxy崩溃时尝试重启之;
--log-file=/path/to/log_file_name                       :日志文件名称;
--log-level=level                                                    :日志级别;
--log-use-syslog                                                    :基于syslog记录日志;
--plugins=plugin                                                   :在mysql-proxy启动时加载的插件;
--user=user_name                                                 :运行mysql-proxy进程的用户;
--defaults-file=/path/to/conf_file_name                :默认使用的配置文件路径,其配置段使用[mysql-proxy]标识;
--proxy-skip-profiling                                            :禁用profile;
--pid-file=/path/to/pid_file_name                           :进程文件名;
 5、MYSQL-Proxy启动后,在服务器端查看端口,其中4040为proxy代理端口用于WEB应用连接,4041为管理端口用于SA或者DBA管理。
netstat -ntpl |grepmysql-proxy
6、基于4041端口MySQL-Proxy查看读写分离状态,登录4041管理端口,命令如下:
mysql  -h192.168.1.16  -uadmin -p  -P 4041
7、以4041管理口登录,然后执行select命令,state均为up状态,type类型为rw、ro,则证明读写分离状态成功。如果状态为unknown未知状态,可以4040端口登录执行:show  databases;命令,直到state变成up状态为止。
select  * from  backends;
8、读写分离数据测试,以3306端口登录到从库,进行数据写入和测试,在从库上创建tang_test测试库,并写入内容:
mysql -uadmin -p-h192.168.1.15 -P 3306
create databasetang_test;
use tang_test;
create table t1 (idchar(20),name char(20));
insert into t1 values(01,'tang.net');
insert into t1 values(02,'tang.com');
9、读写分离数据测试,以4040代理端口登录,执行如下命令,可以查看到数据即证明读写分离成功。
mysql-h192.168.1.16  -uroot -p123456  -P4040 -e "select  *  from tang_test.t1;"
10、登录ApacheWEB服务器,修改DiscuzPHP网站发布目录下config/config_global.php全局配置文件,查找dbhost段,将192.168.1.16改成192.168.1.16:4040。