本文内容来源于 Redis 作者博文,Redis 作者说,他看到的所有针对 Redis 的讨论中,对 Redis持久化的误解是最大的,于是他写了一篇长文来对 Redis 的持久化进行了系统性的论述。文章非常长,也很值得一看,NoSQLFan 将主要内容简述成本文。
什么是持久化,简单来讲就是将数据放到断电后数据不会丢失的设备中。也就是我们通常理解的硬盘上。
写操作的流程
首先我们来看一下数据库在进行写操作时到底做了哪些事,主要有下面五个过程。
客户端向服务端发送写操作(数据在客户端的内存中)
数据库服务端接收到写请求的数据(数据在服务端的内存中)
服务端调用 write (2) 这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)
操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)
故障分析
写操作大致有上面 5 个流程,下面我们结合上面的 5 个流程看一下各种级别的故障。
当数据库系统故障时,这时候系统内核还是 OK 的,那么此时只要我们执行完了第 3 步,那么数据就是安全的,因为后续操作系统会来完成后面几步,保证数据最终会落到磁盘上。
当系统断电,这时候上面 5 项中提到的所有缓存都会失效,并且数据库和操作系统都会停止工作。所以只有当数据在完成第 5 步后,机器断电才能保证数据不丢失,在上述四步中的数据都会丢失。
通过上面 5 步的了解,可能我们会希望搞清下面一些问题:
数据库多长时间调用一次 write (2),将数据写到内核缓冲区
内核多长时间会将系统缓冲区中的数据写到磁盘控制器
磁盘控制器又在什么时候把缓存中的数据写到物理介质上
对于第一个问题,通常数据库层面会进行全面控制。而对第二个问题,操作系统有其默认的策略,但是我们也可以通过 POSIX API 提供的 fsync 系列命令强制操作系统将数据从内核区写到磁盘控制器上。对于第三个问题,好像数据库已经无法触及,但实际上,大多数情况下磁盘缓存是被设置关闭的。或者是只开启为读缓存,也就是写操作不会进行缓存,直接写到磁盘。建议的做法是仅仅当你的磁盘设备有备用电池时才开启写缓存。
数据损坏
所谓数据损坏,就是数据无法恢复,上面我们讲的都是如何保证数据是确实写到磁盘上去,但是写到磁盘上可能并不意味着数据不会损坏。比如我们可能一次写请求会进行两次不同的写操作,当意外发生时,可能会导致一次写操作安全完成,但是另一次还没有进行。如果数据库的数据文件结构组织不合理,可能就会导致数据完全不能恢复的状况出现。
这里通常也有三种策略来组织数据,以防止数据文件损坏到无法恢复的情况:
第一种是最粗糙的处理,就是不通过数据的组织形式保证数据的可恢复性。而是通过配置数据同步备份的方式,在数据文件损坏后通过数据备份来进行恢复。实际上 MongoDB 在不开启 journaling 日志,通过配置 Replica Sets 时就是这种情况。
另一种是在上面基础上添加一个操作日志,每次操作时记一下操作的行为,这样我们可以通过操作日志来进行数据恢复。因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况。这也类似于 MongoDB 开启了 journaling 日志的情况。
更保险的做法是数据库不进行老数据的修改,只是以追加方式去完成写操作,这样数据本身就是一份日志,这样就永远不会出现数据无法恢复的情况了。实际上 CouchDB 就是此做法的优秀范例。
RDB 快照
下面我们说一下 Redis 的第一个持久化策略,RDB 快照。Redis 支持将当前数据的快照存成一个数据文件的持久化机制。而一个持续写入的数据库如何生成快照呢。Redis 借助了 fork 命令的 copy on write 机制。在生成快照时,将当前进程 fork 出一个子进程,然后在子进程中循环所有的数据,将数据写成为 RDB 文件。
我们可以通过 Redis 的 save 指令来配置 RDB 快照生成的时机,比如你可以配置当 10 分钟以内有 100 次写入就生成快照,也可以配置当 1 小时内有 1000 次写入就生成快照,也可以多个规则一起实施。这些规则的定义就在 Redis 的配置文件中,你也可以通过 Redis 的 CONFIG SET 命令在 Redis 运行时设置规则,不需要重启 Redis。
Redis 的 RDB 文件不会坏掉,因为其写操作是在一个新进程中进行的,当生成一个新的 RDB 文件时,Redis 生成的子进程会先将数据写到一个临时文件中,然后通过原子性 rename 系统调用将临时文件重命名为 RDB 文件,这样在任何时候出现故障,Redis 的 RDB 文件都总是可用的。
同时,Redis 的 RDB 文件也是 Redis 主从同步内部实现中的一环。
但是,我们可以很明显的看到,RDB 有他的不足,就是一旦数据库出现问题,那么我们的 RDB 文件中保存的数据并不是全新的,从上次 RDB 文件生成到 Redis 停机这段时间的数据全部丢掉了。在某些业务下,这是可以忍受的,我们也推荐这些业务使用 RDB 的方式进行持久化,因为开启 RDB 的代价并不高。但是对于另外一些对数据安全性要求极高的应用,无法容忍数据丢失的应用,RDB 就无能为力了,所以 Redis 引入了另一个重要的持久化机制:AOF 日志。
AOF 日志
aof 日志的全称是 append only file,从名字上我们就能看出来,它是一个追加写入的日志文件。与一般数据库的 binlog 不同的是,AOF 文件是可识别的纯文本,它的内容就是一个个的 Redis 标准命令。比如我们进行如下实验,使用 Redis2.6 版本,在启动命令参数中设置开启 aof 功能:
./redis-server --appendonly yes
然后我们执行如下的命令:
redis 127.0.0.1:6379> set key1 HelloOKredis 127.0.0.1:6379> append key1 " World!"(integer) 12redis 127.0.0.1:6379> del key1(integer) 1redis 127.0.0.1:6379> del non_existing_key (integer) 0
这时我们查看 AOF 日志文件,就会得到如下内容:
$ cat appendonly.aof*2$6SELECT$10*3$3set$4key1$5Hello*3$6append$4key1$7 World!*2$3del$4key1
可以看到,写操作都生成了一条相应的命令作为日志。其中值得注意的是最后一个 del 命令,它并没有被记录在 AOF 日志中,这是因为 Redis 判断出这个命令不会对当前数据集做出修改。所以不需要记录这个无用的写命令。另外 AOF 日志也不是完全按客户端的请求来生成日志的,比如命令 INCRBYFLOAT 在记 AOF 日志时就被记成一条 SET 记录,因为浮点数操作可能在不同的系统上会不同,所以为了避免同一份日志在不同的系统上生成不同的数据集,所以这里只将操作后的结果通过 SET 来记录。
AOF 重写
你可以会想,每一条写命令都生成一条日志,那么 AOF 文件是不是会很大?答案是肯定的,AOF 文件会越来越大,所以 Redis 又提供了一个功能,叫做 AOF rewrite。其功能就是重新生成一份 AOF 文件,新的 AOF 文件中一条记录的操作只会有一次,而不像一份老文件那样,可能记录了对同一个值的多次操作。其生成过程和 RDB 类似,也是 fork 一个进程,直接遍历数据,写入新的 AOF 临时文件。在写入新文件的过程中,所有的写操作日志还是会写到原来老的 AOF 文件中,同时还会记录在内存缓冲区中。当重完操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中。然后调用原子性的 rename 命令用新的 AOF 文件取代老的 AOF 文件。
从上面的流程我们能够看到,RDB 和 AOF 操作都是顺序 IO 操作,性能都很高。而同时在通过 RDB 文件或者 AOF 日志进行数据库恢复的时候,也是顺序的读取数据加载到内存中。所以也不会造成磁盘的随机读。
AOF 可靠性设置
AOF 是一个写文件操作,其目的是将操作日志写到磁盘上,所以它也同样会遇到我们上面说的写操作的 5 个流程。那么写 AOF 的操作安全性又有多高呢。实际上这是可以设置的,在 Redis 中对 AOF 调用 write (2)写入后,何时再调用 fsync 将其写到磁盘上,通过 appendfsync 选项来控制,下面 appendfsync 的三个设置项,安全强度逐渐变强。
appendfsync no
当设置 appendfsync 为 no 的时候,Redis 不会主动调用 fsync 去将 AOF 日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数 Linux 操作系统,是每 30 秒进行一次 fsync,将缓冲区中的数据写到磁盘上。
appendfsync everysec
当设置 appendfsync 为 everysec 的时候,Redis 会默认每隔一秒进行一次 fsync 调用,将缓冲区中的数据写到磁盘。但是当这一次的 fsync 调用时长超过 1 秒时。Redis 会采取延迟 fsync 的策略,再等一秒钟。也就是在两秒后再进行 fsync,这一次的 fsync 就不管会执行多长时间都会进行。这时候由于在 fsync 时文件描述符会被阻塞,所以当前的写操作就会阻塞。
所以,结论就是,在绝大多数情况下,Redis 会每隔一秒进行一次 fsync。在最坏的情况下,两秒钟会进行一次 fsync 操作。
这一操作在大多数数据库系统中被称为 group commit,就是组合多次写操作的数据,一次性将日志写到磁盘。
appednfsync always
当设置 appendfsync 为 always 时,每一次写操作都会调用一次 fsync,这时数据是最安全的,当然,由于每次都会执行 fsync,所以其性能也会受到影响。
对于 pipelining 有什么不同
对于 pipelining 的操作,其具体过程是客户端一次性发送N个命令,然后等待这N个命令的返回结果被一起返回。通过采用 pipilining 就意味着放弃了对每一个命令的返回值确认。由于在这种情况下,N个命令是在同一个执行过程中执行的。所以当设置 appendfsync 为 everysec 时,可能会有一些偏差,因为这N个命令可能执行时间超过 1 秒甚至 2 秒。但是可以保证的是,最长时间不会超过这N个命令的执行时间和。
与 postgreSQL 和 MySQL 的比较
这一块就不多说了,由于上面操作系统层面的数据安全已经讲了很多,所以其实不同的数据库在实现上都大同小异。总之最后的结论就是,在 Redis 开启 AOF 的情况下,其单机数据安全性并不比这些成熟的 SQL 数据库弱。
数据导入
这些持久化的数据有什么用,当然是用于重启后的数据恢复。Redis 是一个内存数据库,无论是 RDB 还是 AOF,都只是其保证数据恢复的措施。所以 Redis 在利用 RDB 和 AOF 进行恢复的时候,都会读取 RDB 或 AOF 文件,重新加载到内存中。相对于 MySQL 等数据库的启动时间来说,会长很多,因为 MySQL 本来是不需要将数据加载到内存中的。
但是相对来说,MySQL 启动后提供服务时,其被访问的热数据也会慢慢加载到内存中,通常我们称之为预热,而在预热完成前,其性能都不会太高。而 Redis 的好处是一次性将数据加载到内存中,一次性预热。这样只要 Redis 启动完成,那么其提供服务的速度都是非常快的。
而在利用 RDB 和利用 AOF 启动上,其启动时间有一些差别。RDB 的启动时间会更短,原因有两个,一是 RDB 文件中每一条数据只有一条记录,不会像 AOF 日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了。另一个原因是 RDB 文件的存储格式和 Redis 数据在内存中的编码格式是一致的,不需要再进行数据编码工作。在 CPU 消耗上要远小于 AOF 日志的加载。
好了,大概内容就说到这里。更详细完整的版本请看 Redis 作者的博文:Redis persistence demystified。本文如有描述不周之处,就大家指正。
分享到:
相关推荐
本文内容来源于Redis作者博文,Redis作者说,他看到的所有针对Redis的讨论中,对Redis持久化的误解是最大的,于是他写了一篇长文来对Redis的持久化进行了系统性的论述。文章非常长,也很值得一看
2. AOF与RDB持久化:结合AOF(Append Only File)和RDB(Snapshotting)两种持久化方式,兼顾数据安全性与恢复速度。 3. 事务处理:虽然Redis原生不支持分布式事务,但可以通过Lua脚本实现简单事务逻辑,保证数据...
- 持久化操作:Redis4.0版本优化了持久化操作,比如使用UNLINK命令异步删除键,从而提高了FLUSHALL操作的速度。 为了保证Redis实例的稳定性,对info命令的输出信息进行了完善,使开发者可以更加方便地诊断问题。...
Redis是一种高性能的Key-Value数据库,它以内存存储为主,同时也支持数据持久化。在本文中,我们将深入探讨如何利用Redis来实现Session功能,以及这一方法相对于传统的Session和Cookie保存用户状态的优势。 传统上...
通过这种配置,我们可以构建一个能够承受高并发、支持会话持久化并且具备良好容错能力的Web服务集群。Nginx的反向代理和负载均衡,Redis的会话共享,以及Tomcat的应用处理,共同构成了一个高效且可靠的架构。在实际...
**Redis持久化策略** Redis提供了两种主要的持久化方式: 1. **Snapshot(快照)**:在一定时间间隔或写操作达到一定数量后,Redis会生成当前数据集的快照,保存到磁盘。这种方式简单快速,但无法保证数据的完全...
标题中的“redis+mybatis+RSA+DES+登陆授权”是一个综合性的技术组合,涉及到数据库缓存、持久化框架、加密算法以及用户登录授权等多个关键领域。这些技术在实际的Web开发中扮演着重要的角色。下面将分别详细介绍...
K线可用无问题!!!!可指定输赢!!!! 带码支付,不过要自己配置,自己看教程配置一下吧。...Redis ...ZendGuardLoader 脚本解密 用于解密ZendGuard加密脚本!...redis 缓存器 基于内存亦可持久化的Key-Value数据库
- `DistributedSessionManager`:管理Session的创建、读取、更新和删除,与Redis进行交互,将Session数据持久化到Redis中。 - `DistributedHttpSessionWrapper`:包装原始的`HttpServletRequest`,提供访问和修改...
在IT领域,RDB文件通常指的是Redis数据库的持久化文件。Redis是一款开源的、高性能的键值存储系统,用于存储键值对数据,并提供多种数据结构支持,如字符串、哈希、列表、集合和有序集合。RDB文件是Redis的一种持久...
持久层:mybatis持久化,使用MyBatis-Plus优化,减少sql开发量;aop切换数据库实现读写分离。Transtraction注解事务。 MVC: 基于spring mvc注解,Rest风格Controller。Exception统一管理。 缓存和Session:注解redis...
在本文中,我们将深入探讨如何在MVC(Model-View-Controller)框架中利用Form认证进行用户身份验证,并结合Cookie实现持久化的会话管理。同时,我们还将介绍如何利用Redis作为存储工具来记录异常信息,以提升系统的...
`flask-session` 是一个Flask扩展,它可以支持多种持久化存储方式,如Redis、Memcached或文件系统,以克服内置session的局限性。这提供了更好的可扩展性和可靠性,因为session数据存储在服务器外部,即使服务器重启...
Redis是一个内存中的数据结构存储系统,可以作为持久化的Key-Value数据库。由于它可以被多个进程共享,因此可以用来存储PHP和Node.js的Session数据。 - **在PHP中配置Redis**:修改`php.ini`文件,设置`session....
3. 数据库特性:例如Redis的持久化策略(RDB和AOF),MongoDB不支持事务但具有高可用性。 八、Python基础知识 1. Python2与Python3差异:语法、库支持等,如print语句改为函数,Unicode处理等。 2. 编码问题:理解...
SpringBoot是一个快速开发框架,它简化了Spring应用的初始化和配置。Shiro则是一个强大且易用的Java安全框架,处理认证、授权、加密和会话管理。当两者结合,可以为基于RESTful API的Web应用提供安全控制。 **一、...
Shiro的`RememberMe`服务可以实现用户登录状态的持久化。在登录成功后,将用户的凭据存储到cookie中,下次访问时,Shiro会自动识别并恢复用户的身份。这个过程涉及到加密和解密操作,确保数据的安全性。 4. **...
这些依赖帮助处理数据持久化、缓存管理和数据库操作。 3. **工具类**: 核心模块通常提供一系列的工具类,例如日期时间处理、字符串操作、文件操作、加密解密等,这些工具类极大地提高了开发效率。 4. **全局异常...