快照方式(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的持久化、主从复制和哨兵架构,开发者可以构建出稳定、高效、容错的Redis集群,为应用程序提供强大的数据存储与访问支持。在实际应用中,根据业务需求和性能指标,灵活选择和调整这些特性,...
首先,理解数据持久化的概念至关重要。简单来说,持久化就是将内存中的数据保存到非易失性存储设备,如硬盘,以便在系统重启后仍能访问这些数据。在数据库系统中,写操作通常包括以下步骤: 1. 客户端发起写请求,...
不过,对于需要高可用性和数据实时性的应用,Redis 还提供了 AOF 持久化机制,通过追加操作日志来确保数据的持久化,这部分内容虽未在文中详细展开,但也是理解 Redis 持久化机制不可或缺的一部分。
本压缩包包含了关于Redis两种主要持久化方式——RDB(Redis Database Backup)和AOF(Append Only File)的详细文档。 一、RDB持久化 RDB是Redis的一种快照持久化方式。它会在特定时间点或满足特定条件时,将当前...
总之,理解并熟练掌握Redis的持久化机制以及开机自启动配置,是确保数据安全性和服务稳定性的重要环节。在实际应用中,应结合业务需求和系统环境,灵活调整相关参数,以实现最佳的性能和可靠性。
Redis 使用内存存储数据,并定期将其持久化到磁盘,以防止数据丢失。其支持多种数据类型,包括字符串、哈希、列表、集合、有序集合等,这为开发人员提供了极大的灵活性。 标题提及的“Redis 可视化工具”是用于帮助...
Redis 是一个高性能的键值对数据存储系统,广泛应用于缓存和数据库领域。...了解并掌握 Redis 的持久化机制对于 Redis 的使用和维护至关重要,尤其在面试中能够体现出你对 Redis 数据安全性的深入理解和实践经验。
- 持久化:Redis提供了RDB(快照)和AOF(日志)两种持久化方式,确保在服务器重启后能够恢复数据。 - 主从复制:通过主从复制,可以实现数据备份和读写分离,提高系统的可用性和性能。 - 事务:Redis支持简单...
Redis是一个高性能的键值存储系统,广泛应用于缓存、消息队列、数据持久化等多个场景。使用图形化客户端可以大大简化对Redis的操作,提高工作效率。例如,`resp-2022.2.0.0.exe`可能是一款名为RESP(Redis Enhanced ...
Redis,全名Remote Dictionary Server,是一款开源的、基于键值对的数据存储系统,广泛应用于缓存、消息队列、数据持久化等多个场景。它以其高性能、低延迟和丰富的数据结构而备受青睐。 在Windows和Linux环境下,...
为了确保数据在服务器重启或异常情况下的安全,Redis提供了两种主要的持久化机制:RDB(Redis Database Persistence,即快照)和AOF(Append Only File,追加日志)。下面将详细解释这两种持久化方式的区别。 **RDB...
Redis 是一款高性能的键值存储系统,常用于缓存、消息队列等场景。本文将深入探讨 Redis 的高级特性,特别是复制架构和哨兵...同时,理解哨兵的工作原理和配置方法对于优化 Redis 集群的稳定性和性能至关重要。
### Redis的两种持久化方案:RDB 和 AOF Redis是一种高性能的键值数据库,它提供了两种主要的数据持久化机制:RDB(Redis Database Backup)和AOF(Append Only File)。这两种方法各有特点,适用于不同的场景。 #...
Redis是一种高性能的键值数据库,常用于数据缓存、消息队列和数据库持久化等多个场景。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,这使得Redis在处理复杂数据操作时表现出色。在这个压缩包中,...
2. **定期备份**:由于Redis默认不持久化所有数据,应定期备份数据,防止意外数据丢失。 3. **资源监控**:密切监控Redis的内存和CPU使用情况,避免资源耗尽导致服务中断。 4. **版本更新**:保持RDM和Redis的版本...
Redis是一款高性能的键值对数据库,常用于数据缓存、消息队列以及持久化存储等场景。为了方便管理和操作Redis数据库,出现了各种可视化工具,其中“Redis Desktop Manager”是一款受到广泛使用的跨平台Redis管理工具...
7. **持久化与复制**:协助用户理解和配置Redis的持久化策略(如RDB和AOF)以及主从复制设置。 8. **性能监控**:提供基本的性能指标,如命令执行速率、内存使用情况等。 **安装与使用** 在提供的文件列表中,`...
如果需要配置,可以编辑`redis.conf`文件,根据需求设置端口、数据持久化、日志等参数。 4. 安装客户端工具:为了进行数据交互,还需要一个客户端工具。可以选择命令行工具`redis-cli.exe`或图形化的可视化工具,如`...
它的核心特性包括支持多种数据结构(如字符串、哈希、列表、集合、有序集合),以及丰富的事务、持久化、复制和LUA脚本功能。由于其快速响应和轻量级的内存存储方式,Redis在Web应用程序、实时分析和分布式系统等...