0%

redis-常见问题

一、穿透

数据库不存在,redis不存在

  • key设置null值

  • 布隆过滤器(存在一定的误差)

二、击穿

某个key,数据库存在,redis不存在(首次请求、key过期),大量(相同key)的请求一次性进来

  • 过期时间随机
  • 锁方案(见下文)

三、雪崩

缓存击穿的情况*N,一批key,数据库存在,redis不存在(首次请求、key过期),大量(不同key)的请求一次性进来

  • 过期时间随机
  • 锁方案(见下文)

四、解决方案

布隆过滤器方案

1
2
3
4
5
6
7
1、客户端实现过滤拦截
2、客户端实现布隆算法,映射到redis的bitmap实现
3、使用redis的bloomfilter组件

问题:
1、只能key的映射增加不能删除(布谷鸟过滤器可以解决)
2、不存在的key有一定程度可能跟存在的key出现映射重合,存在一定的误差

锁方案

1
2
3
4
5
6
7
8
1、请求redis,没有值
2、抢锁setnx px
2.1、抢到锁的查询DB
2.2、抢不到锁的sleep(或者服务治理返回)
3、更新redis数据,删除锁
4、sleep苏醒的线程回到第一步

问题:引入分布式锁,需要解决这个问题

分布式锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
使用SET key value [EX seconds] [PX milliseconds] [NX] 或 setnx + EXPIRE 
1.setnx
2.过期时间
3.多线程(守护线程)延长过期

问题:
1、线程使用setnx抢占之后可能宕机造成锁未释放,出现死锁?
引入过期时间

2、业务执行时间过久,造成锁已失效,期间其他线程抢占了锁,当前线程执行解锁误删其他线程的锁?
设置value当前线程的操作标识(可以是随机数),删除时取出该值进行判断,看是否为当前线程持有的锁,是的话才进行删除

3、当前线程取出value值判断锁为本线程持有,到删除之间存在时间差,可能因为锁失效缘故造成其他线程再次期间抢占到了锁?
保证get操作和del操作的原子性,使用lua脚本或者事务

4、当前业务未执行完,锁已经失效?
多线程监控(守护线程),对key延长失效时间,可以使用redisson解决方案

五、缓存双写一致

先更新数据库,后更新缓存

由图可知:

  • 请求A先更新DB
  • 请求B也更新DB
  • 但是因为网络等原因,B却比A更早更新了缓存。
  • 导致数据的不一致,出现脏数据

先更新数据库,后删除缓存

  • 更新DB成功
  • 删除缓存失败
  • 导致缓存数据为旧数据,出现脏数据

先更新缓存,后更新数据库

  • 更新缓存成功
  • 更新DB失败
  • 导致数据不一致,出现脏数据

先删除缓存,后更新数据库

由图可知:

- 如果同时有一个请求A先删除,再进行更新操作
- 中间另一个请求B进行查询发现没有缓存,查询了DB更新了缓存
- 之后A再更新DB
- 导致数据的不一致,出现脏数据

解决方案

1
2
1、消息队列
2、canal