Redis学习之单机数据库(二)

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非阻塞,这一点从伪代码可以看出来。

image

  • 载入的时机是服务器启动时自动执行的,但是前提是要关闭AOF的持久化功能。

RDB的文件结构

image

  • REDIS部分长度为5字节,保存着REDIS这五个字符。
  • db_version记录着RDB的版本号。
  • databases部分包含着多个数据库。
  • EOF常量的长度是1字节,标志着RDF文件内容结束。
  • check_sum是检验和。

RDB的文件的databases部分的结构

image

  • SELECTDB的长度是1字节,标志位。
  • db_number保存着一个数据库号码。
  • key_value_pairs保存了数据库中所有键值对数据。

databases部分的key_value_pairs部分

image

然后每种类型的value又有不同的数据结构:

image

3.AOF的持久化

AOF持久化与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。

AOF的持久化分为三个步骤:

  • 命令追加:将命令追加到aof_buf缓冲区中
  • 写入文件:将其写入aof文件
  • 文件同步:对aof文件进行同步

文件的写入和同步可以由以下伪代码表示:
image

如图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模式的,由四个部分组成

image

三个步骤:
- 连接应答处理器

 当Redis服务器进行初始化的时候,程序会将这个连接应答处理器和服务器监听套接字的AE_READABLE事件关联;当客户端连接服务器监听套接字的时候,套接字就会产生AE_READABLE事件,引发连接应答处理器执行相应操作。

![image](连接)
  • 命令请求处理器

    连接成功后,服务器会将客户端套接字的AE_READABBLE事件和命令请求处理器关联起来,当客户端向服务器发送命令请求的时候,套接字就会产生AE_READABLE事件,引发命令请求处理器执行。

    image

  • 命令回复处理器

    当服务器有命令回复客户端时,服务端将客户端套接字的AE_WRIABLE关联起来,当客户端准备好接收服务器传回的命令回复的时候,就会产生AE_WRITABLE事件,引发命令回复处理器执行。

    image

如果大家不能理解,建议去看一下Linux的网络通信模型。

时间事件

时间事件分两种:定时事件和周期性事件。

实现:服务器将所有的时间事件放在一个无序链表中,每当时间事件执行器运行时,它就遍历整个链表,查找所有已到达的时间事件,并调用相应的事件执行器。

image

猜你喜欢

转载自blog.csdn.net/qq_33394088/article/details/80549135
今日推荐