Redis是一种远程内存数据库,主要用C语言编写完成,它也是基于键值对(key-value)的NoSQL数据库,提供了由String(字符串)、hash(哈希)、list(链表)、set(集合)、zset(有序集合)等多种数据机构和算法组成的数据类型,大部分的运算都在内存中完成,这也是它读写性能强劲的主要原因,官方给出的读写性能可以达到10万/秒。Redis在热点数据的保存、session缓存、计数、社交网络、消息队列等场景下发挥了出色的作用,但是大部分NoSQL产品都存在数据不一致的问题,redis也不例外,那么redis 在数据安全方面做了哪些优化和改进呢?
持久化问题
Redis提供了RDB和AOF两种持久化机制,通过这两种方式可以将数据库的数据或者执行的命令持久化到磁盘上,当redis服务器进程重启后可以重新加载数据到内存中。RDB文件保存的路径和文件名分别由dir和dbfilename参数指定。下面我们分别对这两种持久化机制进行介绍。
RDB
RDB持久化又叫做快照持久化,它会把当前的数据库生成快照保存到磁盘中,可以通过命令(save,bgsave)手动触发,也可以根据配置文件中save选项的配置自动触发。
save:
命令演示:
代码语言:javascript复制127.0.0.1:6379> saveOK
save命令会阻塞当前的数据库,直到RDB过程完成为止,如果数据库较大,备份时间过长的话会造成长时间阻塞,基于此种情况,save命令也基本废弃了,不建议线上使用。
bgsave:
命令演示:
代码语言:javascript复制127.0.0.1:6379> bgsaveBackground saving started
bgsave会fork出一个子进程来创建RDB 文件,主进程可以继续处理命令请求,这样就不存在阻塞主进程的情况了。
自动触发机制:
RDB持久化自动触发的参数是由SNAPSHOTTING模块中的save <seconds> <changes>参数控制,默认配置如下:
代码语言:javascript复制save 900 1save 300 10save 60 10000
save 900,代表 900 秒(15 分钟)内至少有一个 key被改变,就把数据持久化到磁盘上。
save 300 1,代表300 秒(分钟)内至少有300 key被改变,就把数据持久化到磁盘上。
save 60 10000,代表60秒(1分钟)内至少有10000 key被改变,就把数据持久化到磁盘上。
RDB持久化给数据安全提供了保障,利用RDB文件恢复数据也比较快,但是由于每次执行bgsave命令都需要fork出一个子进程,频繁执行成本较高,无法进行实时持久化,因此redis又提供了AOF持久化的机制来解决这个问题。
AOF
AOF(append only file)持久化是通过保存redis服务器所执行的写命令来记录数据库状态的,如果你了解MySQL,你就会觉得它很像MySQL的binlog。AOF很好的解决了数据持久化的实时性问题,这也是现阶段比较主流的持久化方案。
AOF持久化方案主要通过以下几个参数控制,其他参数请参考文章《别找了,你要的Redis命令都在这了》
1.appendonly yes
表示是否开启AOF持久化方式。
2. appendfilename appendonly.aof
指定更新日志文件名,默认为appendonly.aof
3. appendfsync everysec
指定日志刷到磁盘的机制,共有3个可选值
no:由操作系统自行决定数据缓存刷到磁盘,相对较快,但是数据一致性无法保证,不建议配置。
always:每次更新操作后手动调用fsync()将数据写到磁盘,数据最安全,但是性能相对较低,会严重降低redis的速度。
everysec:每秒刷新到磁盘一次,兼顾了性能和数据安全,建议配置。
AOF重写机制
随着redis接收的写命令越来越多,AOF 文件也会越来越大,为此,redis引入了重写机制。Redis服务器会移除aof文件中的冗余命令,并且对单条的写命令进行合并,比如lpush list a、lpush list b、lpush list c会合并成lpush list a b c命令,重新生成一个新的aof文件,这样就可以减小aof文件的体积,可以被redis更快的加载。但是重写也需要创建子进程,创建子进程所带来的问题重写时依然存在,所以我们要控制重写的频率。
AOF重写也可以通过手动触发和自动触发两种方式。
手动触发
bgrewriteaof:通过调用bgrewriteaof命令直接触发
代码语言:javascript复制127.0.0.1:6379> bgrewriteaofBackground append only file rewriting started
自动触发
由以下两个参数控制:
auto-aof-rewrite-percentage 100#表示当前aof文件大小与上一次重写后文件大小的比值。
auto-aof-rewrite-min-size 64mb #表示进行aof重写时的最小体积,默认64M。
按照上面的配置,当aof文件达到64M,并且aof 文件的体积比上次重写后的体积大了一倍(100%)时,redis会执行bgrewriteaof命令进行重写。
上面我们说了RDB和AOF两种持久化方式,那么redis加载的时候是怎么加载的呢?下面我画个流程图进行演示:
1.如果开启了AOF,会优先加载AOF文件。
2.当关闭AOF时,会加载RDB文件,当RDB也不存在时,redis可以启动成功,但是数据库是空的。
3.当AOF和RDB文件存在错误时,Redis启动失败,需要进行修复。Redis提供了redis-check-rdb和redis-check-aof两个命令来对AOF和RDB文件进行检测。
复制
Redis提供了两种持久化机制,通过数据的持久化就能把数据永远的存储在磁盘上,但是这样依然存在单机风险,当遇到系统宕机无法启动或者存储异常时,数据还是会丢失。Redis和MySQL一样,同样提供了主从复制技术来实现数据的实时备份和传输。
Redis的复制可以通过配置文件配置,也可以动态的维护。配置文件REPLICATION模块中的slaveof <masterip> <masterport>参数可以配置主库的IP和端口,这样redis启动后会自动连接主库进行复制。如果配置文件中没有配置,还可以在命令行执行slaveof 192.168.1.1 6379来动态的指定主库进行复制。
复制流程图如下:
执行完slave {master IP}{master Port}之后,从库先保存主库的信息。
代码语言:javascript复制127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
………………………………
- 主从服务器之间建立socket链接,用于主库向从库传输数据和复制命令。
- 从节点向主节点发送ping命令,用于检测建链是否可用。正常情况下主库接到ping命令后会回复pong命令,如果从库收不到主库的pong命令会进行断链重连。
- 如果主节点设置了requirepass参数,则要进行密码验证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证。
- 紧接着就要开始传输数据了。Redis的主从数据同步分两种,全量复制和部分复制。 首先从库执行psync {runId} {offset}命令,将自己的runId(运行Id,从节点保存的主节点的runId,如果没有配置上送 ”?”)和offset(复制偏移量,写命令的字节长度累加值,主从节点都会记录,从节点拿着自己的偏移量和主库对比,来判断主从数据是否一致以及从库缺少哪些数据,第一次复制时上送 -1)发送给主库。 当从节点没有主节点的runId和复制偏移量时上送psync ? -1,此时主节点会在本地执行bgsave命令产生RDB快照信息,发送给从节点,这就是全量复制。还有一种情况是slave上送的runid并不是当前主库的runid时,说明slave之前的主库并不是当前主库,这种情况需要进行全量复制。 部分复制是redis针对全量复制资源消耗过高而做出的一种优化措施,当网络出现闪断的时候,从节点直接向主节点申请补发丢失的数据即可,而不需要在进行全量复制,主节点直接从复制积压缓冲区内将这部分数据直接发给从节点,需要补发哪些数据就是由主节点的复制偏移量和从节点的偏移量的差值决定的。比如下图的例子,slave3网络出现异常,导致主库新更新的10个字节的命令没有接收到,当网络恢复时,它会把offset=1000送给主库,这时候主库就知道slave3缺少哪些数据了。如果主库发现从库需要的数据已经不再复制积压缓冲区了,那么就要执行全部复制了。
补充下,复制积压缓存区是保存在主节点上的一个固定长度的队列,默认大小为1M,当主从建立链接时,主节点在响应写命令时,不仅会把命令发给从节点,还会写入到复制积压缓冲区。
代码语言:javascript复制127.0.0.1:6380> info replication
# Replication
role:slave
……………………………………..
repl_backlog_active:1 #开启复制积压缓冲区
repl_backlog_size:1048576 #缓冲区大小
repl_backlog_first_byte_offset:1 #起始偏移量
repl_backlog_histlen:6591#以保存的数据长度
6.从库接收到主库发过来的数据后就会应用这些数据。
7.当完成同步之后,主服务器就进入了命令传播阶段。主库会持续将自己的执行的写命令发送给从库,从库一直接受并执行主库发过来的命令,这样主从之间就实现数据的一致性了。
往期推荐
【redis】部署及参数详解(吐血整理,建议收藏)
别找了,你要的Redis命令都在这了
MySQL是怎么读数据的——多版本并发控制