数据库结构体
typedef struct redisDb {
// ...
//数据库id,默认一共16个
int id;
// 数据库键空间,保存着数据库中的所有键值对
dict *dict;
// 保存键的过期时间
dict *expires;
// ...
} redisDb;
存储对象用一个大字典。
数据库dict
和expires
都是使用指针指向同一个对象,所以不存在数据冗余的情况,此处好画图而已。
过期键删除策略
- 定时删除:清内存,但是耗费CPU时间
- 惰性删除:性能高,但是浪费内存
- 定期删除:隔一段时间删除一次,并且限制执行时长和频率,折中办法
RDB
持久化
BGSAVE
抛出子进程进行数据库数据持久化,由于写时复制的存在所以存储的数据具有时点性。
save 900 1 # 900s 进行1次修改
save 300 10 # 300s 进行10次修改
save 60 10000 # 60s 进行一万次修改就会自动触发 BGSAVE
dirty + lastsave
struct redisServer{
long long dirty; //上次保存之后修改的次数
time_t lastsave; //上次保存的时间
}
AOF
持久化
struct redisServer {
// ...
// AOF 缓冲区
sds aof_buf;
// ...
};
根据刷盘的配置,把每次执行完的命令/每分钟执行的命令/每秒执行的命令都追加到服务器状态的 aof_buf
缓冲区的末尾
RDB + AOF
综合二者,可以实现在BGSAVE
执行期间的操作命令通过缓冲区记录下来,然后再追加到RDB
文件末尾,这样的持久化文件效率才是最高的,也相对完整的
网络IO等事件处理
epoll / kqueue
epoll_wait
的阻塞时长由马上就要到时间的定时事件决定。
事务
语法错误会提交失败
set a "this is a"
lpop a b c d # 语法错误, lpop只能接一个变量
set b "this is b"
逻辑错误,还是会继续执行事务,没有回滚操作
set a "this is a"
lpop a # 逻辑错误, lpop 后边是一个字符串,无法pop
set b "this is b"