redis过期数据删除的机制
过期键的删除策略
- 立即删除:(对CPU不友好)
- 惰性删除:取值的时候,先检查是否过期,过期再删除(浪费内存)
- 定时删除:每隔一段时间对,expires字典进行检查,删除里面的过期键
redis使用的策略
- 惰性删除+定期删除
redis的数据结构有哪些
- String(字符串)
- List(列表)->双向链表
- Hash(字典)->一般有两个hashtable ,通常就一个hashtable有值;在字典缩容扩容时,需要分配新的hashtable ,然后渐进式搬迁
- 大字典的扩容比较费时间,徐娅重新申请新的数组,把旧字典的所有链表的元素连接到新的数组下面,O(n),对于单线程的redis,难以承受,所以采用渐进式rehash小步搬迁
- 当元素的个数等于第一维数组的长度,就会开始扩容,扩容到原数组的两倍,如果redis正在bgsave,为了减少内存也过多分离,redis尽量不去扩容,如果达到长度的五倍,强制扩容;元素少于10%缩容,不考虑是否bgsave
- Set(集合)
- Sorted Set(有序集合)
为什么要用跳跃表
- 要实现随机的插入和删除,不宜使用数组实现
- **性能考虑:**在高并发的情况,树形结构要考虑rebalance的操作,而跳跃表只涉及局部
- **实现考虑:**在复杂度和红黑树相同的情况下,跳跃表实现更简单直观
分布式锁
场景
- 避免不同的节点重复相同的工作
- 避免破坏数据的正确性
锁超时
额外设置一个超时时间,来保证服务的可用性
布隆过滤器
布隆过滤器说值存在->这个值可能不存在
布隆过滤器说不存在->一定不存在
场景
- 大数据判断是否存在
- 解决缓存穿透
- 爬虫/邮箱等系统的过滤
- 推荐去重
持久化的过程
- 客户端向数据库发送写命令(数据在客户端的内存)
- 数据库接受客户端的写请求(数据在服务器的内存)
- 数据库调用系统API将数据写入硬盘(数据在内核缓冲区)
- 操作系统将写缓冲区传输到磁盘控制器(数据在磁盘缓冲区)Linux默认30s后进入下一步
- 操作系统的磁盘控制器将数据写入实际的物理媒介中(数据在磁盘中)
30s不是redis可以承受的,如果发生故障,30s内写入的数据都可能丢失,解决方案:fsync:该命令强制内核将缓冲区写入磁盘,每次调用都会阻塞等待直到IO完成,一般1s左右执行一次。
Redis中的两种持久化方式
- RDB快照
持久化的同时,内存数据结构还在变化,咋办?
使用系统多进程COW(copy on write)机制|fork函数
简单的说:在持久化的时候调用glibc的函数fork产生一个子进程(复制一个进程,主进程子进程共享内存里面的代码块和数据段)
快照持久化交给子进程处理,父进程继续处理客户端请求;此时子进程相应的页面是没有变化的,进程产生一瞬间的数据
- AOF(Append Only File - 仅追加文件)
每次执行修改内存的写操作时,都会记录该操作
当Redis收到客户端修改指令时,先进行参数校验,逻辑处理如果没问题就立即将该指令文本存储到AOF日志中,(先执行指令再将日志存盘->与Mysql等不同)
AOF重写 bgrewriteaof
原理:开辟一个子线程对内存进行遍历转换成一系列的Redis操作指令,序列化到一个新的AOF日志文件。序列化完成后再将操作期间发生的增量AOF日志追加到这个新的 AOF日志文件
fsync 强制从内核缓存刷到磁盘
AOF日志进行写时,实质上时将内容写到内核为文件描述符分配的内存缓存中,然后内核会异步将脏数据刷回磁盘
Redis 主从复制
作用
- 数据冗余
- 故障恢复
- 负载均衡
- 高可用基石
三个阶段:准备阶段-数据同步阶段-命令传播阶段
SYNC(非常耗时)
- 主服务器 执行BGSAVE 生成RDB 消耗主服务器大量的CPU、内存、磁盘IO资源
- 主服务器将生成的RDB发送给从服务器 消耗大量的网络资源
- 从服务器载入主服务器法来的RDB文件,载入期间,从服务器因为阻塞而无法处理命令请求
断线重连再次调用SYNC不合理,于是引入PSYNC
- 全量复制
- 部分复制(主从节点分别维护一个复制偏移量)
Redis Sentinel 哨兵
哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的Redis节点,不存储数据
数据节点:主节点和从节点都是数据节点
哨兵的功能:
- 监控:不断检测主节点、从节点是否运作正常
- 自动故障转移:主节点不能工作算,自动故障专业,把其中的一个从节点升级为新的主节点
- 配置提供者:客户端在初始化时,通过链接哨兵来获得当前Redis服务的主节点地址
- 通知:哨兵可以将故障转移的结果发给客户端
选择新的主服务器的方法
- 在失效主服务器属下的从服务器当中, 那些被标记为主观下线、已断线、或者最后一次回复 PING 命令的时间大于五秒钟的从服务器都会被 淘汰。
- 在失效主服务器属下的从服务器当中, 那些与失效主服务器连接断开的时长超过 down-after 选项指定的时长十倍的从服务器都会被 淘汰。
- 在 经历了以上两轮淘汰之后 剩下来的从服务器中, 我们选出 复制偏移量(replication offset)最大 的那个 从服务器 作为新的主服务器;如果复制偏移量不可用,或者从服务器的复制偏移量相同,那么 带有最小运行 ID 的那个从服务器成为新的主服务器。
Redis集群
作用:
- 数据分区:突破单机内存大小限制,存储容量大大增加,提高响应能力
- 高可用:自动故障转移(类似于哨兵)
缓存雪崩
是什么?
同一时间大量缓存数据失效,导致原本访问Redis的请求全部去到数据库,造成数据库短期内CPU和内存压力变大,严重导致宕机
为什么
缓存数据设置的过期时间是相同的,并且Redis恰好将这部分数据全部删除
怎么解决
给缓存数据设置过期时间时加上一个随机值
ps:
另外对于 “Redis 挂掉了,请求全部走数据库” 这样的情况,我们还可以有如下的思路:
- 事发前:实现 Redis 的高可用(主从架构 + Sentinel 或者 Redis Cluster),尽量避免 Redis 挂掉这种情况发生。
- 事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache) + 限流(hystrix),尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
- 事发后:Redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
缓存穿透
是什么?
查询一个不存在数据库中的数据,缓存一定不存在,绕过Redis请求数据库
为什么?
请求的数据大量不命中缓存,导致走数据库
怎么解决
- 使用布隆过滤器提前拦截
- 把不存在的空对象也放到缓存中,
Redis早期版本为什么选择单线程?
- 单线程能带来更好的可维护性
- 单线程也能并发处理客户端请求(IO多路复用)
- redis服务中运行的绝大多数操作的性能瓶颈都不是CPU
Redis为什么这么快?
- 纯内存操作(不需要进行磁盘IO)
- 单线程,无锁竞争
- 多路I/O复用模型,非阻塞IO
- 高效的数据结构,加上底层做了大量优化
简述一下Redis常用的数据结构及实现[外链图片转存中…(img-416f5Fsl-1647871142317)]
Redis的SDS和C中的字符串相比有什么优势
- C获取字符串长度要O(n)
- C不能很好的杜绝缓冲区溢出/内存泄漏的问题
- C只能保存文本数据
SDS如何解决
- 多增加len表示当前字符串的长度0(1)
- 自动扩展空间
- 有效降低内存分配次数
- 二进制安全
字典如何实现?Rehash了解吗?
数组+链表 ;字典内部两个hashtable,通常情况只有一个hashtable有值 渐进式rehash
压缩列表
为什么节省内存,zset和hash容器对象会在元素个数较少的时候,采用压缩列表进行存储。压缩列表时一块连续的内存空间,元素之间紧紧挨着,没有任何冗余间隙。