`
fisk
  • 浏览: 3811 次
  • 性别: Icon_minigender_1
  • 来自: 香港
社区版块
存档分类
最新评论

redis 持久化理解

阅读更多

快照方式(Snapshotting)
作者将这种持久化方式称为point-in-time, 即它并不能保证每个时刻内存中的数据集与磁盘上的二进制文件是完全一样的,但可以保证磁盘上的二进制文件与系统内存中某个时刻(最近一次fork时刻)的数据是完全一样的。若以时间为横轴,且每个fork时刻用Delta函数来描述,你会看到很多脉冲式的图像。当然,若某次持久化失败,那么相应的Delta函数也应该删除掉。在每次fork的时刻,系统都会把脏数据的数目清零(若持久化成功的话,是这样的;若失败的话,在失败后需要将当前脏数据的数目加上持久化前的数值作为新的脏数据数目),也同时将该时刻作为新的零时刻来计算,上述说明会直接影响到参数设置的理解。

若 fork的时刻是t1, 相应的持久化成功结束的时刻是t2,  那么t2-t1是该次持久化需要的时间。每次持久化过程需要的时间是与系统数据集的大小成正比的,那么典型的数值是什么呢?同时,在加载的时候,需要的时间是多少呢?现在我还没有太多数据做测试,以后慢慢积累这些有价值的经验数据了。
 
参数设置
Save 60 10000
Save 300 10
Save 900 1
根据上述解释,所有的参数设置只有在两个Delta函数之间才是有意义的,即两类计数都不会跨越脉冲/Delta函数。参数的意义是,若每六十秒内,有一万或以上个键的值改变过(包含新创建或删除的键值对),则启动持久化过程;若连续五次都不满足此要求,就会同时进行后面的判断,即若每五分钟内有十或以上个键的值改变过,则启动持久化过程;依次类推。采取类似的设置,我估计原因可能是:在实际中,单位时间键值的改变数量,随时间的分布是随机的,有高有低,需要使用不同频率来匹配各种情形(好似用网捉鱼,用最大网眼的网快速扫,同时用中等网眼的网以一般速度扫,再同时用非常密的网来慢慢扫,比喻也不太恰当,呵呵)。只要数据有任何改变,此设置保证15分钟内系统会至少持久化一次,至多1分钟会持久化一次,因此在系统出问题时,会最多丢失“一分钟的数据”。

需要注意的是,尽管系统是利用单位时间内脏键值的数量来启动持久化事件,但在持久化过程中,并不是采取增量的方式进行的,而是每次都将此刻整个数据集的快照重新编码写到硬盘的二进制文件中。在持久化结束后,硬盘上的数据是数据库中某时刻(持久化过程启动的时刻)的数据快照。若由于系统断电或操作系统出问题,而使得持久化过程失败,那么系统的损失是自上次持久化之后新增的脏数据信息。

系统是利用上述参数来判断何时启动快照方式的持久化过程的,而此功能的实现是基于REDIS自带的事件驱动库完成的。之后,我们再专门讨论这个简洁的事件驱动库。
 
持久化过程(copy-on-write)
当持久化事件被启动时,系统的主进程会采用copy-on-write的模式 fork一个子进程。当然,具体的模式与操作系统中fork的实现有关。所谓copy-on-write, 在此例中可以做如下理解:

系统会根据父进程的信息创建其子进程,但创建之初,他们是共享地址空间的。父进程继续提供各种读写操作的服务,而子进程则进行持久化操作,将内存中的数据重新编码后全部写到硬盘上。对于客户端来讲,它的请求会像往常一样得到响应,与相应的数据信息是否被持久化无关,所以快照持久化是异步进行的。其实,也不是完全无关,因系统的CPU与内存等资源是有限的,两个进程有时会存在资源竞争关系,从而造成相互影响,比如当数据量已占据内存的80%时,父进程同时要对高负载的读写操作作出响应,尤其是写操作。此种情况后面会仔细讨论。

当父进程进行写操作时,它只把被操作的键值从共享地址空间里做本地化的复制,之后在副本上进行写操作,而不是去修改“与子进程共享的地址空间里”的键值信息,因为那样的行为会违背 point-in-time的语义,即在持久化结束前fork时刻内存数据的快照会被改变。若此时父进程需要进行大量写操作,系统会对每个被操作的键值做复制,从而需要更多的内存资源。极限情况是,在子进程持久化的过程中,父进程应客户端的请求,对所有的键值都进行写操作,那么就需要“容量为两倍于数据集大小”的内存才可以应付。当子进程结束持久化时,系统会将其关掉,其被复制的键值所占用的地址空间将被释放,未被复制的键值直接归父进程私有。

通过上述解释,在持久化过程中,内存的使用会出现暂时地上升。当然,若系统是处于一次接一次地进行持久化的状态,那么内存的使用会一直是数据集的1.x倍。那么,具体数值与哪些因素有关呢? 我想基本有以下三个:写操作的负载(平均每秒有多少写操作,取决于业务逻辑),子进程持久化的速度(取决于REDIS内部实现),还有数据集的大小(基本取决于可以占用的内存空间)。这是快照方式需要注意的核心问题,作者也给出过一些典型的数值,请参见[4]

到此为止,算是基本上将 copy-on-write的语义与在持久化背景中的后果解释清楚,那么我们再来仔细看看,子进程所进行的持久化过程是具体怎样的呢。基本上,是通过遍历REDIS实例的每个数据库中的主哈希表,将键与值分别编码而存储起来。至于具体的编码形式,我会在相应的代码阅读中,详细地整理出来。需要提醒一点是,在REDIS 2.4中作者改进了持久化速度,主要是针对小的 hash /list/set/zset。因这四种数据结构的mini版本实现,完全是利用单纯的数组实现的(没有复杂的指针),且其在内存中的结构 [3] 与持久化后的存储方式基本一致。那么,在持久化此类数据时,REDIS没有必要进行编码,只需直接放进缓冲区中。在REDIS将全部数据集写到临时文件后,系统会将之替换掉原来的 RDB文件,从而使得快照持久化过程也是原子操作。

也许你还可能有另外个疑问,在持久化过程中,当内存的需求量超过实际内存的大小时,系统会怎样处理呢?这触及到内存数据库的另一个死穴,而这两个问题,即持久化与容量限制,在传统的硬盘数据库领域,它们是比较容易被同时解决地。但对于REDIS来讲,将来可能会在其自带虚拟内存模块与更先进的持久化技术的结合下,得到较好的解答吧。作者对此的见解是这样 [4],但这是在解决集群与开发出更好的持久化技术之后,才可能被回答的。所以,对于REDIS社区来讲,至少是一年以后的事情。我对容量限制的解决也有些浅显的认识,以后再专门谈。


追加命令方式(Append Only File)
由上述分析可知,快照方式的持久化效果并非尽如人意,在机器或操作系统出现某种问题时,系统会丢失部分数据,同时内存被过多消耗也是遭人诟病的一点(但作者觉得这是个tradeoff)。为此,REDIS提供另一种更好的持久化方式AOF,即主进程每次将收到的写操作命令 以追加的方式写到同一个文件AOF中。而当加载的时候,通过重新播放即可得到数据集原有的状态。

参数设置及追加过程
在AOF中,有三点值得注意:其一,AOF以增长方式进行存储的,所以每次写的信息比快照少很多,只与单位时间内的写操作数目成正比。其二,系统的主进程是在将写操作命令放到通往AOF文件的缓冲区之后,才对相应客户端的请求作出回应的。这表明AOF具有更好的持久化效果,但同时也意味着主进程时刻占用着Disk I/O这个宝贵的资源。表面乍看,好似AOF方式是同步的,其实不然,因它并不能保证在回复客户端前已将命令写落到硬盘上,而只是写到缓存区中。其三,从缓冲区到硬盘的写操作是由系统函数fsync控制的,REDIS提供三种方式。其中,每秒钟 fsync一次是默认的。

appendfsync always
appendfsync everysec
appendfsync no
第一种设置具有最好的持久化效果,每个命令都要即时写到硬盘上,但DISK IO与系统的CPU等主要资源会被占用很多;第二种是每秒做一次fsync,持久化效果也比快照的每分钟做一次好很多;第三个策略是完全由操作系统掌控,比如,在等缓冲区满后,操作系统调用fsync写数据到硬盘上。

问题及引入日志重新技术
乍看这个方案很完美,不像快照那样利用多余的内存,且由于采取追加的方式,系统的CPU与DISK IO等资源的负载压力都被分散掉,同时也得到较好的持久化效果。但任何问题的难度永远是个“守恒量”,除非快照的想法太差,不然AOF方案是不会这样完美的。它的问题是硬盘上的AOF文件会一直单调递增下去,除了硬盘的容量有限制,其他诸如REDIS启动时加载AOF文件会非常慢,备份会非常慢,网络传输也会非常慢等等。

为了解决这个问题,作者提出日志重写的方法(Log rewrite)来不断控制AOF文件的过速增长。关键点是,对于某时刻的数据集来讲,硬盘上庞大的AOF文件可以对之进行重新构建,但这是个反问题,所以有多种答案。在此环境下,反问题的理解是:给定命令序列,可以得到唯一的数据集;但给定数据集,可以有众多命令序列得到该数据集,其长度可长可短,而我们寻求的就是尽量短的命令序列。最佳答案是显然的,即每次根据某时刻的数据集(快照),遍历所有数据库,对每个键值构建“插入操作”的命令,从而组成最直接/短的命令序列。

也许你已经感觉到,日志重写策略在实现上与快照十分类似。那我们现在主要来分析下他们的不同点:其一,启动方式不同,日志重写的启动在于硬盘上AOF文件的大小与增长速度有关。默认设置是:

auto-AOF-rewrite-percentage 100
auto-AOF-rewrite-min-size 64mb
即当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,启动新的日志重写过程;但当刚刚启动REDIS时,这个策略是有严重缺陷的,文件的尺寸可以由1KB变为2KB,1MB变为2MB,但是没有必要重写,所以需要引入另一个参数作为补充,即auto-AOF-rewrite-min-size。其二,构建数据的方式不同,快照是直接对数据集进行重新编码,而日志重写是根据数据集构建命令序列。但可想而知,他们所占的空间都是与数据集大小成正比的;相对于分散的追加方式来说,内存的消耗量是很大的。其三,在子进程进行日志重写的同时,主进程对客户端请求的响应也像平常一样,将写操作命令追加到原有的AOF文件中,当然这也是显然的,因“追加命令方式”就是这样设计的。整个日志重新的细节,请参见[1].

AOF总结
到此为止,我们发现AOF持久化方式包含两个部分,即分散的追加命令与不断的日志重写。主进程负责追加命令(主要利用CPU与DISK IO),子进程在需要的时候被创建而负责日志重写(主要利用CPU,内存与DISK IO)。由于REDIS很少是CPU瓶颈,所以资源的竞争关系主要集中在DISK IO上。日志重写有快照所具有的缺点-内存的消耗量是数据集的1.x倍,同时又会与命令的追加造成DISK IO上的冲突。为了避免这种冲突,系统提供如下参数:

no-appendfsync-on-rewrite yes
即在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里。也许你会觉得,这样的设置会使得AOF持久化效果与快照是一样的。仔细分析还是有不同的,在相同的写操作负载下,AOF中重写的频率应该少于快照持久化的频率。整体来讲,还是有提高的,当然也与具体的业务逻辑有关系,假如系统不断的增加一些计数器的值,此种情况快照的持久化方式会有明显的优势。

持久化总结
快照持久化的问题在于,持久化效果不佳造成的数据丢失,内存消耗量是数据集的1.x倍,以及持久化速度是否可以更快些。而AOF方式,在持久化效果方面,相对快照来讲有改进;但在DISK IO过度消耗时,会使得整个REDIS系统不稳定;其他方面与快照有着类似的问题。其实,除以上两种直接的办法外,在版本2.4下,还可以利用主从复制结构来进一步解决持久化中仍存在的问题;同时也部分地解决容量限制的问题(但要花钱买多台机器)。关于这些,在“主从复制结构”话题中,再作仔细分析。

具体细节可参加如下网页链接
[1] Official manual: http://redis.io/topics/persistence
[2] Copy-on-write: http://en.wikipedia.org/wiki/Copy-on-write
[3] Mini-version of data type: http://redis.io/topics/memory-optimization
[4] Disk or not to disk? Let us move forward:---chapter one
http://groups.google.com/group/redis-db/browse_thread/thread/823d725335c66a69/8011ea10e97563a2?lnk=gst=disk+or+not+#8011ea10e97563a2
[5] Fork: http://linux.die.net/man/2/fork/
  http://en.wikipedia.org/wiki/Fork_(operating_system)
[6] Fsync: http://linux.die.net/man/2/fsync
[7] redis.conf: https://github.com/antirez/redis/blob/unstable/redis.conf

 

 

欢迎讨论/沟通...

分享到:
评论

相关推荐

    Redis面试题50道(含答案)_.pdf

    36、Redis 持久化数据和缓存怎么做扩容? 37、分布式 Redis 是前期做还是后期规模上来了再做好?为 什么? 38、Twemproxy 是什么? 39、支持一致性哈希的客户端有哪些? 40、Redis 与其他 key-value 存储有什么不同...

    redis面试复习.xmind

    # redis持久化 ### 写了快照和命令行模式的优点缺点 (按道理的话本应该写上快照模式的自动和手动,save和bgsave等等,但是这里掌握的还不是很熟练所以暂未完善) # redis常见并发问题 ### 雪崩,击穿,击穿的概念与解决...

    Redis两种持久化方案RDB和AOF详解

    本文主要针对Redis 有两种持久化方案RDB和AOF做了详细的分析,希望我们整理的内容能够帮助大家对这个两种方案有更加深入的理解。 Redis 有两种持久化方案,RDB (Redis DataBase)和 AOF (Append Only File)。如果...

    redisStudy.zip

    redis 支持rdb和aof两种持久机制,redis4.0后支持混合持久化。rdb是定时的持久机制,宕机有可能会丢失最后一次持久化之后存在数据丢失。aof是基于操作日志追加的持久机制。(基本回答) 加分项: 1.rdb持久化原理 ...

    2023年Redis缓存面试题目汇总

    Redis的AOF(Append Only File)持久化是如何工作的? 你如何理解Redis的复制?它有什么作用? Redis的集群是如何实现高可用的? 你如何保证Redis的安全性? 你如何优化Redis的性能? 在什么情况下你会使用Redis事务...

    第7单元 Redis事务与持久化1

    3、 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句 4、 在事务开启之前,如果客户

    RedisPersistence:Redis的持久性

    Redis持久化一,什么叫持久化?将数据(如内存中的对象)保存到可永久保存的存储设备中。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中,XML数据文件中等等。也可以从应用层和系统层这两个...

    Redis从入门到高可用分布式

    第5章 Redis持久化的取舍和选择 第6章 常见的持久化开发运维问题 第7章 Redis复制的原理与优化 第8章 Redis Sentinel 第9章 初识Redis Cluster 第10章 深入Redis Cluster 第11章 缓存设计与优化 第12章 Redis云平台...

    Redis从入门到高可用视频.zip

    第5章 Redis持久化的取舍和选择 第6章 常见的持久化开发运维问题 第7章 Redis复制的原理与优化 第8章 Redis Sentinel 第9章 初识Redis Cluster 第10章 深入Redis Cluster 第11章 缓存设计与优化 第12章 Redis云平台...

    redis.xmind

    已脑图的方式总结redis,覆盖知识点全面,包含RedisAPI的使用和理解、持久化、主从复制、哨兵模式、分布式集群内容等等

    异步 redis client.rar

    但Redis的其他功能,比如说持久化、异步删除、集群数据同步,其实是开启了额外的线程来完成的。 Redis单线程为什么还能这么快? 因为Redis是基于内存的,所有的运算都是内存级别的,而且单线程避免了多线程的切换...

    redis面试题热门20道以及解析

    redis面试题热门20道以及解析:这些面试题涵盖了Redis的核心概念和关键特性,包括其内存数据库的定位、数据结构、持久化机制、事务处理、高并发和高可用性策略。问题触及了Redis的性能优势、单线程模型、主从复制、...

    Redis入门到精通视频教程

    在本课程中,你将了解Redis是什么、能干什么、如何用,了解NoSQL的使用场景和概念,快速掌握Redis的安装配置、五大数据类型、常用操作命令、Redis持久化、主从复制、事务控制以及用Jedis操作进行Java开发、Redis的高...

    《Redis 6 开发与实战》教学配套PPT.rar

    使用场景及目标:通过《Redis 6 开发与实战》配套PPT,读者可以系统学习Redis的各个方面知识,包括常用数据类型、命令、缓存持久化、集群部署、监控等内容,同时了解如何将Redis与Spring Boot整合应用。目标是帮助...

    2024年20道经典Redis面试常考.zip

    通过深入解析20道经典的Redis面试题,如Redis的数据结构、持久化机制、性能、事务、集群等,本博客旨在帮助求职者更好地准备Redis相关的职位面试。 适用人群: 本博客主要面向Redis初学者,特别是那些即将参加春招的...

    Redis缓存架构实战-基于社区电商场景(资料+视频教程)

    内容覆盖了Redis的基础知识、数据类型、持久化机制,以及如何将Redis高效地应用于社区电商平台以提升性能和用户体验。教程还涉及了缓存策略设计、数据一致性处理、分布式缓存解决方案以及系统监控和调优等高级主题。...

    Springboot如何使用Redis bitmap实现签到功能含完整代码(值得珍藏)

    在实际应用中,我们还需要考虑用户ID的分配策略、位图的更新策略以及签到状态的持久化等问题。 本文详细介绍了Redis Bitmap的原理,以及如何在Spring Boot项目中整合Redis,并实现签到功能。通过示例代码和说明,...

    Redis开发实战2023最新教程-视频教程网盘链接提取码下载.txt

    通过该教程,你将学习如何使用Redis进行高效的数据存储、缓存和消息队列的实现,掌握Redis的常用命令和功能,了解Redis集群和持久化等高级应用。无论是初学者还是有一定经验的开发者,都能从中获得实用的技巧和深入...

    redis-source-code-scenario-analysis:Redis源代码分析-redis source code

    介绍 分析原始码的目的主要有以下几个: ...第4章,主要讲解redis的持久化原理和实现。 第5章,主要讲解redis的植入机制的实现。 第6章,主要讲解redis同步机制的实现。 第7章,主要讲解redis的最佳实

    leetcode下载-JavaTopic:Java面试题总结

    redis持久化的原理(RDB、AOF); redis缓存穿透、缓存雪崩,有没有在实际的工作中遇到过,如果解决缓存雪崩问题; redis的有几种集群方式; redis的基本数据类型(String、List、Hash、Set、ZSet)的使用场景? ...

Global site tag (gtag.js) - Google Analytics