持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。

通常 Redis 将数据存储在内存中或虚拟内存中,它是通过以下两种方式实现对数据的持久化。
大型互联网公司,比如京东,淘宝对缓存依赖是非常高的,如果缓存不做持久化的话,万一缓存挂了,下次重启缓存里面是没有任何数据的,意味着你再重启,所以的请求都会穿透redis打到后端服务器(后端直接访问MySQL).那就相当于缓存都没有用了,MySQL会支撑不了我们整个大规模的并发请求.那么很有可能就会导致我们的缓存雪崩,那么整个网站就会宕机.
所以大多数场景redis需要持久化的.
缓存在互联网公司主要是抗并发的,让我们大量请求流量在缓存这一层就直接返回去了而不要到数据库这一层.redis不要当数据库来用.

实现:

单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。

两种方式

  • RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
  • AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

RDB快照模式

RDB:是Redis DataBase缩写快照

这种方式就是将内存中数据以快照的方式写入到二进制文件中 ,默认的文件名为dump.rdb。

客户端也可以使用 save 或者 bgsave 命令通知 redis 做一次快照持久化。save 操作是在主线程中保存快照的,由于 redis 是用一个主线程来处理所有客户端的请求,这种方式会阻塞所有客户端请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步增量数据。如果数据量大的话,写操作会比较多,必然会引起大量的磁盘 IO 操作,可能会严重影响性能。
注意:由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外宕机的话,就会丢失最后一次快照后的所有数据修改。

在默认情况下,Redis将内存数据库快照保存在名字为 dump.rdb 的二进制文件中(做了压缩了)。这个需要在redis.conf那里配置一下.
image.png

优点


1、只有一个文件 dump.rdb,方便持久化。
2、容灾性好,一个文件可以保存到安全的磁盘。
3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
4.相对于数据集大时,比 AOF 的启动效率更高,当实例宕机恢复时,加载RDB文件的速度很快,能够在很短时间内迅速恢复文件中的数据
5.RDB文件数据是被压缩写入的,因此RDB文件的体积要比整个实例内存要小

缺点

  • 由于是某一时刻的数据快照,因此它的数据并不全
  • 生成RDB文件的代价是比较大的,它会消耗大量的CPU和内存资源

    使用场景

  • 主从全量同步数据

  • 数据库备份
  • 对于丢失数据不敏感的业务场景,实例宕机后快速恢复数据

由此可以看出,RDB非常适合做数据备份,我们可以定时让Redis生成RDB文件,然后备份这个快照文件即可。

定时生成RDB

  1. # 最近15分钟内 至少产生1次写入
  2. save 900 1
  3. # 最近5分钟内 至少产生10次写入
  4. save 300 10
  5. # 最近1分钟内 至少产生10000次写入
  6. save 60 10000

如果达到以上任意条件,则Redis会自动生成新的RDB文件,降低RDB数据内容与实例数据的差异。

Copy On Write

Redis上执行save和bgsave命令都可以生成RDB文件,但前者是在前台执行的,也就是说在生成RDB文件时,会阻塞整个实例,在RDB未生成之前,任何请求都是无法处理的,对于内存很大的实例,生成RDB文件非常耗时,显然这是我们不能接受的。
所以通常我们会选择执行bgsave让Redis在后台生成RDB文件,这样Redis依旧可以处理客户端请求,不会阻塞整个实例。
但不是说后台生成RDB就是没有代价的,Redis为了实现后台把内存数据的快照写入文件,采用了操作系统提供的Copy On Write技术,也就是我们熟知的fork系统调用。
fork系统调用会产生一个子进程,它与父进程共享相同的内存地址空间,这样子进程在这一时刻就能拥有与父进程的相同的内存数据。
虽然子进程与父进程共享同一块内存地址空间,但在fork子进程时,操作系统需要拷贝父进程的内存页表给子进程,如果整个Redis实例内存占用很大,那么它的内存页表也会很大,在拷贝时就会比较耗时,同时这个过程会消耗大量的CPU资源。在完成拷贝之前父进程也处于阻塞状态,无法处理客户端请求。
fork执行完之后,子进程就可以扫描自身所有的内存数据,然后把全部数据写入到RDB文件中。
之后父进程依旧处理客户端的请求,当在处理写命令时,父进程会重新分配新的内存地址空间,从操作系统申请新的内存使用,不再与子进程共享,这个过程就是Copy On Write(写实复制)名字的由来。这样父子进程的内存就会逐渐分离,父进程申请新的内存空间并更改内存数据,子进程的内存数据不受影响。
由此可以看出,在生成RDB文件时,不仅消耗CPU资源,还有需要占用最多一倍的内存空间。《Redis 开发陷阱及避坑指南》这篇推荐看下。
我们在Redis执行info命令,可以看到fork子进程的耗时,可以通过这个耗时来评估fork时间是否符合预期。同时我们应该保证Redis机器拥有足够的CPU和内存资源,并合理设置生成RDB的时机。另外,关注公众号Java技术栈回复redis可以获取系列Redis教程。

配置


dbfilename “dump.rbd” 生成的名字
dir 是配置生成的具体的位置的.

SNAPSHTTING 生成快照策略
save 60 1000 #比如说,以下设置会让Redis在满足“60秒内有至少有1000个键被改动”这一条件时,自动保存一次数据集,注意,是每执行一次 set命令会累加一次.执行get命令是不累加的.
前面60是时间,右面1000是在这个时间段改动的次数.

save 900 1 # 是900秒内有一次改动就帮你生成一次快照.
save 300 10 # 是300秒内有10次改动就帮你生成一次快照 .
当然这个可以配置多个的,只要成立了就会帮你生成一次快照.

假如save 300 10 #是300秒内有10次改动就会生成一次快照,但是有9次改动后服务器宕机了,此时那9条数据会丢失.
如果不想使用RDB快照策略就把这些save 900 1 等等的快照生成策略全部注释掉

AOF日志追加模式

AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录 到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

在RDB快照模式情况下 save 300 10 是300秒内有10次改动就会生成一次快照,如果我在300秒内只有9次改动的时候忽然服务器宕机了,那么我还没开始生成快照,我的内存里面的9条数据就会全部丢失了.
这就是RDB的问题.

AOF可以到指令级别,你每修改一条指令,我都可以把内存的数据马上存到持久化文件里面去.AOF基本上可以完全不丢失数据,Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog.

这种方式 redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认appendonly.aof)。当 redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于操作系统会在内核中缓存 write 做的修改,所以可能不是立即写到磁盘上。这样的持久化还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis 我们想要通过 fsync 函数强制操作系统写入到磁盘的时机。有三种方式如下(默认是:每秒 fsync 一次)

AOF文件不是二进制的乱码,你可以直接打开查看里面的内容.

image.png

配置

你可以通过修改redis.conf配置文件来打开 AOF 功能:
appendonly yes #改成yes就启动了AOF持久化方式.
#当然如果RDB方式也可以一起开启
#这样的话,两种持久化方式它都会去执行.
appendfilename # 指定生成AOF持久化文件的路径

刷盘方 式

AOF可以配置几种策略的,注意三种策略只能有一次存在:

appendfsync always:每次有新命令追加到AOF文件时就执行一次fsync,强制写入到磁盘,性能非常慢,数据非常安全。不推荐使用.

appendfsync everysec:每秒fsync一次,强制写入到磁盘,足够快(和使用RDB持久化差不多),并且在故障时只会丢失1秒钟的数据。(redis官方推荐这种,对性能影响不是特别大.)

appendfsync no:从不fsync,将数据交给操作系统来处理,完全依赖操作系统,。更快,性能最好,持久化没保证,也更不安全的选择。

以上可以看出AOF相对于RDB的优点是,AOF数据文件更新比较及时,比RDB保存更完整的数据,这样在数据恢复时能够恢复尽量完整的数据,降低丢失数据的风险。
如果同时存在RDB文件和AOF文件,Redis会优先使用AOF文件进行数据恢复。
但它的缺点也很易见:

  • 随着时间增长,AOF文件会越来越大
  • AOF文件刷盘会增加磁盘IO的负担,可能影响Redis的性能(开启每秒刷盘时)

AOF重写

Redis提供了AOF瘦身的功能,可以设置在AOF文件很大时,自动触发AOF重写,Redis会扫描整个实例的数据,重新生成一个AOF文件达成瘦身的效果。但这个重写过程也需要消耗大量的CPU资源。


如下两个配置可以控制AOF自动重写频率:

auto-aof-rewrite-min-size 64mb : aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大.
auto-aof-rewrite-percentage 100 : aof文件自上一次重写后文件大小增长了100%则再次触发重,比如说原来文件有100mb,当你变成200mb的时候会触发AOF重写


重写当然AOF还可以手动重写,进入redis客户端执行命令bgrewriteaof重写AOF,
注意,AOF重写redis会fork出一个子进程去做,不会对redis正常命令处理有太多影响.

性能影响

如果AOF的刷盘时机设置为每次写入都刷盘,那么会大大降低Redis的写入性能,因为每次写命令都需要写入文件并刷到磁盘中才会返回,当写入量很大时,会增加磁盘IO的负担。
性能与数据安全不能兼得,虽然Redis提供了实时刷盘的机制,但是在真正场景中使用的不多。
通常我们会选择每秒刷盘这种方式,既能保证良好的写入性能,在实例宕机时最多丢失1秒的数据,做到性能和安全的平衡。

优点

1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。
2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))

缺点

1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集大的时候,比 rdb 启动效率低。

使用场景

由于AOF可以最大可能降低丢失数据的风险,所以它一般适用于对丢失数据很敏感的业务场景,例如涉及金钱交易的业务。

RDB和AOF区别

Redis持久化 - 图3
AOF文件比RDB更新频率高,优先使用AOF还原数据。
AOF比RDB更安全也更大
RDB性能比AOF好
如果两个都配了优先加载AOF

Redis 4.0 混合持久化

重启Redis时,我们很少使用RDB来恢复内存状态,因为会丢失大量数据。我们通常使用AOF日志重放,但是重放AOF日志性能相对RDB来说要慢很多,这样在Redis实例很大的情况下,启动需要花费很长的时间。 Redis4.0为了解决这个问题,带来了一个新的持久化选项——混合持久化。

通过如下配置可以开启混合持久化:
aof-use-rdb-preamble yes #默认是no如果你改成了yes就是开启了混合持久化了.


混合持久化AOF文件结构
Redis持久化 - 图4


AOF在重写的时候会把内存里面的数据重写到aof文件里面,但是是RDB格式的,如果后面你还有数据需要保存,会继续追加到aof文件里面来,但是是写成aof格式的.
如果发生下一次重写的时候这些aof命令又会变成RDB格式的.

于是在Redis重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率大幅得到提升。

如何选用合适的持久化方式

一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。

有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。

如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

重启恢复数据

redis重启会自动加载缓存文件,内部是C语言的方式实现的.
如果开启RDB快照方式就是给二进制文件重新解析放到内存中
如果是AOF的方式的话,redis会按照AOF快照文件中的命令一条一条的执行,AOF的方式恢复速度是比较慢的,假如说你一条数据你修改了很多次,AOF也记录了很多次没用的指令,这样恢复的时候一条一条的执行是很慢的.
AOF会定期根据内存的最新数据生成aof文件,这样没用的指令就没了.

如果RDB和AOF两种配置都是开启状态,redis一启动会优先选用AOF来恢复数据,因为AOF的数据更完整,所以AOF启动的优先级高.但是实际情况我们两个不会都一起开启.

为啥不能用redis做专门的持久化数据库存储

SQL 查询还是要强大很多吧,简单分析需求可能一个 SQL 就搞定了。
还有一种就是如果数据量多了, Redis 会吃满内存,内存爆掉会有丢数据的风险,这时序列化到 MySQL 就是一种解决方案,将一些冷数据从 Redis 转移到 MySQL ,降低 Redis 内存使用。
Redis 重启后数据加载会耗时。
Redis 持久存储和内存占用是 1:1 的关系,数据都用redis存储会占据大量空间,有用没用的数据都会在内存。
RDB 定时(一般比较长..)刷到磁盘, 丢数据的风险比较大.
MySQL 有权限控制,用户可以精确到每个 IP 的每个账户,目标可以精确到每个表的每个操作。
Redis 则是天生设计成完全开放权限,包括完全删除数据库的操作,任何人都可以执行。要么就只能把指令重命名成空的,完全禁止任何人执行。
MySQL 的数据库保存在磁盘中,万一崩溃断电,也有数据库日志可以用以完成数据库事务。
MySQL 支持主从备份,所有的写入操作都可以实时发送到异地,哪怕突然机房被核弹轰炸,也不会丢失数据(可能除了最后几条语句)。Redis 的崩溃……嗯小心数据全丢。Redis 的 Replication 备份……嗯小心数据全丢。

负载均衡:MySQL 可以单主多从,也可以胆子够大在内网做双主,也可以用 innodb 配合 galera 做集群,每台机器都有一个独立的拷贝,因此服务器之间只要传输写指令即可。