Redis 缓存
Redis 各版本特性
- 2.8:scan指令、HyperLogLog
- 3.2:Geo
- 4.0:
- Redis-Cell
- 布隆过滤器
- AOF sync到硬盘
- 惰性删除(unlink/flushall async)
- 异步删除点(rename指令、key过期、LRU淘汰)
- 混合持久化
- 5.0
- Stream
- listpack
- LFU(最近最少访问频率)
redis 数据结构
- String、Hash、List、Set、ZSet
- 位图:单key存储不能超过512M,即offset最多2^32,存储计算:(offset/8/1024/1024)MB。
- 订阅:订阅者需要阻塞的读,下线期间数据是丢失的(redis 5.0能持久化)
- Geo:实现附近的人的功能
- Redis-Cell:分布式限流模块
- HyperLogLog:有误差的统计uv等,pfadd、pfcount。不能作布隆过滤器,因为如果判断不存在就加入集合了。
- 布隆过滤器:False is always false
redis 优化经验
- Redis优化经验
- list、hash、set、zset 的元素个数不超过8192,value大小不超过1MB
redis 实现队列有什么优点和缺点
- 优点:实现简单(LPUSH+BRPOP),可持久化,保证顺序
- 缺点:不能重复消费,消费确认ACK麻烦(需要额外支持),不支持广播模式
单 key 不宜超过 1MB
- Redis 深度历险:核心原理与应用实践
- 有三个原因:
- 在集群环境中,key 太大会导致数据迁移卡顿
- 内存分配时,扩容需要申请更大的内存,也会导致卡顿
- 被删除时,内存会被一次性回收,也会卡顿
- redis-cli -h 127.0.0.1 -p 7001 –-bigkeys -i 0.1,查找大 key 每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升
redis 的进程和线程
- Redis的多线程与多进程
- redis 数据处理是单进程单线程的。单线程的好处:cpu不是瓶颈、线程切换有开销、多线程有锁的问题。
- redis 的另外2个子进程:
- 子进程写快照rdb,用到了多进程的COW原理(子进程和父进程共享内存,修改的时候clone修改的部分再修改)
- 子进程AOF日志重写:直接基于内存转换成新的AOF日志,同时补上转换期间的增量日志,生成完后替换掉旧的AOF日志。
- redis 另外的线程:
- 日志线程
- AOF日志不是实时写入磁盘的,是定时1s,Redis AOF 持久化- Redis源码分析
rdb 和 aof 过程
- https://www.cnblogs.com/itdragon/p/7906481.html
- rdb是定时备份快照到dump.rdb,以子进程的cow的方式。配置文件:save 900 1表示900秒内有1个key更新就触发。
- aof是先实时记录到aof_buf(内存),写到磁盘的appendonly.aof取决于配置文件:
- appendfsync everysec,定时一秒写入AOF文件,(4.0后单独线程处理)
- appendfsync always,实时写入AOF文件
- appendfsync no,同步时间不确定
- aof重写:也是定时
- 以子进程重新生成整个aof文件,并压缩
- 同时写aof_buf和aof_rewrite_buf
- 生成完aof文件后追加aof_rewrite_buf,并替换老的aof文件
- 4.0后混合持久化,先rdb恢复,再aof恢复
redis 为什么快?(主要考察一个 IO 多路复用和单线程不加锁)
- 纯内存
- 非阻塞IO
- 避免线程切换和竞争消耗
一致性哈希是什么?节点较少时数据分布不均匀怎么办?
- 定义:在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。
- 节点较少时,每个节点多虚拟一个hash槽点。
key 的淘汰策略,怎么保证淘汰合理的 key?
- 惰性过期:访问 Key 时判断是否过期,过期则清除。CPU 友好,内存不友好
- 定期过期:隔一定时间,expires 字典中扫描一定数量的 Key,清除其中已过期的 Key。内存和 CPU 资源达到最优的平衡效果
- noeviction: 内存不足直接报错
- allkeys-lru: 所有key通用; LRU
- allkeys-random: 所有key通用; 随机删除一部分
- volatile-lru: expire的部分; LRU
- volatile-random: expire的部分; 随机删除一部分
- volatile-ttl: expire的部分; 即将过期的
Redis 底层数据结构
- Redis 设计与实现
- 图解redis五种数据结构底层实现
- String,整型直接用long,其他用【sds】(小字符串用embstr编码,大字符串用raw)
- Hash,单值长度>64或者元素数量>512,用【dict】,否则用【ziplist】
- List,单值长度>64或者元素数量>512,用【linkedlist】或【quicklist】,否则用【ziplist】
- Zset,单值长度>64或者元素数量>128,用【skiplist】,否则用【ziplist】
- Set,元素都是整型或者元素数量<=512,用【intset】,否则用【dict】
- 【sds】,对比C的char,获取字符串长度复杂度为O(1);不会造成缓冲区溢出
- 【ziplist】,为节约内存而开发的顺序型数据结构,内存是连续的
- 【dict】,两个哈希表,ht[1]只在对ht[0]进行rehash的时候使用
- 【linkedlist】,双端链表
- 【quicklist】,3.2才有,【linkedlist】和【ziplist】的结合。因为【linkedlist】指针占空间。
- 【intset】,底层实现为数组,有序、无重复的方式保存元素
- 【skiplist】,对比B+树,实现简单
- 【listpack】,5.0才有,对比【ziplist】,不会有级联更新问题,省了一个字段
Redis 的同步机制
主从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
估算Redis内存使用量
主从、哨兵Sentinel和集群Cluster
- Redis系列(一):Redis 主从同步集群模式
- Redis的高可用(原理篇)
- Redis5.x哨兵搭建手记
- 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解
- Redis Cluster原理
- 主从,从节点初次同步,主节点bgsave新的rdb文件“无盘复制”传输,后增量同步缓冲数据。断线重连,从节点发送缓存区的偏移量,从偏移位置同步。
- 哨兵,节点是奇数个,客户端通过哨兵来获取主节点和从节点的地址。节点变更时,哨兵会自动修改哨兵和监控节点的配置文件。
- 哨兵每秒 ping 一次 Master 结点、Slave 节点、Sentinel 进程
- 如果 ping 超时时间大于 down-after-milliseconds,则实例标记为主观下线
- 如果足够数量的哨兵都认为是主观下线,则实例标记为客观下线,即节点故障
- 如果有节点故障,就选举一个领头哨兵,负责故障处理。选举是先到先得,投票过半即可。
- 如果主节点故障,领头哨兵会选一个数据量最全的节点作为主节点,同时将原主节点变成它的从节点
- 集群,集群共16383槽,例如三主三从,三主均摊16383槽,从节点默认不分摊读请求。客户端通过“槽->节点”的信息也只能找到主节点。客户端首次连接会更新“槽->节点”的信息,后面信息变更是通过节点纠错。
- 没有选主算法,有两个算法:主宕机需要把从节点提升为主节点,用的Raft;如何检测主宕机,采用quorum+心跳的机制。
- 每个节点有2个端口,如6379有16379,16739是集群总线端口,用于和其他节点二进制通讯。
- 增加节点:先add-node,再reshard分配槽给新节点。
- 减少节点:先reshard把槽都分配出去,再del-node(先从后主)。
阿里云Redis使用规范
- 阿里云Redis使用规范
- redis集群,执行lua脚本时,所有key,必须在1个slot上,可以指定key的tag使其都在1个slot上
- 指定key的tag,key里面有{}包裹的就是tag