1、应用场景
1.1)热点数据的缓存;1.2)计数器;1.3)限时消息的应用;1.4)延时消息;1.5)队列;1.6)排行榜;1.7)分布式锁;1.8)分布式回话;
2、数据类型
2.1)String(存储结构:int、SDS)
热点数据的缓存
计数器
分布式session共享
2.2)List(存储结构:redis 3.2之前ziplist或linkedlist;3.2之后,quicklist【ziplist和linkedlist的结合】)
队列(rpush或lpoop)
分页
文章列表
2.3)hash(存储结构:ziplist或者hashtable)
2.4)set(存储结构:intset或者hashtable)
可以取 交集、并集、差集,可以应用于查看QQ共同好友
2.5)zset(存储结构:ziplist或者 skiplist+hashtable)
排行榜
ziplist的数据结构
zlbytes:ziplist的数据大小(字节数),32位的无符号整数
zltail:最后一个节点的偏移量,反向变量ziplist或者往尾结点pop数据时使用
zllen:entry的个数
zlend:值为OFXX,用于标记ziplist的尾结点
entry的组成:
prevlength:记录上一个节点的长度,为了反向变量list
encoding:当前节点的编码规则
data:当前节点的值
skiplist的数据结构
1、跳跃表的每层都是有序的链表
2、查询的次数近似于层数,时间复杂度是O(log n),插入删除的时间复杂度是O(log n)
3、底层的链表包含所有的数据
4、跳跃表是一种随机的数据结构(通过抛硬币决定高度,正面继续,反面停止)
5、跳跃表的空间复杂度O(n)
插入节点操作
1、在底层链表所在位置插入数据
2、算出层数
3、调整索引
删除节点操作
1、在底层链表找到所在的位置
2、删除每一层的索引
查找操作
1、可以把跳跃表看成多个有序的链表
2、查找的过程中,从最上层开始查找,找到对应的区间,再往下一层找(每个节点都有两个指针,一个往右,一个往下)
跳跃表和红黑树相比,性能差不多,但是跳跃表更容易实现,并且不需要维护平衡性
3、redis的持久化策略
RDB和AOF,如果同时采取了RDB和AOF,恢复的时候优先AOF,数据更加完整
RDB:周期性的保存全量数据(快照)
AOF:以日志文件的形式存储,类似于mysql的binlog,将指令append-only到日志文件中,减少文件的寻址,提高性能。
执行RDB持久化策略时
1)redis会调用系统的fork()函数,创建一个子进程
2)子进程将数据集写入临时的RDB文件
3)当子进程完成对临时的RDB文件写入时,redis用新的RDB文件来替换旧的RDB文件,并将旧的RDB文件删除。
优缺点比较
RDB
优点:
1)RDB会生成多个数据文件,每个文件代表某一时刻redis中的数据,这种多个数据文件的方式,非常适合冷备。
2)redis调用系统fork()函数生成临时RDB文件,对系统读写性能影响小。
3)在恢复大数据集的时候速度比AOF快。
缺点:
1)上次全量保存后的数据可能会丢失
2)RDB每次fork出子进程来执行RDB快照文件时,如果文件特别大,可能会导致客户端提高服务暂停毫秒数或者几秒
AOF
优点:
1)可以更好的保护数据不丢失,一般AOF会每个一秒,通过后台线程执行一次fsync操作,如果redis进程挂掉,最多丢失1秒的数据。
2)AOF以append-only的模式写入,所以没有任何磁盘寻址的开销,写入性能非常高。
3)适合做灾难性误删除紧急恢复,如果某人不小心用 flush all命令清空了所有数据,如果还没rewrite,那么就可以将日志文件中的flush all删除,进行恢复。
缺点:
1)对于同一份文件,AOF文件比RDB数据快照要大。
2)AOF开启后,支持写的QPS会比RDB执行写的QPS低,一般AOF一秒执行一次fsync,性能还是很高的。
3)数据恢复比较慢,不适合做冷备。
4、Redis的过期删除策略
1、定期删除:每隔100秒就随机抽取一些设置了过期时间的key,检查是否过期,如果过期就删除
2、消极删除:获取某个key时,如果设置了过期时间,redis会判断是否过期,如果过期,就删除,返回null
5、redis的淘汰策略
FIFO:删除最早的数据
LRU:删除最近最少使用的数据
LRU脚本
class LRUCache<k,v> extends linkedHashMap<k,v> {
private final int CACHE_SIZE;
public LRUCache(int cacheSize) {
super((int)Math.ceil(cacheSize/0.75)+1,0.75f,true);
CACHE_SIZE = cacheSize;
}
@overried
protecked boolean revomeEldestEntry(Map.Entry<k,v> eldest) {
return size() > CACHE_SIZE;
}
}
6、redis为单线程,为什么性能这么快
6.1、纯内存操作
6.2、核心是基于非阻塞IO的多路复用机制
6.3、C语言实现,更接近底层
6.4、单线程避免了多线程频繁的上下文切换
redis线程模型:文件事件处理器
1、多个socket
2、多路复用IO
3、文件事件分派器
4、事件处理器(连接答应处理器、命令请求处理器、命令响应处理器)
过程:多个socket会有不同的操作,不同的操作会产生不同的事件,多路复用IO监听这些socket,将产生事件的socket放入队列中,文件事件分派器会从队列中取出socket,根据事件类型分派给不同的时间处理器。
7、如何保证高可用
7.1)主从模式
7.2)哨兵模式
7.3)集群模式(主从+哨兵)
主从数据同步
核心:当启动一个slave node的时候,它会发送一个psync指令给master,如果slave 第一次访问master,master会启动一个子线程,进行全量复制生成快照文件,同时还会将新指令写入内存中,master完成全量复制后,将RDB文件发送给slave,slave在磁盘中解压,在从磁盘中加载到内存中,然后将内存中的数据发个slave。
主从复制断点续传
主服务器端为复制流维护一个内存缓冲区(in-memory backlog)。主从服务器都维护一个复制偏移量(replication offset)和master run id。
当连接断开时,从服务器会重新连接上主服务器,然后请求继续复制,假如主从服务器的两个master run id相同,并且指定的偏移量在内存缓冲区中还有效, 则复制就会从上次中断的点开始继续。如果其中一个条件不满足,就会进行完全重新同步。
无磁盘复制
master在内存中直接创建RDB,然后发送给slave,不会在自己本地落盘,在需要在配置文件中配置。repl-diskless-sync 和 repl-diskless-sync-delay。
哨兵机制实现高可用
1、集群监控:复制监控master和slave进程是否正常工作
2、消息通知:如果某个redis实例有故障,通知管理员
3、故障转移:如果master挂掉,会自动转移到slave上
4、配置中心:如果故障发生转移,会将新的master地址通知到client客户端
redis哨兵主备切换数据丢失问题
1、异步复制导致的数据丢失
因为master往slave的复制是异步的,所以可能部分数据还没复制到slave,master就宕机了,此时这部分数据就丢失了
2、脑裂导致的数据丢失
某个master突然脱离的正常的网络,但实际上还是正常运行的。在选举新的master之前,旧的master依然在接收数据,当新的master选举后,旧的master数据会被清空。
redis哨兵主备切换解决数据丢失的问题
1、min-slaves-to-write 至少保证几个从节点写入
2、min-slaves-max-log 数据复制和同步延时超过几秒,master就不再就收数据
通过减少异步复制数据的丢失和减少脑裂数据的丢失
选举算法
1、跟master断开连接的时长
2、slave的优先级(priory越小,优先级越高)
3、较大的复制 offset
4、较小的run id
集群模式的通信协议:gossip
未完待续。。。
1、分布式锁的实现
2、分布式寻找算法