数据结构
说一说Redis五种基本数据结构
- SDS: 简单动态化数组
{
int len;
int free;
char buf[];
}
优点:
- 二进制安全(只关心数据, 不关心格式, '\0’并不是字符串结尾的标志, 只是一个普通的数据)
- 自动扩容(1MB一下加倍, 1MB以上每次1MB). 减少内存重分配次数
- 兼容部分C的字符串代码
- 获取长度时间复杂度为O(1)
- list链表
{
listNode *pre;
listNode *next;
void *value;
}listNode
- 字典
{
void* key;
void* val;
dictEntry* next;//拉链法解决哈希冲突
}dickEntry
{
dictEntry **table;
unsigned long size;//哈希表大小
unsigned long sizemask;//大小掩码,用来计算索引值
unsigned long used;//已有节点的数量
}dicttht
{
...
dictht ht[2];//双哈希表rehash
}
渐进式rehash: rehash同时保留新旧两个hash, 查询时同时查询两个hash, 然后渐进的将旧hash的内容一点点迁移到新的hash结构中. 搬迁完后新的hash取代旧的hash
- 跳跃表如下
- set集合
内部实现相当于一个特殊的字典, 所有的value都是NULL
Redis的跳表怎么实现的
跳表是一个查询插入和删除的时间复杂度都为O(logn)的多层链表
所谓多层, 是链表的每一个结点会有多个前向指针forwards, 有多少个forwards是随机的. 这些forwards指向
{
string value
double socre
zslnode*[] forwords
zslnode* backward
}zslnode
{
zslnode* header
int maxLevel
map<string,zslnode*>bt
}zsl
持久化
AOF持久化
aof重写机制: fork一个子进程遍历内存转换成一系列Redis的操作指令. 在此期间将维护一个AOF缓冲区, 记录所有的写操作. 重写完成后将缓冲区的数据追加到新aof文件结尾.
RDB持久化
集群
说一说Sentinel
着眼于高可用: 负责监控主从服务, master挂了自动将slave提升为master, 同时接受所有的请求并转发到master
说一说Cluster
着眼于扩展性: 当单个Redis内存不足时, 自动进行分片储存
说一说Redis如何进行主从同步的
- 增量同步: 指令 内存buffer 环状数组
- 快照同步: bgsave 先同步到文件, 再发送
- 无盘复制: 直接通过套接字把内存的内容发送的从节点
高级数据结构
HyperLogLog是什么, 能干什么
简单介绍一下布隆过滤器
其他
Redis的管道是干什么的
Redis的数据淘汰策略
五种
noeviction返回错误
volatile-lru: 淘汰设置了过期时间, 最久没被使用的
volatile-ttl:淘汰时间越近的优先被淘汰
allkeys-lru:所有的key
allkeys-random: 随机删除
Redis为什么这么快
单线程(CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽), 避免不必要的上下文切换和资源竞争
多路io复用的监听机制
定制化的序列化(将储存对象转化为另一种通用的格式化方式, 包括二进制, json)方式
数据结构简单, 专门设计了适合的数据结构, 包括跳表等
完全基于内存, 绝大部分请求都是纯粹的内存操作
如何保证Redis缓存和数据库数据的一致性
最经典的缓存+数据库读写的模式,就是 预留缓存模式Cache Aside Pattern。
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
更新的时候,先删除缓存,然后再更新数据库,这样读的时候就会发现缓存中没有数据而直接去数据库中拿数据了。(因为要删除,狗日的编辑器可能会背着你做一些优化,要彻底将缓存中的数据进行删除才行)
Redis怎么处理过期时间
通过expire设置过期时间, 到达过期时间时, 有两种方式处理: 定期删除和惰性删除
定期删除就是定时选一批过期的数据删除
惰性删除就是在访问数据时会检查是否过期, 如果过期了就删除