关于redis:当你对-redis-说你中意的女孩是-Mia

0次阅读

共计 4317 个字符,预计需要花费 11 分钟才能阅读完成。

作者:京东科技 周新智

一、Redis

家喻户晓,Redis = Remote Dictionary Server,即近程字典服务。

是一个开源的应用 ANSI C 语言编写、反对网络、可基于内存亦可长久化的日志型、Key-Value 数据库,并提供多种语言的 API。

二、当你对 redis 说你中意的女孩是 Mia 时

1、set myLove Mia

redis 会将 key:myLove value:Mia

包装成一个 dictEntry 对象、一个 redisObject 对象,如下图所示:

dictEntry:家喻户晓,Redis 是 Key-Value 数据库,因而对每个键值对都会有一个 dictEntry,外面存储了指向 Key 和 Value 的指针;next 指向下一个 dictEntry,与本 Key-Value 无关。

Key:图中右上角可见,Key(”myLove”)并不是间接以字符串存储,而是存储在 SDS 构造中。

redisObject:Value(“Mia”)既不是间接以字符串存储,也不是像 Key 一样间接存储在 SDS 中,而是存储在 redisObject 中。实际上,不管 Value 是 5 种类型的哪一种,都是通过 redisObject 来存储的;而 redisObject 中的 type 字段指明了 Value 对象的类型,ptr 字段则指向对象所在的地址。不过能够看出,字符串对象尽管通过了 redisObject 的包装,但依然须要通过 SDS 存储。

1.1、对 myLove 进行对象封装

1.1.1、dictEntry

redis 外部整体的存储构造是一个大的 hashmap,外部是数组实现的 hash,key 抵触通过挂链表去实现,每个 dictEntry 为一个 key/value 对象,value 为定义的 redisObject。

结构图如下:

dictEntry 是存储 key->value 的中央,再让咱们看一下 dictEntry 构造体

/*
 * 字典
 */
typedef struct dictEntry {
    // 键
    void *key;
    // 值
    union {
        // 指向具体 redisObject
        void *val;
        // 
        uint64_t u64;
        int64_t s64;
    } v;
    // 指向下个哈希表节点,造成链表
    struct dictEntry *next;
} dictEntry;
1.1.2、对象封装 redisObject

咱们接着再往下看 redisObject 到底是什么构造的

/*
 * Redis 对象
 */
typedef struct redisObject {
    // 类型 4bits
    unsigned type:4;
    // 编码方式 4bits
    unsigned encoding:4;
    // LRU 工夫(绝对于 server.lruclock)24bits
    unsigned lru:22;
    // 援用计数 Redis 外面的数据能够通过援用计数进行共享 32bits
    int refcount;
    // 指向对象的值 64-bit
    void *ptr;
} robj;

*ptr 指向具体的数据结构的地址;type 示意该对象的类型,即 String,List,Hash,Set,Zset 中的一个,但为了进步存储效率与程序执行效率,每种对象的底层数据结构实现都可能不止一种,encoding 示意对象底层所应用的编码。

redis 对象底层的八种数据结构:

 REDIS_ENCODING_INT(long 类型的整数)REDIS_ENCODING_EMBSTR embstr(编码的简略动静字符串)REDIS_ENCODING_RAW(简略动静字符串)REDIS_ENCODING_HT(字典)REDIS_ENCODING_LINKEDLIST(双端链表)REDIS_ENCODING_ZIPLIST(压缩列表)REDIS_ENCODING_INTSET(整数汇合)REDIS_ENCODING_SKIPLIST(跳跃表和字典)

查看 redisObject 详细信息:

# 查看 key 对应 value 的 redisObject 类型
type key
    type myLove
    
# 查看 key 对应 value 的 redisObject 详细信息
debug object key
     debug object myLove

value 为 string、int 类型是 redisObject 中的 type、encoding 不同表现形式

Value 为 string 类型时:

Value 为 int 类型时:

以上两种不同 value 类型,type 雷同,encoding 不同

1.2、对 myLove 进行长久化

1.2.1、rdb 文件写入

1.2.2、aof 缓存写入 文件保留

默认状况下 没有开启 AOF (append only file)

开启 AOF 长久化后,每执行一条会更改 redis 数据的命令,redis 就会将写入、批改、删除命令写入到硬盘中的 AOF 文件(当然并不是立刻写入文件,而是立刻写入 aof 缓存中,再依据 aof 配置的数据长久化条件进行写入),这一过程显然会升高 redis 的性能,但大部分状况下这个影响是可能承受的,

另外应用快的硬盘能够进步 AOF 的性能。

配置 redis.conf

# 能够通过批改 redis.conf 配置文件中的 appendonly 参数开启 
    appendonly yes
# AOF 文件的保留地位和 RDB 文件的地位雷同,都是通过 dir 参数设置的。dir ./
# 默认的文件名是 appendonly.aof,能够通过 appendfilename 参数批改 appendfilename appendonly.aof

AOF 文件中存储的是 redis 的命令 原理

Redis 将所有对数据库进行过写入的命令 (及其参数) 记录到 AOF 文件,以此达到记录数据库状态的 目标,为了不便起见,咱们称说这种记录过程为同步。

同步命令到 AOF 文件的整个过程能够分为三个阶段:

命令流传:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。缓存追 加:AOF 程序依据接管到的命令数据,将命令转换为网络通讯协定 RESP 的格局,而后将协定内容追加到服务器的 AOF 缓存中。文件写入和保留: AOF 缓存中的内容被写入到 AOF 文件开端,如果设定的 AOF 保留条件被满足的话,fsync 函数或者 fdatasync 函数会被调用,将写入的内容真正地保留到磁盘中。

命令流传:

当一个 Redis 客户端须要执行命令时,它通过网络连接,将协定文本发送给 Redis 服务器。服务器在 接到客户端的申请之后,它会依据协定文本的内容,抉择适当的命令函数,并将各个参数从字符串文 本转换为 Redis 字符串对象(StringObject)。每当命令函数胜利执行之后,命令参数都会被流传到 AOF 程序。

缓存追加:

当命令被流传到 AOF 程序之后,程序会依据命令以及命令的参数,将命令从字符串对象转换回原来的 协定文本。协定文本生成之后,它会被追加到 redis.h/redisServer 构造的 aof_buf 开端。

redisServer 构造维持着 Redis 服务器的状态,aof_buf 域则保留着所有期待写入到 AOF 文件的协 议文本。

RESP 协定:

Redis 客户端应用 RESP(Redis 的序列化协定)协定与 Redis 的服务器端进行通信。尽管该协定是专门为 Redis 设计的,然而该协定也能够用于其余 客户端 - 服务器 (Client-Server)软件我的项目。

能够通过特殊符号来辨别出数据的类型:

单行回复:以 + 号结尾。

谬误回复:以 - 号结尾。

整数回复:以: 号结尾。

批量回复:以 $ 号结尾。

多条批量回复:以 * 号结尾。

1、距离符号,在 Linux 下是 \r\n,在 Windows 下是 \n

2、简略字符串 Simple Strings, 以 “+” 加号 结尾

3、谬误 Errors, 以 ”-“ 减号 结尾

4、整数型 Integer,以 “:” 冒号结尾

5、大字符串类型 Bulk Strings, 以 “$” 美元符号结尾,长度限度 512M 6、数组类型 Arrays,以 “*” 星号结尾 用 SET 命令来举例说明 RESP 协定的格局。

理论发送的申请数据:

redis> SET myLove "Mia"
"OK"
*3\r\n$3\r\nSET\r\n$6\r\nmyLove\r\n$3\r\nMia\r\n
*3
$3
SET
$5
mykey
$5
Hello

理论收到的响应数据:

+OK\r\n

文件写入和保留:

每当服务器惯例工作函数被执行、或者事件处理器被执行时,aof.c/flushAppendOnlyFile 函数都会被 调用,这个函数执行以下两个工作:

WRITE: 依据条件,将 aof_buf 中的缓存写入到 AOF 文件。SAVE: 依据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保留到磁盘中。

2、给你的爱一个期限 expire myLove 999999999

从图可知,在 redis 的数据库中,redisDb 构造中的 expires 字典中保留了数据库中所有键的过期工夫,所以叫过期字典。

过期字典的 key 是一个指针,指向键空间的某个键对象(就是数据库键)

过期字典的 value 是一个 long 类型的整数,这个整数保留了键所指向的数据库键的过期工夫,一个毫秒精度的 UNIX 工夫戳

过期键断定

通过过期字典,咱们能够失去一个 key 是否过期:

判断 key 是否存在于过期字典中

通过过期字典拿到 key 的过期工夫,判断以后 UNIX 工夫戳是否大于 key 工夫

过期 key 如何删除

惰性删除策略

过期键的惰性删除策略由 db.c/expireIfNeeded 函数实现,所有读写数据库的 Redis 命令在执行之前都会调用 expireIfNeeded 函数对输出键进行查看:

如果输出键曾经过期,那么 expireIfNeeded 函数将输出键从数据库中删除。

如果输出键未过期,那么 expireIfNeeded 函数不做动作。

expireIfNeeded 函数就像一个过滤器,它能够在命令真正执行之前,过滤掉过期的输出键,从而防止命令接触到过期键。

另外,因为每个被拜访的键都可能因为过期而被 expireIfNeeded 函数删除,所以每个命令的实现函数都必须能同时解决键存在以及键不存在这两种状况:

当键存在时,命令依照键存在的状况执行。

当键不存在或者键因为过期而被 expireIfNeeded 函数删除时,命令依照键不存在的状况执行。

定期删除策略的实现

过期键的定期删除策略由 redis.c/activeExpireCycle 函数实现,每当 Redis 的服务器周期性操作 redis.c/serverCron 函数执行时,activeExpireCycle 函数就会被调用,它在规定的工夫内,分屡次遍历服务器中的各个数据库,从数据库的 expires 字典中随机查看一部分键的过期工夫,并删除其中的过期键。

3、del myLove

不好意思,哥们的爱无奈删除!

正文完
 0