Redis常见问题(一)

文章目录

1.redis缓存双写一致性问题

redis和mysql的数据需要达成一致,确保系统在任何时刻都能提供"正确"且"符合逻辑"的数据。

1. 常见的2种思路是

  • 先删缓存数据再更新数据库数据:正常情况A线程先删缓存数据再更新数据库数据,B线程查缓存为空,去数据库找数据更新缓存。而当A线程删缓存数据,在更新数据库前被B线程查缓存为空然后立马找数据库数据(旧数据)最后A线程再更新数据库,导致缓存为旧数据数据库为新数据。
  • 先更新数据库再去删缓存数据:正常情况A线程更新数据库数据后删除缓存数据,B线程查缓存为空,去数据库找新数据更新缓存。而当B线程先查缓存数据(旧数据)然后A线程更新数据库新数据,然后删除缓存,B线程操作缓存更新之前的旧数据。

为什么是"删除"而不是"更新"缓存?

  • 性能开销: 如果缓存结构复杂(比如一个大的 JSON),每次改数据库都要重新计算并写入缓存,代价太高。
  • 并发安全: 两个写操作并发时,如果更新缓存,可能会因为网络延迟导致旧值覆盖新值;而"删除"操作是幂等的,能有效避免脏数据。

不论是操作顺序,都可能因为线程操作导致不一致,此时引入延迟双删

2. 延时双删 (Delayed Double Delete)

在"先更新数据库,再删除缓存"的情况下,如果在删除缓存前,有一个读请求进来了,它会把旧数据重新写回 Redis。为了解决这个极短时间窗口内的不一致,可以使用延时双删。

先删除 Redis 缓存。

再更新 MySQL 数据库。

休眠一小段时间(比如 500ms)。(MySQL主从同步需要时间)

再次删除 Redis 缓存。

痛点: 休眠时间很难确定,且会降低接口的响应速度(除非异步删除)。

3.在强一致性场景用锁

使用redisson自带的读写锁,共享锁(ReadLock)共享读和排他锁(WriteLock)阻塞读写,避免脏数据。

4.基于 Binlog 的异步更新

原理: 应用只负责更新 MySQL。通过一个中间件(如 Canal)伪装成 MySQL 的从节点,监听 MySQL 的 Binlog。

流程:

  1. MySQL 发生变更。
  2. Canal 监听到变更,把数据发到消息队列(如 Kafka/RocketMQ)。
  3. 专门的缓存服务消费消息,更新/删除 Redis。
    优点: 业务代码干净,不需关心缓存逻辑;即使 Redis 宕机,消息队列也能保证最终能更新成功

2.redis持久化问题

  1. RDB (快照模式)
    RDB 是在指定的时间间隔内,将内存中的数据集快照写入磁盘。它就像是给数据"拍张照片"。
    工作原理: Redis 会 fork 一个子进程,由子进程将内存数据写入一个临时的二进制文件(.rdb),完成后替换旧文件。
    优点:
    文件紧凑: RDB 文件是压缩后的二进制文件,非常适合备份和全量同步。
    恢复极快: 相比 AOF,加载 RDB 文件的速度要快得多。
    缺点:
    数据丢失风险: 如果两次快照之间宕机,会丢失最后一次快照后的所有数据。
    性能开销: fork 进程在数据量巨大时可能会导致服务器瞬间卡顿。
  2. AOF (追加日志模式)
    AOF 以日志的形式记录每一个写操作指令。它不记录数据本身,而是记录**"怎么操作数据的"**。
    工作原理: 所有的写命令都会追加到 aof_buf 缓冲区中,然后根据配置策略刷写到磁盘(.aof 文件)。
    刷盘策略 (appendfsync):
    always:每次写操作都同步,最安全但性能最差。
    everysec(默认):每秒同步一次,兼顾性能和安全(最多丢 1 秒数据)。
    no:交给操作系统决定何时同步,性能最好但最不安全。
    AOF 重写 (Rewrite): 随着时间推移,AOF 文件会变得很大。Redis 会自动执行重写,剔除冗余命令(比如对同一个 Key 的多次修改简化为最后一次结果),从而减小体积。

RDB是一个快照文件,它把数据写到磁盘,当redis宕机要恢复数据可以注解从RDB 文件恢复。

AOF是追加文件,记录了操作命令,redis宕机恢复可以通过这个文件再一次执行命令。

RDB是二进制文件体积小恢复快,但是容易丢数据(每隔一段时间才快照),AOF慢一些丢数据风险小

混合持久化:它修改了 AOF 的格式,让 AOF 文件的开头是一段 RDB 格式的二进制数据(普通快照),结尾是增量的 AOF 命令(在快照生成期间产生的最新指令)。它先正常快照然后后面补新指令。

优点: 重启恢复速度极快(像 RDB),同时数据又非常全(像 AOF)。

3.redis数据过期策略

1.惰性删除:设置该key过期时间后不管,等需要该key时再检查是否过期,如果过期就删除,反之返回该key.

优点:对cpu友好,用到再检查

缺点:对内存不友好,key过期没有用一直在内存里面。

2.定期删除:每隔一段时间对一些key(一定数量随机key,最后会遍历完所有key)检查,删除过期key.

SLOW模式是定时任务,执行频率默认10hz每次不超过25ms

FAST模式执行频率不固定,间隔不低于2ms耗时不超过1ms

4.redis数据淘汰策略

当redis内存不够,此时向redis添加新key,redis会按照某一种规则将内存数据删掉。

8种淘汰策略:

noeviction(eviction:驱逐):不淘汰任何key,内存满不允许写新数据,默认策略

allkeys-lru(所有key里):淘汰最近最少使用的key

allkeys-lfu:淘汰访问频率最低的key

allkeys-random:随机淘汰key

volatile-lru(volatile:易变的,在设置了TTL的key):淘汰最近最少使用的key

volatile-lfu:淘汰访问频率最低的key

volatile-random:随机淘汰key

volatile-ttl:淘汰最快过期的key

  • LRU (Least Recently Used) ------ 最近最少使用
    核心逻辑: 记录每个 Key 最后一次被访问的时间戳。谁很久没被摸过了,就踢谁。
    缺点: 无法应对"突发流量"。如果一个 Key 一年没用,刚刚被点了一下,它就不会被淘汰;而一个非常火热的 Key 如果刚好 1 秒没被点,可能会被误杀。
  • LFU (Least Frequently Used) ------ 最不经常使用
    核心逻辑: 给每个 Key 带个"计数器"。访问次数越少,越容易被踢。
    优点: 真正保留了"高频"数据。即使一个 Key 刚才没被点,只要它历史点击率极高,依然能存活。
相关推荐
2301_781571422 小时前
Golang格式化输出占位符都有什么_Golang fmt占位符教程【通俗】
jvm·数据库·python
养肥胖虎2 小时前
RAG学习笔记(3):区分数据库检索与RAG的使用场景
数据库·ai·rag
_ku_ku_3 小时前
数据库系统原理 · 数据库应用开发 · 自学总结
数据库
No8g攻城狮3 小时前
【人大金仓】wsl2+ubuntu22.04安装人大金仓数据库V9
java·数据库·spring boot·非关系型数据库
山峰哥3 小时前
SQL慢查询调优实战:从全表扫描到索引覆盖的完整复盘
前端·数据库·sql·性能优化
代码中介商4 小时前
Redis入门:5大数据类型全解析
数据库·redis·缓存
渣渣盟4 小时前
数据库设计范式详解(纯小白版)
数据库·oracle·软考·数据库工程师
夜雪闻竹6 小时前
Cursor 对话导入:解析 SQLite 里的宝藏
数据库·sqlite·ai编程
hhb_6186 小时前
PL/SQL核心技术难点梳理与实战应用案例解析
数据库·sql
m0_470857646 小时前
PHP怎么实现工厂模式_Factory模式编写指南【指南】
jvm·数据库·python