当你对 redis 说你中意的女孩是 Mia
作者:京东科技 周新智
一、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 结构体
1.1.2、对象封装 redisObject
我们接着再往下看 redisObject 究竟是什么结构的
*ptr 指向具体的数据结构的地址;type 表示该对象的类型,即 String,List,Hash,Set,Zset 中的一个,但为了提高存储效率与程序执行效率,每种对象的底层数据结构实现都可能不止一种,encoding 表示对象底层所使用的编码。
redis 对象底层的八种数据结构:
查看 redisObject 详细信息 :
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
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 协议的格式。
实际发送的请求数据:
实际收到的响应数据:
文件写入和保存:
每当服务器常规任务函数被执行、 或者事件处理器被执行时, 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
不好意思,哥们的爱无法删除!
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/4c8eb7e25507134ed48e32446】。文章转载请联系作者。
评论