前言
上一篇大概介绍了 redis 属于内存数据库,采用了事件驱动模式并通过 io 复用来实现。除此之外,redis 还可以以 AOF 与 RDB 两种方式进行持久化等。接下来,我们继续从 redis 的早期版本入手,了解 redis 基本结构。
导航
从文件名出发……
文件并不多,大体如下:
- 数据结构
- adlist:双向链表
- dict:哈希表
- 事件驱动
- ae,ae_epoll,ae_kqueue,aeselect
- 辅助
- 压缩:lzf
- 排序:pqsort
- 字符串:sds
- 功能性
- tcp:anet
- 开放 api:hiredis
- 命令行编辑器:linenoise
- main
- 命令行(command line interface):redis-cli
- 状态:redis-stat
- redis.c
可以看出主体服务在 redis.c 文件中,而这些文件中有一个字符串辅助库 sds(A C dynamic strings library)比较特别。
1 | // sds.h |
可以看到,sds 总是指向 sdshdr 结构中实际存储字符串的地方 buf[],而 sdsnew()函数返回的是 sds。这样,一方面可以兼容 c 字符串函数,另一方便,通过指针的移动,很容易获取 sdshdr 结构中的其他成员:len,free。len 记录着 sds 的长度,注意这个长度并不是 buf[]的长度,而是 buf[]中实际用于存储字符的长度。至于 buf 中没有用来存储字符的长度,则记录在了 free 里。所以实际上,len(buf)=len+free+1(结尾字符)。
所以使用 sds,可以避免通过遍历来计算字符串的长度,将 O(n)的时间复杂度降为 O(1);还可以通过预分配来减少重新分配内存空间次数:
1 | static sds sdsMakeRoomFor(sds s, size_t addlen) { |
而 sdstrim()函数中,也不会释放多余的内存空间,而是通过变更 len 与 free 值来维护 sds。
redis.c
了解了 sds,再看服务入口文件 redis.c。在 redis.c 文件中,定义了一些基本数据类型。让我们从这些数据类型开始了解。
- redisObject
1 | typedef struct redisObject { |
- redisDb
1 | typedef struct redisDb { |
- redisCommand
1 | struct redisCommand { |
再加上上一篇所提到的事件驱动模式,redis 的主体流程就呼之欲出了: