- Memcache【1】:多线程、异步IO、KV存储、内存存储没有持久化、采用LRU(Least Recent Used)淘汰算法。
1、内存管理:Slab->Page,内存将按照1MB的大小划分为页,而该页内存则会继续被分割为一系列具有相同大小的内存块。在一条新的记录到来时,Memcached会首先检查该记录 的大小,并根据记录的大小选择记录所需要存储到的Slab类型。一个需要考虑的特殊情况就是对记录的更新。在对一个记录进行更新的时候,记录的大小可能会发生变化。在这种情况下,其所对应的Slab类型也可能会发生变化。因此在更新时,记录在内存中的位置可能会发生变化。
2、由于各个实例被选中的概率基本相同,因此具有较大内存的Memcached实例将无法被充分利用:我们可以通过在具有较大内存的服务器上部署多个Memcached实 例来解决这个问题。
3、Slab钙化:当需要插入一条99K的数据而Memcached已经没有足够的内存再次 分配一个Slab实例的时候,其并不会释放具有300K块大小的Slab,而是在100K块大小的各个Slab中找到需要释放的块,并将新数据添加到该块中。
解决方法:
1) 重启Memcached实例,简单粗暴,启动后重新分配Slab class,但是如果是单点可能造成大量请求访问数据库,出现雪崩现象,冲跨数据库。
2) 随机过期:过期淘汰策略也支持淘汰其他slab class的数据,twitter工程师采用随机选择一个Slab,释放该Slab的所有缓存数据,然后重新建立一个合适的Slab。
3) 通过slab_reassign、slab_authmove参数控制。
- Redis:
1、redisObject【2】:
type:对象的类型;encoding:对象存储方式。
1)String:SDS(本质上就是char *,表头(header)用来存放sds的信息。)(APPEND、INCR、INCRBY、DECR、DECRBY、GET、SET)
encoding:如果一个String类型的value能够保存为整数,则将对应redisObject 对象的encoding修改为REDIS_ENCODING_INT,将对应robj对象的ptr值改为对应的数值。如果不能转为整数,保持原有encoding为REDIS_ENCODING_RAW。
tips【3】:SDS优点:兼容C的部分函数、二进制安全、获得字符串长度的操作复杂度为O(1)、杜绝缓冲区溢出
2)List:List实现为一个双向链表,支持了反向的插入、查找和遍历。(LPUSH、RPUSH、LPOP、RPOP、LLEN)
encoding:OBJ_ENCODING_ZIPLIST:压缩列表实现的列表对象,存储效率高,适合修改较少、数据量较少;OBJ_ENCODING_QUICKLIST:快速列表实现的列表对象;
tips【4】:quicklist是由ziplist组成的双向链表,链表中的每一个节点都以压缩列表ziplist的结构保存着数据,而ziplist有多个entry节点,保存着数据。
3)Hash:Hash是一个string类型的field和value的映射表,一个key可对应多个field,一个field对应一个value,hash内部存储的value为一个hashMap。(HSET、HGET、HEXISTS)
encoding:当hash
中的K/V字符串长度都小于64B且键值对小于512个时使用ziplist
,超过时使用hashtable
。
4)Set:内部实现其实是一个value值为null的HashMap(SADD、SREM、SCARD、SISMEMBER)
encoding:当set中的值都为数值且小于512时使用intset
,超过则使用hashtable
5)Sort Set:功能与Set非常相似,只不过它是可以通过用户提供一个优先级参数来实现自动排序的,内部使用HashMap和SkipList来实现数据的有序存储,保证查询的效率以及元素有序性。(APPEND、INCR、NCR、INCRBY、DECR、DECRBY、GET、SET)
encoding:当zset
中元素长度都小于64B且zset键值对
小于128个时使用ziplist
,超过时会使用skiplist
2、Redis中3种特殊的数据类型【5】(BitMap、Geo和HyperLogLog)
3、持久化:RDB和AOF的区别【6】
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
优点:只包含一个文件、灾难恢复、性能、启动效率会更高;缺点:实时性、高可用性差
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
优点:更高的数据安全性(每秒同步、每修改同步和不同步)、写入过程中即使出现宕机现象也不会破坏日志文件、rewrite机制、记录所有的修改操作;缺点:恢复速度、运行效率不如RDB;
-
主从同步【7】:完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布 记录
全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
1)从服务器连接主服务器,发送SYNC命令;
2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
-
Redis Sentinel(哨兵)架构下的高可用【7】: Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
实现原理:
三个定时监控任务
1)每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构。2)每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断。
3)每隔一秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
主观下线
因为每隔一秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。客观下线
当Sentinel主观下线的节点是主节点时,该Sentinel节点会向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,那么意味着大部分的Sentinel节点都对这个主节点的下线做了同意的判定,于是该Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定。领导者Sentinel节点选举
Raft算法:假设s1(sentinel-1)最先完成客观下线,它会向其余Sentinel节点发送命令,请求成为领导者;收到命令的Sentinel节点如果没有同意过其他Sentinel节点的请求,那么就会同意s1的请求,否则拒绝;如果s1发现自己的票数已经大于等于某个值,那么它将成为领导者。故障转移
1)领导者Sentinel节点在从节点列表中选出一个节点作为新的主节点2)上一步的选取规则是与主节点复制相似度最高的从节点
3)领导者Sentinel节点让剩余的从节点成为新的主节点的从节点
4)Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点
- Redis Cluster(集群)下的高可用【7】:
主观下线
集群中每个节点都会定期向其他节点发送ping消息,接受节点回复ping消息作为响应。如果在cluster-node-timeout时间内通信一直失败,则发送节点会认为接收节点存在故障,把接受节点标记为主观下线(pfail)状态。
客观下线
1)当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播。
2)假设节点a标记节点b为主观下线,一段时间后节点a通过消息把节点b的状态发送到其他节点,当其他节点收到消息并解析出消息体中含有b的pfail状态,把节点b加入下线报告链表;
3)当某一节点c收到节点b的pfail状态时,此时有超过一半的槽主节点都标记了节点b为pfail状态时,则标记故障节点b为客观下线;
4)向集群广播一条pfail消息,通知集群内的所有节点标记故障节点b为客观下线状态并立刻生效,同时通知故障节点b的从节点触发故障转移流程。
故障恢复
1)资格检查
若从节点与主节点断线时间超过一定时间,则不具备资格
2)准备选举时间
当从节点符合故障转移资格后,要等待一段选举时间后才开始选举
在故障节点的所有从节点中,复制偏移量最大的那个从节点最先开始(与主节点的数据最一致)进行选举,然后是次大的节点开始选举.....剩下其余的从节点等待到它们的选举时间到达后再进行选举
3)发起选举
4)选举投票
只有持有槽的主节点才具有一张唯一的选票,从从节点收集到N/2 + 1个持有槽的主节点投票时,从节点可以执行替换主节点操作
5)替换主节点
当从节点收集到足够的选票之后,触发替换主节点操作
当前从节点取消复制变为主节点
撤销故障主节点负责的槽,并把这些槽委派给自己
向集群广播自己的pong消息,通知集群内所有的节点当前从节点变为主节点并接管了故障主节点的槽信息
- Redis处理过期key机制:
当client主动访问key的时,会先对key进行超时判断,过时的key会立即删除;另外redis会在后台,每秒10次的执行如下操作:随机选取100个key校验是否过期,如果有25个以上的key过期了,立刻额外随机选取下100个key。也就是说,如果过期的key不多,redis最多每秒回收200条左右,如果有超过25%的key过期了,它就会做得更多,这样即使从不被访问的数据,过期了也会被删除掉。
- Redis与数据库的数据一致性【8】:
更新的时候,先删除缓存,然后再更新数据库。
读的时候,先读缓存;如果没有的话,就读数据库,同时将数据放入缓存,并返回响应。
高并发下采用队列保证“更新缓存”一定是在DB修改之后。
- 其他常见问题
缓存穿透 | 恶意攻击 | 空对象缓存,bloomfilter过滤器 |
缓存击穿 | 热点key失效 | 互斥更新,随机退避,差异失效时间 |
缓存雪崩 | 缓存宕机 | 快速失败熔断,主从模式,集群模式 |
- 基于Redis实现的延迟消息队列【9】(a.订单下单之后超过30分钟用户未支付,需要取消订单;b.订单一些评论,如果48h用户未对商家评论,系统会自动产生一条默认评论;c.点我达订单下单后,超过一定时间订单未派出,需要超时取消订单等。。。)
将整个Redis当做消息池,以kv形式存储消息
使用ZSET做优先队列,按照score维持优先级
使用LIST结构,以先进先出的方式消费
zset和list存储消息地址(对应消息池的每个key)
自定义路由对象,存储zset和list名称,以点对点的方式将消息从zset路由到正确的list
使用定时器维持路由
根据TTL规则实现消息延迟
- Redis分布式锁【10】:
加锁:Redis提供了一个只有在某个key不存在的情况下才会设置key的值的原子命令,该命令也能设置key值过期时间;
释放锁:Lua脚本,Redis执行该脚本的过程中,其他客户端的命令都需要等待该Lua脚本执行完才能执行。
【1】https://www.cnblogs.com/WuNaiHuaLuo/p/5225330.html 《Memcache缓存系统原理》
【2】https://blog.csdn.net/men_wen/article/details/70257207 《Redis源码剖析和注释(八)--- 对象系统(redisObject)》
【3】https://blog.csdn.net/men_wen/article/details/69396550 《Redis源码剖析和注释(二)--- 简单动态字符串》
【4】https://blog.csdn.net/men_wen/article/details/70229375 《Redis源码剖析和注释(七)--- 快速列表(quicklist)》
【5】https://www.jb51.net/article/136322.htm 《Redis中3种特殊的数据类型(BitMap、Geo和HyperLogLog)》
【6】https://blog.csdn.net/m0_38110132/article/details/76906422 《redis的持久化方式RDB和AOF的区别》
【7】https://blog.csdn.net/weixin_42711549/article/details/83061052 《Redis 的主从同步,及两种高可用方式》
【8】https://blog.csdn.net/gly1256288307/article/details/88739612 《如何保证Redis与数据库的数据一致性,看这一篇就够了》
【9】https://blog.csdn.net/wizard_rp/article/details/79303623 《基于Redis实现的延迟消息队列》、
【10】https://baijiahao.baidu.com/s?id=1623086259657780069&wfr=spider&for=pc 《如何优雅地用Redis实现分布式锁?》