大家好,我是热心的大肚皮,皮哥。
之前我们聊过string、list、hash、set、zset,并且自实现了动态字符串、双端链表、hash字典、压缩链表、跳跃链表等结构。那么redis是怎么组织这些数据结构高效的运行呢?
redis如何新增一个kv
redis的键值都是redisObject对象,在创建时会生成redisDb中一个键名和一个键值的redisObject对象。其中redisDb对象构成了redis的键空间。
键空间
redis是一个键值对(key-value pair)数据库服务器,服务器中的每个数据库都由一个redisDb结构表示,redisDb结构中dict字典保存了数据库中的所有键值对,我们将这个字典称为键空间(key space)。
typedef struct redisDb {
// 保存着数据库以整数表示的号码
int id;
// 保存着数据库中的所有键值对数据
// 这个属性也被称为键空间(key space)
dict *dict;
// 保存着键的过期信息
dict *expires;
// 实现列表阻塞原语,如 BLPOP
dict *blocking_keys;
dict *ready_keys;
// 用于实现 WATCH 命令
dict *watched_keys;
} redisDb;
redisObject
每一个redis中key的value都是下面的结构。
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向数据的指针
void *ptr;
// 记录对象最后一次被程序访问时间,用于计算空转时长(当前时间-lru)
unsigned lru:22; /* lru time (relative to server.lruclock) */
// 引用计数,用于内存回收
int refcount;
} robj;
如图所示:
大家看到这里应该明白了吧,具体的添加如下图。
修改与删除区别不大,就不过多阐述了。
redis如何过期一个kv
过期字典
在键空间中,不单单有dict字典,还有个expires属性,这个expires字典记录着当前数据库的全部过期时间,也叫做过期字典:
其中过期字典与正常的键都指向同一个对象,所以不存在空间浪费。
过期策略
我们推断一下过期策略可能有三种。
定时过期,在redis中创建大量的定时器,太消耗性能,而惰性过期,如果key不被访问,那么会浪费大量的内存,定期过期则会造成过期的数据也被访问到。为了解决上面的问题,redis采用惰性过期与定期过期一起使用。
惰性过期具体流程如下。
而定期过期由activeExpireCycle函数实现,每当serverCron函数执行时,activeExpireCycle函数会在规定时间内分多次遍历各个数据库,在过期字典里随机检查一部分key的过期时间并删除,
工作小技巧
假设一个大型的redis集群中,大量的key同时过期了,会出现什么样的结果呢?之前皮哥接手了一个电商某模块的C端系统后,通过性能监控发现每天一个固定的时间,用到的redis的接口都会出现性能抖动,可用率降低。
经过分析发现,是在同一时间有大量的key过期,从而导致性能波动,那么为什么会出现这种原因呢?原因有二。
如果在过期时客户端请求过来,而客户端的超时时间设置的比较短,那么会出现大量的链接超时断开,业务会出现很多异常。所以一定要注意,解起来也比较简单,可以在过期时间上加个随机数来避免,这样也能让你的TL对你刮目相看呦。