### 3.1.3 数据库键空间(key space)
`server.h/redisDb`结构中的dict称为数据库键空间,保存了数据库的所有键值对,键为字符串对象,值为任意对象
```c
typedef struct redisDb {
dict *dict; // 数据库键空间,保存所有键值对,键为字符串对象,值为任意对象
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
```
键空间的增删改查都同dict操作,此外还有:
- 清空键空间:`FlushDb`
- 从键空间随机返回一个键:`RandomKey`
- 返回数据库键数量:`DbSize`
- 还有`Exists`、`Rename`、`Keys`等
----
读写键空间时的维护操作如下:
- 读取一个键后(读写操作都会对键进行读取),服务器会根据键是否存在,更新`hit`(命中)和`miss`(未命中),这两个值可以通过`info status`命令查看
- 在读取一个键后,服务器会更新键的`lru`(最后一次更新时间)属性,可以通过`object idleime <key>`命令查看某个键的闲置时间
- 读取一个键时,如果发现其已过期,会先删除再进行其他操作
- 每次修改一个键后,会对`dirty`(脏计数器)加1,这个计数器会触发服务器的持久化以及复制操作
- 如果开启了服务器通知功能,在对键进行修改之后,服务器将按照配置发送相应的数据库通知
----
设置键的生存时间和过期时间:
- 通过`Expire`和`PExpire`,客户端能以秒或毫秒的精度为数据库中的某个键设置`ttl`(Time To Live,生存时间),服务器会自动删除生存时间为0的键
- 与此类似,可以通过`ExpireAt`和`PExpireAt`设置过期时间,过期时间是一个UNIX时间戳,当过期时间来临时,服务器会自动删除该键。
- `ttl`和`pttl`接收一个带有生存时间或过期时间的键,返回这个键的剩余生存时间。
- `setex` 命令可以再设置一个字符串键的同时为键设置过期时间,这是一个类型限定的命令,其原理与`Expire`完全一样。
其中,`EXPIRE`命令将转成`PEXPIRE`命令,`PEXPIRE`和`EXPIREAT`都将转成`PEXPIREAT`命令。
----
redisDb结构的expires字典保存了数据库中所有键的过期时间,称为`过期字典`
- 过期字典的键是一个指针,指向键空间的某个对象
- 过期字典的值是一个`long long`类型的整数,保存了过期时间(毫秒精度的UNIX时间戳)
----
`PPERSIST`命令可以移除一个键的过期时间,如`PERSIST <key>`,底层是将字典中的key移除。
----
过期时间的判定:
- 检查给定键是否存在于过期字典,如果存在则取得其过期时间
- 检查当前UNIX时间戳是否大于键的过期时间:如果是的话,那么键已过期,否则的话,键未过期。
----
过期键的删除策略:
- 定时删除:在设置键的过期时间的同时,创建一个定时器(Timer),让定时器在键的过期时间来临时,立即执行对键的删除操作。
- 对内存最友好,对CPU最不友好
- 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除键。
- 对CPU最友好,对内存最不友好
- 由`db.c/expireIfNeed`函数实现
- 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键,具体由算法决定。
- 难点是确定删除操作执行的时长和频率
- 由`server.c/activeExpireCycle`函数实现
- 空白目录
- 精简版Spring的实现
- 0 前言
- 1 注册和获取bean
- 2 抽象工厂实例化bean
- 3 注入bean属性
- 4 通过XML配置beanFactory
- 5 将bean注入到bean
- 6 加入应用程序上下文
- 7 JDK动态代理实现的方法拦截器
- 8 加入切入点和aspectj
- 9 自动创建AOP代理
- Redis原理
- 1 Redis简介与构建
- 1.1 什么是Redis
- 1.2 构建Redis
- 1.3 源码结构
- 2 Redis数据结构与对象
- 2.1 简单动态字符串
- 2.1.1 sds的结构
- 2.1.2 sds与C字符串的区别
- 2.1.3 sds主要操作的API
- 2.2 双向链表
- 2.2.1 adlist的结构
- 2.2.2 adlist和listNode的API
- 2.3 字典
- 2.3.1 字典的结构
- 2.3.2 哈希算法
- 2.3.3 解决键冲突
- 2.3.4 rehash
- 2.3.5 字典的API
- 2.4 跳跃表
- 2.4.1 跳跃表的结构
- 2.4.2 跳跃表的API
- 2.5 整数集合
- 2.5.1 整数集合的结构
- 2.5.2 整数集合的API
- 2.6 压缩列表
- 2.6.1 压缩列表的结构
- 2.6.2 压缩列表结点的结构
- 2.6.3 连锁更新
- 2.6.4 压缩列表API
- 2.7 对象
- 2.7.1 类型
- 2.7.2 编码和底层实现
- 2.7.3 字符串对象
- 2.7.4 列表对象
- 2.7.5 哈希对象
- 2.7.6 集合对象
- 2.7.7 有序集合对象
- 2.7.8 类型检查与命令多态
- 2.7.9 内存回收
- 2.7.10 对象共享
- 2.7.11 对象空转时长
- 3 单机数据库的实现
- 3.1 数据库
- 3.1.1 服务端中的数据库
- 3.1.2 切换数据库
- 3.1.3 数据库键空间
- 3.1.4 过期键的处理
- 3.1.5 数据库通知
- 3.2 RDB持久化
- 操作系统
- 2021-01-08 Linux I/O 操作
- 2021-03-01 Linux 进程控制
- 2021-03-01 Linux 进程通信
- 2021-06-11 Linux 性能优化
- 2021-06-18 性能指标
- 2022-05-05 Android 系统源码阅读笔记
- Java基础
- 2020-07-18 Java 前端编译与优化
- 2020-07-28 Java 虚拟机类加载机制
- 2020-09-11 Java 语法规则
- 2020-09-28 Java 虚拟机字节码执行引擎
- 2020-11-09 class 文件结构
- 2020-12-08 Java 内存模型
- 2021-09-06 Java 并发包
- 代码性能
- 2020-12-03 Java 字符串代码性能
- 2021-01-02 ASM 运行时增强技术
- 理解Unsafe
- Java 8
- 1 行为参数化
- 1.1 行为参数化的实现原理
- 1.2 Java 8中的行为参数化
- 1.3 行为参数化 - 排序
- 1.4 行为参数化 - 线程
- 1.5 泛型实现的行为参数化
- 1.6 小结
- 2 Lambda表达式
- 2.1 Lambda表达式的组成
- 2.2 函数式接口
- 2.2.1 Predicate
- 2.2.2 Consumer
- 2.2.3 Function
- 2.2.4 函数式接口列表
- 2.3 方法引用
- 2.3.1 方法引用的类别
- 2.3.2 构造函数引用
- 2.4 复合方法
- 2.4.1 Comparator复合
- 2.4.2 Predicate复合
- 2.4.3 Function复合
- 3 流处理
- 3.1 流简介
- 3.1.1 流的定义
- 3.1.2 流的特点
- 3.2 流操作
- 3.2.1 中间操作
- 3.2.2 终端操作
- 3.3.3 构建流
- 3.3 流API
- 3.3.1 flatMap的用法
- 3.3.2 reduce的用法
- 3.4 collect操作
- 3.4.1 collect示例
- 3.4.2 Collector接口