redis的持久化方式和具体实现

●主要是9-11章的内容

9 数据库

数据库实现,服务器保存数据库、客户端切换数据库、保存键值对、针对数据库的添加、删除、查看、更新等。服务器保存键的过期时间的方法、自动删除。

9.1 服务器中的数据库

●服务器状态redis.h/redisServer结构的db数组中。db数组的每个项都是一个redis.h/redisDb结构。redisDb结构代表一个数据库
● 初始化服务器时,程序会根据服务器状态的dbnum(默认16)属性来决定创建多少数据库
struct redisServer {
// 数据库数组
redisDb *db;
// 服务器的数据库数量
int dbnum;

client
●实用:切换数据库。默认是0,通过select num 来切换数据库
●底层:redisClient.db(客户端记录当前目标数据库的指针)指针指向redisServer.db数组中的其中一个元素
●attention:执行像flushdb这样的危险命令(为啥,flushdb会做什么?)前,先执行select命令。

127.0.0.1:6379> set msg “hello world”
OK
127.0.0.1:6379> get msg
“hello world”
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> get msg
(nil)
127.0.0.1:6379[2]> set msg “another world”
OK
127.0.0.1:6379[2]> get msg
“another world”
127.0.0.1:6379[2]> select 0
OK
127.0.0.1:6379> get msg
“hello world”
127.0.0.1:6379>

键空间
Q:什么是键空间?
A:redisDb结构的dict字典保存了数据库中的所有键值对,这个字典被称之为键空间(key space)
/* Redis database representation. There are multiple databases identified

  • by integers from 0 (the default database) up to the max configured

  • database. The database number is the ‘id’ field in the structure. */
    typedef struct redisDb {

    // 数据库键空间,保存着数据库中的所有键值对
    dict dict; / The keyspace for this DB */

●数据库中的键就是键空间的键,每一个键都是一个字符串对象
●数据库中的值就是键空间的值,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象
数据库的crud、也就是键空间的crud
●添加、删除、更新键值都是对键空间的操作。
●读写键空间的维护操作
○读取时会更新hit和miss这两个值,在info stats命令中的keyspace_hits和keyspace_misses属性中查看
○读取一个键后,会更新键的LRU(latest recently used),计算键的闲置时间,使用object idletime key命令查看key的闲置时间(单位s)
○读取时发现过期,会先删除再执行
○客户端使用watch命令监视某个键时,如果该key被修改后,这个key会被标记为dirty,从而让事务程序注意到这个键已经被修改
○每次修改一个键后,都会对dirty键计算器值+1,这个计数器会触发服务器持久化和复制操作

9.4 设置键的生存空间或过期时间

●命令解释:
○expire命令:ttl(s)
○pexpire:ttl(ms)
○expireat:时间戳(s)
○pexpireat:时间戳(ms)
○persist:移除一个键的过期时间
○ttl :查看还有多久被删除
127.0.0.1:6379> expire msg 5
(integer) 1
127.0.0.1:6379> expireat msg 1675596008
(integer) 1
127.0.0.1:6379> get msg
“1”
127.0.0.1:6379> ttl msg
(integer) 9851
127.0.0.1:6379> persist msg # 持久化这个key
(integer) 1
127.0.0.1:6379> ttl msg
(integer) -1

●过期时间
○redisDb结构中的expires字典保存了所有键的过期时间,这个字典为过期字典
○键是个指针,指向键空间中的某个键对象(某个数据库键)
○long long类型的整数,存的是过期的时间戳
○执行这些对key生存时间的操作时,会更改expires里值
○执行persist后,该key会在expires字典中删除

9.5 过期键删除策略

这个比较好玩 应该
●定时删除
○内存友好,cpu不友好
○创建一个定时器需要用到redis服务器中的时间事件(实现方式:无序链表),查找一个事件的时间复杂度o(n)
●惰性删除
○cpu友好,读取时才会去删除,内存不友好,如果有大量过期键,一直不被访问,会一直不被删除,除非手动执行flushdb,可以看作是一种内存泄漏
●定期删除
○上面两种的折中
○难点在于控制频率。为啥时长会受影响?
●redis服务器使用惰性和定期删除两种策略
○定期删除
■ 周期性操作serverCron函数执行时,activeExpireCycle函数就会被调用。规定时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并做删除动作
■定量

9.7 AOF 、RDB和复制功能对过期键的处理

●RDB(redis database)
○快照模式
○主从有区别,但是最终一致
●AOF(append only file)
○追加
○过期键对主从没影响
●复制
○过期键由主服务器控制删除
○从服务器听主命令

9.8 数据库通知

客户端通过订阅给定的频道或者模式,来获取数据库中键的变化
键空间通知
键事件通知

10 RDB 持久化

避免存在内存的数据因为服务器重启丢失,会有持久化功能,保存在磁盘里。可以手动执行,也可以根据服务器配置选项定期执行。
RDB持久化生成的RDB文件是一个经过压缩的二进制文件。通过该文件可以还原生成 RDB文件时的数据库状态。

10.1 RDB文件的创建与载入

● save命令
○进程阻塞
○执行save时候其他命令会被拒绝
●bgsave命令
○派生子进程,由子进程负责创建rdb文件
○执行时,save命令会被拒绝;bgsave命令会被拒绝,bgrewriteaof命令也被拒绝(性能考虑)
●文件载入
○时机:服务器启动时自动执行
○aof文件更新频率会比rdb文件高,如果服务器开启了aof,优先使用aof还原
○服务器状态:阻塞

10.2 自动间隔性保存

save 900 1 # 900s内对数据库至少进行了1次修改
save 300 10 # 300s内对数据库至少进行了10次修改

通过dirty(服务器内键被修改的次数)字段和lastsave(上一次save的时间,单位s)字段实现自动间隔性保存。
服务器周期性操作函数serverCron(服务器周期性操作函数)默认100ms会执行一次,其中就会检查save选项所设置的保存条件是否已经满足。

10.3 RDB文件结构

●REDIS:用来判断是否为rdb文件,rdb文件是二进制数据,REDIS符号代表的是’R’、‘E’、‘D’、‘I’、‘S’五个字符,没有’\0’结尾符号。
●db_version:rdb文件的版本
●databases:零个或者任意多个数据库,以及各个数据库中的键值对数据
●EOF:常量,标识rdb文件正文内容的结束。读的这个字符时候表示都已经载入完成了
●check_num:校验和,8字节长,无符号整数。校验前四部分,载入时也会计算一边,看和记录的是否相同,用来判断文件是否损坏。

10.3.1 database 部分

●SELECTDB:标识
●db_number:数据库编号
●key_value_pairs:所有的键值对数据。键空间&过期字典
○type、key、value组成
○带过期时间的expiretime_ms:标识,ms:键值对的过期时间

10.3.3 value的编码

● 字符串:区分是整数还是字符串
○整数:encoding存integer,常量表示8、16、32编码存储位数,实际值
○字符串:小于等于20字节,直接原样保存;大于20字节,压缩后保存


● 列表

○长度+项。每一项里也会先记录长度&实际内容
● 集合对象
○size+项目
●哈希表对象
○hash_size
○key_value_pair:key和value挨在一起
●有序集合
○sorted_set_size
○element:member和score两部分,member均由size和实际数据表示
●intset编码集合
○转为字符串对象,再存到rdb文件里
●ziplist编码的列表、哈希表或者有序文件
○value存的是一个压缩列表
○根据type来还原

10.4 分析rdb文件

od -c dump.rdb # 但是失败了,没找到原因

11. AOF持久化

rdb:通过保存数据库中的键值对来记录数据库状态
aof:通过保存redis执行的写命令来记录数据库状态的
aof文件的所有命令都是以redis的命令请求协议格式来保存的。redis的命令请求协议是纯文本格式,aof文件里的内容也是。
为啥会有这么多转义字符 \r\n
服务器启动时,通过载入和执行AOF文件中保存的命令来还原服务器关闭之前的数据库状态

11.1 AOF持久化的实现

●命令追加(append)
○写命令被执行完后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区末尾
●文件写入
○事件循环:文件事件(负责和客户端通信)、时间事件(执行定时运行的函数)
○服务器每结束一个时间循环之前,会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区的内容写入和保存到aof文件里
●文件同步(sync)
○appendFsync值表示不同的同步方式
○always(写入并同步到aof)、everysec(写入aof,如果上次同步时间超过1s,再同步)、no(只写,不同步)
○不同方式时长和安全性不一样

11.2 AOF文件的载入与数据还原

● 读取aof文件里的保存的写命令,并且重新执行一边aof里保存的写命令即可。
●伪客户端,无网络连接,其余和客户端一样

11.3 AOF重写

aof文件内容会越来越多,可能会对redis服务器、宿主机造成影响,且还原时间长
rewrite功能:类似文件合并
实现
●通过读取当前数据状态来实现,用一条命令来代替保存在aof文件中的六条命令(应该是所有对这个键的操作)
●重写时会检查键所包含的元素数量,如果元素数量超过了设置的常量值,将使用多条命令来记录键的值,而不是单单使用一条命令。主要是为了避免命令在执行时造成输入缓冲区溢出
●子进程来执行rewrite,避免执行aof文件写入时有命令到服务器导致数据不一致,会在子进程创建后,当redis每执行完一个命令后,同时将这个写命令发送给aof缓冲区和aof重写缓冲区。

你可能感兴趣的:(redis,redis,数据库,java)