前面几篇介绍了 各种数据类型以及其底层实现, 从这篇开始我们就要介绍 Redis 的高级功能部分,虽然会比较抽象,但是通过 CGDB 调试辅助,还是可以顺利理解
Redis 数据库为 各种数据类型(字符串、列表、集合、有序集合、哈希 类型)的 存储载体,当然 Redis db 也并不仅仅是 存储功能,这次 我们就对其 进行详细解说 ……
数据库结构
Redis 数据库 的数据结构如下:
*dict
存储 键值对字典地址,为 字典结构 ,存储了所有键值对数据,键为 Redis 数据的 key,值 为 各种数据类型的对象(字符串对象、列表对象、集合对象、有序集合对象、哈希对象),具体 字典 dict 结构 的详解请查看之前的 篇章 字典*expires
存储 过期时间数据,也为 字典结构*blocking_keys
存储 阻塞键 以及客户端信息,比如 list 类型 的 阻塞命令*ready_keys
存储阻塞键 就绪数据信息,对应上面 blocking_keys 数据,用于解除阻塞*watched_keys
watch 键 的信息,后续 事务中会提到
服务器在启动的时候会根据 server.dbnum
(默认 16) 初始化对应个数的数据库,成功后存储在 redisServer 信息中
客户端 client 在执行 select 切换数据库命令的时候,会将当前客户端执行的目标数据库存储在 client 下的 db 字段中
假设有 列表类型 foo1、哈希类型 foo2、字符串类型 foo3,其中 foo1 和 foo3 设置了过期时间;数据库数据存储 图例结构如下:
所以说 对 数据库的操作 更多是 字典的操作…
过期删除策略
我们上面内容也提到了,数据库中会 特地保存过期数据,那么肯定也有一些策略 专门对 过期数据进行清除
定时删除
【主动策略】在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临的时候立即执行删除操作
定时删除策略对内存是最友好的,可以保证过期键会尽快被删除,释放占用的多余内存;但是它对 CPU 是最不友好的,在过期键比较多的时候,删除行为会占用相当一部分 CPU 时间,在 内存不紧张 但是 CPU 时间比较紧张的时候,将 CPU 时间用在 删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响
而且定时器在 Redis 一般是一个 无序的时间事件链表,所以查找时间的复杂度为 O(n),所以特别高效的处理大量时间事件并不现实
惰性删除
【被动策略】放任键过期不管,但是每次从键空间获取键的时候,都检查该键是否过期,如果过期就删除,不过期就该干嘛干嘛
惰性删除策略对CPU时间是最友好的,只会在操作键的时候才进行过期检查,可以保证删除过期键的操作只会在非做不可的情况下进行,这样就不会影响其他 业务资源;但是 这个策略对内存却是非常不友好的,因为很有可能有一些键永远都不会被删除,这种情况对于非常依赖内存的内存数据库来说,肯定不是个好事
定期删除
【主动策略】每隔一段时间,程序就会对数据库进行一次检查,删除里面的过期键,至于要删除多少键、检查多少个数据库,又有详细算法
定期删除策略为 上述两种策略的整合和折中,毕竟之前的两种策略在单一使用时都会有明显的缺陷;定期删除策略每隔一段时间执行一次删除过期操作,并通过限制删除操作执行的时间和频率来减少删除操作对 CPU 时间的影响
不过定期删除策略的难点在于确认删除操作执行的时长以及频率
- 如果删除操作执行的时间太频繁或者执行时间过长,定期删除策略就退化成了定时删除策略,那么 CPU 时间就惨了
- 如果删除操作执行太少或者执行时间太短,那么又可能退化成惰性删除策略,可能浪费内存
所以针对不同场景,Redis 服务器可以合理的设置 删除操作 的执行 时长和频率
下面我们就 Redis 数据库 一些典型的 API 进行解析~~~
源码解析
由于有些方法里的代码量比较大,我们这里按照 典型的代码片段进行解析,同志们可以根据文章提示的代码位置 和 代码里面的关键词 在源码中搜素,可能数据结构一些元素 看不太懂什么意思,没关系,先混个脸熟,后面看完回头再看过来就明白了
切换数据库
切换数据库命令 select,处理函数为 selectDb()
数据库添加键值对
添加键值对的 底层操作函数为 dbAdd()
,如果注意的话,这个函数在之前的一些源码中都出现过
数据库删除键值对
删除键值对 底层操作函数为 dbDelete()
,删除 字典数据的同时,还需要 去除过期数据;
不过删除并不是简单的 一刀切,Redis 作为一个对效率要求极其苛刻的工具,肯定不会忍受因为 删除大 key 或者 过期淘汰大 key 而引起的阻塞,所以 从 4.0 开始,提供了 lazyfree 机制,可以将一些比较耗费的操作 经过鉴别抛入后台,让后台线程进行处理
所以删除操作其实是 分为了两种情况
|
|
异步删除 dbAsyncDelete()
同步删除 dbSyncDelete()
,为默认操作
数据库设置键值
设置键值函数为 setKey()
,可能是 添加 或者 重设
数据库查找键值对
键值对查找函数 不止一个,运用于不同的场景下,而底层操作函数始终为 lookupKey()
数据库键操作过期时间
为 key 设置过期时间,数据库部分处理函数为 setExpire()
查找、删除 过期时间的逻辑就比较简单了,主要单纯的 操作 过期信息字典
数据库删除策略
上面我们提到 惰性删除策略,它的主要处理函数为 expireIfNeeded()
定期删除策略 具体逻辑由 activeExpireCycle()
函数处理,该函数不光在 Redis 的事件驱动循环 beforesleep 中执行,也会在 CPU 空闲的时候 通过 databasesCron 定时处理函数进行 调用执行
上述中提到的 具体执行过期删除的函数 activeExpireCycleTryExpire()
以上为比较经典的函数逻辑解析,另外我从 其他资料找到了 一些 API 的详解,大家可以将就看看
本文作者: wettper
本文链接: http://www.web-lovers.com/redis-source-db.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!