比原始的 C 字符串操作更安全便捷
struct sdshdr {
// 记录 buf 数组中已使用字节的数量
// 等于 SDS 所保存字符串的长度
int len;
// 记录 buf 数组中未使用字节的数量
int free;
// 字节数组,用于保存字符串
char buf[];
};
应用于:列表键、慢查询、监视器等
应用于:字典、数据库 redisDb 结构等
Rehash 简单总结
/* 字典结构定义 */
typedef struct dict {
dictType *type; // 字典类型
void *privdata; // 私有数据
dictht ht[2]; // 哈希表[两个][为了后续字典的扩展作Rehash之用]
long rehashidx; // 记录rehash 进度的标志,值为-1表示rehash未进行
int iterators; // 当前正在迭代的迭代器数
} dict;
/* Hash 表数据结构 */
typedef struct dictht {
dictEntry **table; // 哈希表数组
unsigned long size; // 哈希表的大小
unsigned long sizemask; // 哈希表大小掩码
unsigned long used; // 哈希表现有节点的数量
} dictht;
/* 哈希桶数据结构 */
typedef struct dictEntry {
void *key; // 键定义
// 值定义
union {
void *val; // 自定义类型
uint64_t u64; // 无符号整形
int64_t s64; // 有符号整形
double d; // 浮点型
} v;
struct dictEntry *next; //指向下一个哈希表节点
} dictEntry;
用于:有序集合
跳表具有如下性质:
(1) 由很多层结构组成
(2) 每一层都是一个有序的链表
(3) 最底层(Level 1)的链表包含所有元素
(4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现
(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素
(6) 通过一个随机函数,来决定将这个结点插入到哪几级索引中
集合键的底层实现,当集合只包含整数值元素,且数量不多的时候使用
typedef struct intset { unit32_t encoding; //编码方式 int16_t int32_t 或者 int64_t unit32_t length; //集合包含的元素数量 int8_t contents[]; //重点:保存元素的数数组(数字真正的类型取决于 encoding 属性) }intset;
升级操作(不支持降级):
用于实现:列表和字典类型
应用:hash、list、zset 容器对象中,在元素个数较少的时候,会使用ziplist进行存储
遍历:通过 zltail 获取到队尾节点,之后根据偏移量获取上一个节点
更新:增加元素可能造成拓展内存或者重新分配内存
struct ziplist<T> { int32 zlbytes; // 整个压缩列表占用字节数 int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点 int16 zllength; // 元素个数 T[] entries; // 元素内容列表,挨个挨个紧凑存储 int8 zlend; // 标志压缩列表的结束,值恒为 0xFF } zlend //压缩列表的末端标记 struct entry { int<var> prevlen; // 前一个 entry 的字节长度 (通过这个进行遍历数据) int<var> encoding; // 元素类型编码和长度 optional byte[] content; // 元素内容 }
它有点儿类似数组,通过一片连续的内存空间,来存储数据。不过,它跟数组不同之处在于:
我们在遍历节点的之后就知道每个节点的长度(占用内存的大小),就可以很容易计算出下一个节点再内存中的位置。这种结构就像一个简单的压缩列表了。
Redis的每种对象其实都由对象结构(redisObject) 与 对应编码的数据结构组合而成
redisObject 是 Redis 类型系统的核心, 数据库中的每个键、值, 以及 Redis 本身处理的参数
typedef struct redisObject {
unsigned type:4; // 类型
unsigned encoding:4; // 编码方式
unsigned lru:LRU_BITS; // LRU - 24位, 记录最末一次访问时间(相对于lru_clock); 或者 LFU(最少使用的数据:8位频率,16位访问时间)
int refcount; //引用计数
void *ptr; //指向底层数据结构实例
} robj;
Redis中存在“数据库”的概念,该结构由redis.h中的redisDb定义。
typedef struct redisDb {
int id; //id是数据库序号,为0-15(默认Redis有16个数据库)
long avg_ttl; //存储的数据库对象的平均ttl(time to live),用于统计
dict *dict; //存储数据库所有的key-value(重要)
dict *expires; //存储key的过期时间(重要)
dict *blocking_keys;//blpop 存储阻塞key和客户端对象
dict *ready_keys;//阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象
dict *watched_keys;//存储watch监控的的key和客户端对象
} redisDb;
Redis 过期键的删除策略
上面 redisDb 结构中的 expires 字典保存了数据库中所有键的过期时间,redis 使用下面两种方式删除过期数据
将多条命令请求打包,然后一次性、按顺序地执行多个命令的机制(服务器不会中断事务而去执行其他客户端的命令请求)
事务执行过程
内存快照 RDB持久化
把内存中的数据以快照的方式写入二进制文件中,默认文件为 dump.rdb 。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
日志追加 aof
把增加、修改数据的命令通过 write 函数追加到文件尾部(默认为 appendonly.aof ),Redis重启时读取文件把数据写入内存。
重写机制:当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。