前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis系列——5.持久化

Redis系列——5.持久化

作者头像
陈琛
发布2020-06-12 16:07:03
3280
发布2020-06-12 16:07:03
举报
文章被收录于专栏:陈琛的Redis文章陈琛的Redis文章

目录

前言

什么是持久化?

持久化的实现

1.快照(RDB:Redis Database)

a. 简介

b.三种触发方式

2.写日志(AOF:Append only file)

a. 简介

b.三种触发条件

c.AOF重写

前言

hello,小可爱们,咱又见面啦。一日不见,如隔三秋。有没有想偶啊?

好了好了,不闹了,咱今天先来提一个问题,redis之所以速度快,是因为将数据放在内存中。众所周知,存放在内存中的速度是快,但是关闭redis,数据即丢,如断电即丢。

那这样肯定是不行的,比如我上线了一个新的项目,前台页面展示用的是redis中的数据,突然断电了,那我数据咋整,用户看不到数据了,那我肯定GG啦。

开发redis的人也不傻,他们写了一个持久化的方案,将内存中的数据写入到硬盘中,这样数据丢不了。

什么是持久化?

redis持久化就是对数据的更新保存在磁盘上,以便数据恢复。

持久化的实现方式

1.快照(RDB)

a

简介

对数据在某时某点的完整备份。

将数据完整的生成一个快照,以二进制格式保存在硬盘中,后缀为.rdb。当需要进行恢复时,再从硬盘加载到内存中。

b

三种触发方式

  • save命令触发方式(同步):新生成一个新的临时文件,当save执行完后,用新的替换老的。 如下图,我们看一下dump.rdb上次更新时间是33分,而当前时间是37分,说明他在之前几分钟刚更新过。

接下来,我们执行save命令,强制使他更新备份文件,如下图,当前时间是38分,而rdb文件的更新时间也是38分。

  • bgsave命令触发方式(异步):与同步相同。
  • 规则自动触发方式(3种触发条件):当某些条件达到时,自动生成rdb文件。

2.写日志(AOF)

a

简介

所有数据更新语句都记录在日志中。

b

三种触发条件

always:让缓冲区的数据及时刷新到硬盘。

everysec:每秒刷新到硬盘,在高写入量下,可以保护硬盘,出现故障可能会丢掉一秒的数据。‘

no:不可控,在不知道啥时候刷新,也不知道丢多少数据。

c

AOF重写

随着时间的流逝,文件越来越大,这主要有两个缺点:一是对服务器的压力越来越大,二是AOF还原数据库状态的时间越来越长。

为了解决这个体积膨胀的问题,redis提供了AOF重写功能,其实也就是创建一个新的AOF文件,新旧文件所保存的数据库状态是相同的,但新文件不会包含任何浪费空间的冗余指令,而且体积比旧文件少很多。

具体是个什么概念呢?我们一起感受一下,如下图,我们先往list的尾部插入A,B,C,D,E,再往删掉A,B,再往尾部插入F,G,最后打印出来,可以知道最终的list为CDEFG。

# 假设服务器对键list执行了以下命令s; 127.0.0.1:6379> RPUSH list "A" "B" (integer) 2 127.0.0.1:6379> RPUSH list "C" (integer) 3 127.0.0.1:6379> RPUSH list "D" "E" (integer) 5 127.0.0.1:6379> LPOP list "A" 127.0.0.1:6379> LPOP list "B" 127.0.0.1:6379> RPUSH list "F" "G" (integer) 5 127.0.0.1:6379> LRANGE list 0 -1 1) "C" 2) "D" 3) "E" 4) "F" 5) "G" 127.0.0.1:6379>

这样AOF中存储的就是6个语句,因为上面一共7个语句,要去掉查询语句。

我们试想一下,如果我一直操作这几个数据,那他会一直存储我的操作语句,这样AOF文件的体积肯定会越来越大,但数据量其实就这么几个值。如果用很大的空间来存储这么小的数据量,那肯定是不合理的。

那AOF的重写并不是对原来的AOF进行读取和分析,而是通过数据库的状态来实现,现在数据库中一个有5个值,其实也要用一个RPUSH list "C" "D" "E" "F" "G"就可以啦。

下面来看一下AOF重写的伪代码,redis的底层是用C写的,且开源的,先立一个flag,之后看redis的源码,emmmm,希望以后不要打脸。

def AOF_REWRITE(tmp_tile_name): f = create(tmp_tile_name) # 遍历所有数据库 for db in redisServer.db: # 如果数据库为空,那么跳过这个数据库 if db.is_empty(): continue # 写入 SELECT 命令,用于切换数据库 f.write_command("SELECT " + db.number) # 遍历所有键 for key in db: # 如果键带有过期时间,并且已经过期,那么跳过这个键 if key.have_expire_time() and key.is_expired(): continue if key.type == String: # 用 SET key value 命令来保存字符串键 value = get_value_from_string(key) f.write_command("SET " + key + value) elif key.type == List: # 用 RPUSH key item1 item2 ... itemN 命令来保存列表键 item1, item2, ..., itemN = get_item_from_list(key) f.write_command("RPUSH " + key + item1 + item2 + ... + itemN) elif key.type == Set: # 用 SADD key member1 member2 ... memberN 命令来保存集合键 member1, member2, ..., memberN = get_member_from_set(key) f.write_command("SADD " + key + member1 + member2 + ... + memberN) elif key.type == Hash: # 用 HMSET key field1 value1 field2 value2 ... fieldN valueN 命令来保存哈希键 field1, value1, field2, value2, ..., fieldN, valueN =\ get_field_and_value_from_hash(key) f.write_command("HMSET " + key + field1 + value1 + field2 + value2 +\ ... + fieldN + valueN) elif key.type == SortedSet: # 用 ZADD key score1 member1 score2 member2 ... scoreN memberN # 命令来保存有序集键 score1, member1, score2, member2, ..., scoreN, memberN = \ get_score_and_member_from_sorted_set(key) f.write_command("ZADD " + key + score1 + member1 + score2 + member2 +\ ... + scoreN + memberN) else: raise_type_error() # 如果键带有过期时间,那么用 EXPIREAT key time 命令来保存键的过期时间 if key.have_expire_time(): f.write_command("EXPIREAT " + key + key.expire_time_in_unix_timestamp()) # 关闭文件 f.close()

接下来,我们看一下哪些条件下会触发AOF的重写功能?

  • 显式手动触发 redis客户端向Redis发bgrewriteaof命令,redis服务端fork一个子进程去完成AOF重写。这里的AOF重写,是将Redis内存中的数据进行一次回溯,回溯成AOF文件。而不是重写AOF文件生成新的AOF文件去替换。
  • AOF重写配置自动触发 auto-aof-rewrite-min-size:AOF文件重写需要的尺寸 auto-aof-rewrite-percentage:AOF文件增长率 redis提供了aof_current_size和aof_base_size,分别用来统计AOF当前尺寸(单位:字节)和AOF上次启动和重写的尺寸(单位:字节)。 AOF自动重写的触发时机,同时满足以下两点: aof_current_size > auto-aof-rewrite-min-size aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage

这边具体的演示就不写了,其实就是能看出在重写之后,AOF文件的大小比之前小了很多,去除了很多冗余命令。

那我们来想一个问题,因为redis是单线程工作的,那么如果在我手动重写AOF的时候,又有新的命令写入,这个时候怎么办?

很明显,如果我在重写的时候,又要新的命令写入,这个时候是不能写入的,redis也就失去了功能。这肯定不是我们想看到的。

那redis将重写程序放在了子进程中,这样的好处主要有两点:

  • 子进程进程AOF重写的时候,主进程可以继续处理命令要求
  • 子进程带有主进程的数据副本,使用子进程而是线程。

解决了这个问题,新的问题又来了。so,坑是永远填不完的,emmmm,相信偶。

新的问题就是现在是重写的时候,redis可以执行新的命令,那么在重写结束之后,重写的AOF文件与原来的AOF文件对数据库状态的描述是不一样的,因为在重写期间,redis又接受了新的命令,执行了新的命令。

所以redis添加了AOF重写缓存的概念,在重写期间,redis在执行新的命令之后,将命令添加到原来的AOF文件中,同时也将命令添加到AOF重写缓存中,这样在完成重写工作后,再将AOF重写缓存中的命令添加到新的AOF文件中。最后将新的AOF文件重命名,覆盖原来的AOF文件。

这样就解决了数据库不一样的情况,至此,AOF重写完毕。

哎啊,累死了,redis的持久化终于结束了,历经了好几天的晚上,终于把他整理完毕了。

答应偶,一定要看,好吗?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 学习Java的小姐姐 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 三种触发方式
  • 简介
  • 三种触发条件
  • AOF重写
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档