DRAFT 🍟Java
约 1450 字大约 5 分钟
2026-01-04
Redis
缓存穿透
攻击者查询多个不存在的 Id,绕过了 Redis,从而直达 MYSQL,导致了对数据库的巨大压力
解决方案:
使用布隆过滤器,在 Redis 缓存的过程中预热布隆过滤器
通过布隆过滤器,本质是通过多个 Hash(ID) = Index,将 ID 映射到 Bitmap 中对应的位置上 通过布隆过滤器的可能存在,没通过的 ID 一定不存在
对于不存在的数据,缓存一个空对象,设置一个较短的过期
接口限流
缓存击穿(点)
Redis 缓存的某一个 key 在某一个时间发生过期,导致大量的并发请求到了 DB,从而导致了 DB 的压力过大
解决方案:
- 互斥锁,一个人先去拿到这个锁,之后回填缓存数据,后面的人等待读缓存就行
- 逻辑过期,发现你过期,拿着锁开启一个新线程去 DB 拿数据,回填缓存,其他人继续读旧数据
缓存雪崩(面)
大量的 key 在同一时间过期,导致大量的请求打到 DB,从而导致 DB 压力过大
解决方案:
- 设置不同的过期时间,避免大量 key 在同一时间过期
- 搭建这个 Redis 集群,避免单点故障
Redis 的一致性问题
延迟双删
- 先删除缓存,在更新数据库:删除后马上去拿了数据库的旧数据
- 先更新数据库,在删除缓存:删除后马上去拿了数据库的旧数据
- (更新前拿数据,删完马上填数据)
分布式锁
- 读的时候,随便读,但是读写互斥
- 写的时候,加锁,更新数据库,更新缓存
入侵业务代码,通过 mq 监听,修改 Redis
通过 DB 的 binlog 日志,利用阿里的 canal 中间件,伪装为一个 MYsql 的从节点,实现 redis 的更新
Redis 的持久化的问题
通过 RDB,Redis database backup
save 通过主进程来; bgsave 通过 fork 一个子进程来实现对于文件的存储
AOF,Append Only File
Redis 删除策略
惰性删除
发现了过期才删除
定期删除
每隔一定的时间,定期检查删除 SLOW(25ms)/FAST(2ms)模式
实际策略:定期删除 + 惰性删除
Redis 数据淘汰策略
- noeviction
- volatile-ttl
- allkeys-random
- volatile-random
- allkeys-lru
- volatile-lfu
- allkeys-lfu
- 数据库有 1000 万数据,Redis 只能缓存 20w 数据,如何保证 Redis 中的数据都是热点数据?
使用 allkeys-lru(挑选最近最少使用的数据淘汰)淘汰策略,留下来的都是经常访问的热点数据
- Redis 的内存用完了会发生什么?
主要看数据淘汰策略是什么?如果是默认的配置(noeviction),会直接报错
分布式锁
问题场景
优惠券抢购:
- 单机环境:
synchronized或ReentrantLock即可解决 - 分布式环境:每个服务有独立 JVM,本地锁失效
Redis 分布式锁实现
// 加锁:SETNX + 过期时间(原子操作)
SET lock:coupon unique_value NX EX 30
// 释放锁:Lua 脚本保证原子性
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
end
return 0为了避免在处理业务时候,redis 的 key 提前过期,加一个 watch dog,在加锁的的时候,每隔 release time/3 做一次续期
红锁来解决 Redis 集群的问题:
只有超过 1/2 的结点都加上锁,才算成功加锁
MySQL
定义慢查询
慢查询是指执行时间超过预设阈值的 SQL 查询,通常用于识别和优化性能瓶颈。
- 使用开源工具 skywalking
- 在 mysql 中开启慢日志查询
- 如何分析呢?
定位到慢 SQL 后,使用 EXPLAIN 获取执行计划,重点关注:
- type:访问类型,性能由好到差:
system>const>eq_ref>ref>range>index>ALL - possible_keys / key:可用索引与实际使用的索引,若 key 为 NULL 表示未命中索引
- rows:预估扫描行数,越小越好
- Extra:额外信息,如
Using filesort、Using temporary表示需要优化
了解过索引么?
- 索引(index)是帮助 MySQL 高效获取数据的数据结构(有序)
- 提高数据检索的效率,降低数据库的 IO 成本(不需要全表扫描)
- 通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU 的消耗
B+数,非叶子节点负责索引、叶子节点负责存储数据、叶子结点是循环链表
索引的底层数据结构
- MySQL 的 InnoDB 引擎采用的 B+树的数据结构来存储索引阶数更多,路径更短
- 磁盘读写代价 B+树更低,非叶子节点只存储指针,叶子阶段存储数据
- B+树便于扫库和区间查询,叶子节点是一个双向链表
聚集索引与二级索引
- 聚簇索引(聚集索引):数据与索引存放在一起,B+ 树叶子节点保存整行数据,每张表有且仅有一个
- 非聚簇索引(二级索引):数据与索引分开存储,B+ 树叶子节点仅保存主键值,可存在多个(select * from user_table where name = 'Mike',先找二级索引 name 的 B+树,找到 ID,再找这个 ID 的 B+树)
回表查询:通过二级索引查到主键值后,再回到聚集索引中检索完整行数据的过程称为回表
