1. 数据类型
1.1 字符串SDS
struct sdshdr {
//字符串末尾为一个空字节
int len;
//空间预分配:每一次扩容时,如果小于1M就分配len大小的空间给free,大于1M就分配1M
//惰性空间释放:截断时,不释放buf空间,将空白的交给free
int free;
//buf保存二进制数据,二进制安全
char[] buf;
}
1.2 链表
列表键的底层实现之一,此外,发布订阅,慢查询,监视器也会使用链表。
Quickest:压缩列表+指针。
1.3 字典
Redis的字典由两个哈希表组成,采用渐进式rehash
typedef struct dict {
//针对不同类型的键值对,为创建多态字典使用
dictType *type;
void *privdata;
//两个哈希表,用于渐进式rehash
dictht ht[2];
int trehashidx;//-1表示没有rehash
}
typedef struct dictht {
dictEntry **table;//指向哈希节点
unsigned long size;//表大小
unsigned long sizemask;//用于计算索引值,size-1
unsigned long used;//已有节点数量
}
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
}
struct dictEntry *next;//解决哈希冲突
}
1.4 跳表
跳表层高为1-32之间的随机数。
1.5 整数集合
按照从小到大有序排列,且保证不重复。
typedef struct intset {
uint32_t encoding;//有升级机制
uint32_t length;
int8_t contents[];
}
1.6 压缩列表
存个数,尾部,每个节点纪录上个节点的长度。
2. 单机数据库
2.1 过期设置
- setnx适用于字符串,赋值+设置过期时间;
- 通过过期字典来维护过期数据,可以采取定期删除与惰性删除相结合的方式,定期取一部分,判断是否需要删除,用的时候惰性判断;
- RDB存储,写入或者AOF重写都会检查过期键,AOF存储不会检查;
2.2 持久化
- RDB以压缩的二进制文件的方式快照存储,恢复迅速,但是会有间隔不一致性,快照存储过程中运用了多线程的cow技术;
- AOF以指令追加的形式即时存储,恢复慢,一致性更高,但是也会有丢失,因为AOF是先存储在内存中的。
2.3 主从同步
- 完整同步过程:从服务器发送SYNC命令,主服务器发送最新的RDB文件,并且发送缓存区保存的写命令;
- 部分同步过程:维护一个偏移量与一个循环队列;
- 无盘复制:主库不进行文件IO,而是在遍历过程中将快照直接同步给从库,从库存在自己的硬盘上,之后刷进自己的内存;
- 心跳机制每秒向主服务器发一个偏移量,检测服务器连接状态以及丢失情况。
2.4 哨兵模式
- 选举,拜占庭容错,投票选举;
- 选出新的主库;
- 哨兵机制也不能保证完全的一致性,除非牺牲可用性。
2.5 节点与分槽
- 节点只能用0号数据库;
- 节点靠位图记录自己分到的槽;
- slot数组维护每一个槽指向的节点对应的数据结构的指针;
- 节点会用跳跃表保存槽与键之间的关系;
- 槽迁移与ASK错误重定位;
- MOVED错误是将请求定向槽对应的节点;
2.6 缓存问题
缓存穿透;缓存击穿;缓存雪崩。
2.7 线程模型(略)
3. 应用设计
3.1 分布式锁
set key val ex time nx #实现原子性操作
#val可以设计为random,但是要注意,判断val是否符合与del锁不是原子性的,要用lua脚本
#分布式锁保证一致性是靠redlock算法,过半更新原则
3.2 限流算法
- 漏斗算法
- 令牌桶算法