说道redis,我们可能都会知道了他是一个类似缓存的一个内存数据库
,这篇我们来讲讲redis这种非关系型数据库
用在什么地方,以及他的特色——持久化是怎么实现的。
1 redis的适用环境
首先作为一个nosql的key—value组成的数据库,它们能存储的数据结构必须是简单的,因为有关系的数据
即使存储进去之后查询也是很困难的
,并且对于海量的数据存储还是关系型数据库比较合适
。
举一个把一般数据库数据存储到key-value中的例子:
遵从规则为
key:表名:主键值:列名
value:列值
如果加上表关系的话还要复杂好几倍的。
那么什么样的数据适合存储在非关系型数据库中的呢?
-
关系不是很密切的的数据
,比如用户信息,班级信息,评论数量等等。 -
量比较大的数据
,如访问记录等 -
访问比较频繁的数据
,如用户信息,访问数量,最新微博等
2 持久化
那么这么多,这么重要的数据都存储在内存中,如果突然断电,岂不是很糟糕,于是就有了数据的持久化机制,这个其实就是把内存中的数据存储到硬盘中,方便数据的持续存在,也可以减少断电造成的损失。
那么我们怎么持久化数据呢?多长时间进行一次持久化呢?
redis 支持两种持久化方式
,一种是 Snapshotting(快照,RDB)
也是默认方式,另一种是 Append-only file(缩写 aof)
的方式。下面分别介绍:
2.1 Snapshotting
快照是默认的持久化方式
。这种方式是就是将内存中数据以快照的方式
写入到二进制文件
中,默认的文件名为dump.rdb
。我们可以配置 redis在 n 秒内如果超过 m 个 key 被修改
就自动做快照,下面是默认的快照保存配置:
save 900 1 #900 秒内如果超过 1 个 key 被修改,则发起快照保存
save 300 10 #300 秒内容如超过 10 个 key 被修改,则发起快照保存
save 60 10000
下面介绍详细的快照保存过程
:
- redis
调用 fork创建子进程
,现在有了子进程和父进程。 - 父进程
继续处理 client 请求
,子进程负责将内存内容写入到临时文件
。由于 os 的实时复制机制( copy on write)父子进程会共享相同的物理页面,当父进程处理写请求
时,os 会为父进程要修改的页面创建副本
,而不是写共享的页面。所以子进程地址空间内的数据是fork时刻
整个数据库的一个快照。所以他是一个全量的方式
来进行持久化的。 -
当子进程将快照写入临时文件完毕后
,用临时文件替换原来的快照文件
,然后子进程退出。client 也可以使用 save 或者 bgsave 命令通知 redis 做一次快照持久化。save
操作是在主线程中保存快照
的,由于 redis 是用一个主线程来处理所有 client 的请求,这种方式会阻塞所有client 请求
。所以不推荐使用。另一点需要注意的是,每次快照持久化
都是将内存
数据完整
写入到磁盘
一次,并不是增量
的只同步变更数据。
优点:
- 一旦采用该方式,那么你的
整个Redis数据库
将只包含一个文件
,这样非常方便进行备份
。比如你可能打算每1天归档一些数据。 -
方便备份
,我们可以很容易
的将一个一个RDB文件移动到其他的存储介质上
- RDB 在
恢复大数据集时的速度比 AOF 的恢复速度要快
。 - RDB
可以最大化 Redis 的性能
:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作
,父进程无须执行任何磁盘 I/O 操作
。
缺点:
- 服务器宕机时,
会丢失从上一次快照后修改的数据
。 - 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,
如果数据量大的话
,而且写操作比较多
,必然会引起大量的磁盘 io 操作
,可能会严重影响性能
。
2.2 AOF方式
由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外 down 掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用 aof 持久化方式。下面介绍 Append-only file:aof 比快照方式有更好的持久化性,是由于在使用 aof 持久化方式时,redis 会将每一个收到的写命令
都通过 write 函数追加到文件中
(默认是 appendonly.aof
)。当 redis 重启时
会通过重新执行文件中保存的写命令
来在内存中重建整个数据库的内容
。当然由于 os 会在内核中缓存 write 做的修改
,所以可能不是立即写到磁盘上。这样 aof 方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉 redis 我们想要通过 fsync
函数强制 os 写入到磁盘的时机
。有三种方式如下(默认是:每秒 fsync 一次)
# appendfsync always //收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
#appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中
# appendfsync no //完全依赖 os,性能最好,持久化没保证
如果日志过大
,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件
中,同时Redis还会创建一个新的文件
用于记录此期间
有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性
。
aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用 incr test命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的。因为要恢复数据库的状态其实文件中保存一条 set test 100 就够了。为了压缩 aof
的持久化文件。 redis 提供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件。
AOF的缺点:
- 对于相同数量的数据集而言,
AOF文件通常要大于RDB文件
。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
。 -
根据同步策略的不同
,AOF在运行效率上往往会慢于RDB
。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。
二者选择的标准
二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。RDB
这个就更有些 eventually consistent(最终一致
)的意思了。