1. 数据库
数据结构
/* 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 */
// 键的过期时间,字典的键为键,字典的值为过期事件 UNIX 时间戳
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 */
// 正在被 WATCH 命令监视的键
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */
// 数据库号码
int id; /* Database ID */
// 数据库的键的平均 TTL ,统计信息
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
```
### 切换数据库
默认情况下,Redis客户端的目标数据库为0号数据库,但是客户端可以通过SELECT n来切换目标数据库。
### 数据库键空间
键空间的键其实就是数据库的键。
如:
SET message “hello world”
RPUSH alphabet “a” “b” “c”
HSET book name “Redis in Action”
HSET book name “Redis in Action”
HSET book name “Redis in Action”
![image](C:\Users\Administrator\Pictures\键空间.png)
dict是一个键空间,alpahbet是一个列表键,book是一个哈希表键,message是一个字符串键。
### 数据库的增删改查
以list对象作为例子:
- 添加:rpush key value
- 删除:pop key
- 查找:lindex key idx
- 修改:lset key value
- 长度:llen
### 过期键的设置和删除
PEXPIREAT x n
PERSIST x n
“`
过期键有三种删除策略:
- 定时删除:设置定时器,这样很明显会占cpu资源,但是对内存友好。
- 惰性删除:下次拿出开始,检查到键过期则删除,这样显然对内存不友好对cpu友好。
- 定期删除:一种介于两者之间的策略。
-
AOF,RDB和复制功能对过期键的处理
RDB保存数据
- 生成RDB文件:通过SAVA命令或者BGSAVE可以创建一个RDB文件。过期的键不会保存到RDB文件中。
- 载入RDB文件:如果服务器是主服务器那么未过期的键会被载入到数据库中,而过期的就会被忽略。从服务器则无论时候过期都会载入,然后主服务器进行数据同步时会清空从服务器中的数据。
AOF文件写入
- aof文件主要是记录数据写入修改删除操作
- 当过期键被删除的时候,程序就会向aop文件
追加一条DEL命令来显示记录该键已经被删除。
复制
- 主服务器,在删除一个过期的键之后,会显式地向所有的从服务器发送一个DEL来通知从服务器删除这个过期键。
- 从服务器只有收到主服务器发来的DEL命令之后,才会删除过期键,否则像处理未过期的键一样处理。
2.RDB持久化
概述:Redis可以将数据库状态保存为RDB文件放在磁盘,服务器停机的情况下可以用RDB来还原数据库状态。
RDB的创建和载入
- 创建有两种方式SAVE和BGSAVE,区别是SAVE阻塞,二BGSAVE非阻塞,这一点从伪代码可以看出来。
- 载入的时机是服务器启动时自动执行的,但是前提是要关闭AOF的持久化功能。
RDB的文件结构
- REDIS部分长度为5字节,保存着REDIS这五个字符。
- db_version记录着RDB的版本号。
- databases部分包含着多个数据库。
- EOF常量的长度是1字节,标志着RDF文件内容结束。
- check_sum是检验和。
RDB的文件的databases部分的结构
- SELECTDB的长度是1字节,标志位。
- db_number保存着一个数据库号码。
- key_value_pairs保存了数据库中所有键值对数据。
databases部分的key_value_pairs部分
然后每种类型的value又有不同的数据结构:
3.AOF的持久化
AOF持久化与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。
AOF的持久化分为三个步骤:
- 命令追加:将命令追加到aof_buf缓冲区中
- 写入文件:将其写入aof文件
- 文件同步:对aof文件进行同步
文件的写入和同步可以由以下伪代码表示:
如图flushApendOnlyFile函数由appnedfsync这个值来决定同步的时机。
- always代表将缓存区的所有内容写入同步。
- everysec表示超过一秒钟就对aof进行同步。
- no表示只写入而不同步。
AOF的重写
aof的重写是为了解决写命令冗余导致aof文件膨胀的问题,redis的解决方法对aof文件进行重写。
重写的方法并不是对比前后两个版本的aof文件,而是直接对比两个版本的数据库,直接根据数据的不同重写aof文件。
这样就产生了一个新的问题,重写程序aof_rewrite函数可以创建一个新的AOF文件,但是这个函数进行大量的写入操作,而且因为redis是单线程,所以会长时间阻塞!
为了解决这个问题,Redis把重写操作放到了子进程中,让父进程继续去处理命令请求。但是又有一个新的问题,在子进程进行AOF重写阶段,服务器还要继续处理命令请求,而新的命令可能对数据库进行修改导致重写后的AOF文件的状态不一致。
为了解决这个问题,Redis设置了一个AOF重写缓存区,这个缓存区在服务器创建子进程的时候使用,所以在子进程重写阶段,服务器只是多了一个步骤,将写命令同时追加到AOF重写缓存区中,当子进程对AOF重写完成后,将重写缓存区写入到新的AOF文件中。
4.事件
文件事件
Reids的文件事件处理器是基于Reactor模式的,由四个部分组成
三个步骤:
- 连接应答处理器
当Redis服务器进行初始化的时候,程序会将这个连接应答处理器和服务器监听套接字的AE_READABLE事件关联;当客户端连接服务器监听套接字的时候,套接字就会产生AE_READABLE事件,引发连接应答处理器执行相应操作。
![image](连接)
命令请求处理器
连接成功后,服务器会将客户端套接字的AE_READABBLE事件和命令请求处理器关联起来,当客户端向服务器发送命令请求的时候,套接字就会产生AE_READABLE事件,引发命令请求处理器执行。
命令回复处理器
当服务器有命令回复客户端时,服务端将客户端套接字的AE_WRIABLE关联起来,当客户端准备好接收服务器传回的命令回复的时候,就会产生AE_WRITABLE事件,引发命令回复处理器执行。
如果大家不能理解,建议去看一下Linux的网络通信模型。
时间事件
时间事件分两种:定时事件和周期性事件。
实现:服务器将所有的时间事件放在一个无序链表中,每当时间事件执行器运行时,它就遍历整个链表,查找所有已到达的时间事件,并调用相应的事件执行器。