阿里Java岗二面:Redis了解?说说持久化机制及RDB/AOF应用场景分析
Posted Java程序员飞飞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里Java岗二面:Redis了解?说说持久化机制及RDB/AOF应用场景分析相关的知识,希望对你有一定的参考价值。
Redis是一种面向“key-value”类型数据的分布式NoSQL数据库系统,具有高性能、持久存储、适应高并发应用场景等优势。它虽然起步较晚,但发展却十分迅速。
文章主要包含二个方面:Redis持久化是如何工作的、Redis提供了RDB持久化和AOF持久化。
一、Redis持久化是如何工作的?
什么是持久化?简单来讲就是将数据放到断电后数据不会丢失的设备中,也就是我们通常理解的硬盘上。
首先我们来看一下数据库在进行写操作时到底做了哪些事,主要有下面五个过程:
- 客户端向服务端发送写操作(数据在客户端的内存中)。
- 数据库服务端接收到写请求的数据(数据在服务端的内存中)。
- 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
- 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
- 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。
故障分析
写操作大致有上面5个流程,下面我们结合上面的5个流程看一下各种级别的故障:
- 当数据库系统故障时,这时候系统内核还是完好的。那么此时只要我们执行完了第3步,那么数据就是安全的,因为后续操作系统会来完成后面几步,保证数据最终会落到磁盘上。
- 当系统断电时,这时候上面5项中提到的所有缓存都会失效,并且数据库和操作系统都会停止工作。所以只有当数据在完成第5步后,才能保证在断电后数据不丢失。
通过上面5步的了解,可能我们会希望搞清下面一些问题:
- 数据库多长时间调用一次write,将数据写到内核缓冲区?
- 内核多长时间会将系统缓冲区中的数据写到磁盘控制器?
- 磁盘控制器又在什么时候把缓存中的数据写到物理介质上?
对于第一个问题,通常数据库层面会进行全面控制。
而对第二个问题,操作系统有其默认的策略,但是我们也可以通过POSIX
API提供的fsync系列命令强制操作系统将数据从内核区写到磁盘控制器上。对于第三个问题,好像数据库已经无法触及,但实际上,大多数情况下磁盘缓存是被设置关闭的,或者是只开启为读缓存,也就是说写操作不会进行缓存,直接写到磁盘。
建议的做法是仅仅当你的磁盘设备有备用电池时才开启写缓存。
数据损坏
所谓数据损坏,就是数据无法恢复,上面我们讲的都是如何保证数据是确实写到磁盘上去,但是写到磁盘上可能并不意味着数据不会损坏。比如我们可能一次写请求会进行两次不同的写操作,当意外发生时,可能会导致一次写操作安全完成,但是另一次还没有进行。如果数据库的数据文件结构组织不合理,可能就会导致数据完全不能恢复的状况出现。
这里通常也有三种策略来组织数据,以防止数据文件损坏到无法恢复的情况:
- 第一种是最粗糙的处理,就是不通过数据的组织形式保证数据的可恢复性。而是通过配置数据同步备份的方式,在数据文件损坏后通过数据备份来进行恢复。实际上MongoDB在不开启操作日志,通过配置ReplicaSets时就是这种情况。
- 另一种是在上面基础上添加一个操作日志,每次操作时记一下操作的行为,这样我们可以通过操作日志来进行数据恢复。因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况。这也类似于MongoDB开启了操作日志的情况。
- 更保险的做法是数据库不进行旧数据的修改,只是以追加方式去完成写操作,这样数据本身就是一份日志,这样就永远不会出现数据无法恢复的情况了。实际上CouchDB就是此做法的优秀范例。
二 、Redis提供了RDB持久化和AOF持久化
RDB机制的优势和略施
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000
RDB文件保存过程
- redis调用fork,现在有了子进程和父进程。
- 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
- 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。
另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。
优势
- 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。比如你可能打算没1天归档一些数据。
- 方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
- RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
劣势
- 如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save
point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。
因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。
AOF文件保存过程
redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。
当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)
appendonly yes //启用aof持久化方式#
appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐#
appendfsync no //完全依赖os,性能最好,持久化没保证
aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。
为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下:
- redis调用fork ,现在有父子两个进程
- 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到
- 写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
- 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
优势
- 使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 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 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
- AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。) 测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。
抉择
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
对于Redis有收集整理过一些学习笔记和Redis高频面试题解析在这分享给大家!
由于篇幅限制,太长看下去会很乏味,也会影响阅读体验,下面展现将以图片形式展示。获取原文件以及更多资源请“一键三连”,点击获取资料!
Redis学习笔记
一、Redis 基本数据结构与实战场景
二、Redis 常见异常及解决方案
三、分布式环境下常见的应用场景
四、Redis 集群模式
Redis高频常问面试题解析
一、Redis基础概念相关问题
1.什么是Redis?
2. Redis的特点有哪些?
3. Memcache与Redis的区别都有哪些?
4. Redis相比Memcached有哪些优势?
5.如何实现本地缓存?请描述一下你知道的方式
5. Redis通讯协议是什么?有什么特点?
二、Redis 数据结构及指令相关问题
- Redis支持的数据类型
- Redis常用的命令有哪些?
- 一个字符串类型的值能存储最大容量是多少?
- Redis各个数据类型最大存储量分别是多少?
- 请价绍一下Redis的数据类型SortedSet ( zset )以及底层实现机制?
- Redis事务相关命令有哪些?
- 什么是Redis事务?原理是什么?
- Redis事务的注意点有哪些?
- Redis为什么不支持回滚?
- 请介绍一 下Redis的Pipeline (管道) ,以及使用场景
- 请说明一 下Redis的批量命令与Pipeline 有什么不同?
- 请介绍一下Redis的发布订阅功能
- Redis的链表数据结构的特征有哪些?
- 请价绍一下Redis的String类型底层实现?
- Redis的String类型使用SSD方式实现的好处?
- 设置键的生存时间和过期时间有哪些命令?
三、Redis 高并发处理策略相关问题
1.为什么Redis需要把所有数据放到内存中?
2. Redis是单线程的吗?
3. Redis为什么设计成单线程的?
4.什么是缓存穿透?怎么解决?
5.什么是缓存霄崩?怎么解决?
6.缓存的更新策略有几种?分别有什么注意事项?
7.请介绍几个可能导致Redis阻塞的原因
8.怎么去发现Redis阻塞异常情况?
四、Redis 集群相关问题
- Redis集群架构模式有哪几种?
- Redis集群最大节点个数是多少?
- Redis集群的主从复制模型是怎样的?
- 请介绍一下Redis集群实现方案
- Redis集群会有写操作丢失吗?为什么?
- Redis慢查询是什么?通过什么配置?
- Redis的慢查询修复经验有哪些?怎么修复的?
- 如何优化Redis服务的性能?
- Redis的主从复制模式有什么优缺点?
- Redis sentinel (哨兵)模式优缺点有哪些?
- 如何设置Redis的最大连接数?查看Redis的最大连接数?查看Redis的当前连接数?
- 介绍一些Redis常用的安全设置?
五、Redis 缓存管理及持久化机制相关问题
- Redis持久化机制有哪些?
- Redis持久化机制AOE和RDB有哪些不同之处?
- 请介绍一下RDB持久化机制的优缺点
- 请介绍一下AOF持久化机制的优缺点
- 如果AOF文件的数据出现异常,Redis 服务怎么处理?
- 常见的淘汰算法有哪些?
- Redis淘汰策略有哪些?
- Redis缓存失效策略有哪些?
- Redis如何做内存优化?
- 什么是bigkey?有什么影响?
- 怎么发现bigkey?
- Redis的内存消耗分类有哪些?内存统计使用什么命令?
- 简单介绍一下Redis的内存管理方式有哪些?
- 如何设置Redis的内存上限?有什么作用?
- Redis报内存不足怎么处理?
六、Redis 应用场景设计相关问题
- Redis适用场景有哪些?
- Redis常用的业务场景有哪些?
- Redis支持的Java户端有哪些?简单说明一下特点。
- 请简单描述-下Jedis的基本使用方法?
- Jedis连接池链接方法有什么优点?
- 什么是分布式锁?有什么作用?
- 分布式锁可以通过什么来实现?
- 介绍一下分布式锁实现需要注意的事项?
- Redis怎么实现分布式锁?
- 缓存命中率表示什么?
- 怎么提高缓存命中率?
- 请介绍一下Spring注解缓存
解析展示
Redis学习笔记
以上是关于阿里Java岗二面:Redis了解?说说持久化机制及RDB/AOF应用场景分析的主要内容,如果未能解决你的问题,请参考以下文章