redis
是使用 C 语言编写的,但是 C
语言是没有字典这个数据结构的,因此 C
语言自己使用结构体来自定义一个字典结构
typedef struct redisDb
srcserver.h 中的 redis 数据库 数据结构
/* 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 */ 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 */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ int id; /* Database ID */ long long avg_ttl; /* Average TTL, just for stats */ unsigned long expires_cursor; /* Cursor of the active expire cycle. */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */ } redisDb;
redisDb 存放了 redis 数据库底层的数据结构:
- dict
字典类型
- expires
过期时间
- blocking_keys
客户端等待数据的键 (BLPOP)
- ready_keys
收到PUSH的键被阻塞
- watched_keys
监控 MULTI/EXEC CAS 的键,例如事务的时候就会使用到
- id
数据库的 id, 0 – 15
- avg_ttl
统计平均的 ttl
- expires_cursor
记录过期周期
- defrag_later
存放 key 的列表
typedef struct dict
srcdict.h 字典的数据结构
typedef struct dict { dictType *type; void *privdata; dictht ht[2]; long rehashidx; /* rehashing not in progress if rehashidx == -1 */ int16_t pauserehash; /* If >0 rehashing is paused ( SETBIT login:9:11 25 1 (integer) 0 127.0.0.1:6379> SETBIT login:9:11 26 1 (integer) 0 127.0.0.1:6379> SETBIT login:9:11 27 1 (integer) 0 127.0.0.1:6379> BITCOUNT login:9:11 (integer) 3 127.0.0.1:6379> strlen login:9:11 (integer) 4
- BITCOUNT key [start end]
通过 BITCOUNT 可以看出 11 号在线人数 3 个人,login:9:11 占用字节数位 4 字节
127.0.0.1:6379> SETBIT login:9:12 26 1 (integer) 0 127.0.0.1:6379> SETBIT login:9:12 25 0 (integer) 0 127.0.0.1:6379> SETBIT login:9:12 27 1 (integer) 0 127.0.0.1:6379> STRLEN login:9:12 (integer) 4
通过 BITCOUNT 可以看出 12 号在线人数 2 个人,login:9:12 占用字节数位 4 字节
下面我们将取 login:9:11 和 login:9:12 的 与操作,来计算 11 号 和 12 号两天来都在线的人数
127.0.0.1:6379> BITOP and login:and login:9:11 login:9:12 (integer) 4 127.0.0.1:6379> BITCOUNT login:and (integer) 2
- BITOP operation destkey key [key ...]
根据上述结果我们可以看出,11 号 和 12 号两天来都在线的人数为 2 人,验证 ok
我们再来看看11 号 和 12 号任意一天在线的人数
127.0.0.1:6379> BITOP or login:or login:9:11 login:9:12 (integer) 4 127.0.0.1:6379> BITCOUNT login:or (integer) 3
根据上述结果我们可以看出,11 号 和 12 号任意一天在线的人数为 3 人,验证 ok
127.0.0.1:6379> type login:or string 127.0.0.1:6379> OBJECT encoding login:or "raw" 127.0.0.1:6379> OBJECT encoding login:9:12 "raw" 127.0.0.1:6379> OBJECT encoding login:and "raw"
咱们来看看上述用到的 key ,在 redis 里面实际是什么数据类型吧,
- OBJECT encoding [arguments [arguments ...]]
可以看出上述都是 “raw” 类型, 也就是 redis 的 sds 类型
缓存行
咱们再来看一个小例子,redis 中设置一个字符串 key
127.0.0.1:6379> set name xiaoming OK 127.0.0.1:6379> OBJECT encoding name "embstr"
我们可以看出 name 的类型是 “embstr”,那么 “embstr” 底层是如何实现的呢?“embstr” 有能承载多少个字节的数据呢?
上述我们有说到 redis 里面存放键值对的地方在 dictEntry 结构体中,dictEntry 结构体中的val 指针指向的是一个 redisObject 结构体,是这样的
我们在一个 64 位的机器中,CPU 在内存中读取数据的是通过读取缓存行的方式来实现的
一个缓存行有 64 字节
一个 redisObject 结构体占 16 字节
那么就还剩 48 字节 可以使用,那么使用 redis 里面 哪一个 sds 数据结构来存放数据数据呢?
使用 hisdshdr8 类型,hisdshdr8 类型 sds 的前 3 个元素占用 3 个字节,那么剩下的 buf 存放数据就可以存放 45个字节(64 - 16 - 3)的数据了
如果你这么认为了,那么就有点粗心哦,因为 redis 为了兼容 C 语言的标准,会在字符串的后面加上 1 个 ‘ ’ ,他是占一个字节的因此最终 “embstr”
实际能存放的字节数是:
44 字节
来回顾上一篇文章,可以看出
当数据占用空间在 0 - - 2^5-1 , 使用 hisdshdr5 数据类型
2^5 – 2^8-1 的占用空间的时候,使用 hisdshdr8 数据类型
小小的实践
我们在 redis 中设置一个 test 的值为一个 44字节 的内容,查看这个 key 的类型,是 embstr
127.0.0.1:6379> set test 99999999991111111111222222222233333333334444 OK 127.0.0.1:6379> OBJECT encoding test "embstr" 127.0.0.1:6379> STRLEN test (integer) 44
再来设置 test2 为 大于 44 字节的内容,再查看他的内容是 raw
127.0.0.1:6379> set test2 999999999911111111112222222222333333333344449 OK 127.0.0.1:6379> OBJECT encoding test2 "raw"
最后送上一张上述数据结构的关系图
参考资料:
- redis_doc
- reids 源码 reids-6.2.5 Redis 6.2.5 is the latest stable version.