一、Redis应用问题解决

① 缓存穿透:大量请求根本不存在的 key

② 缓存雪崩: redis中大量 key集体过期

③ 缓存击穿: redis中一个热点 key过期(大量用户访问该热点 key,但是热点 key过期)

④ mysql与 redis双写不一致:

1.1 缓存穿透

1.1.1 场景描述

缓存穿透:是指查询一个根本不存在的数据,缓存层和存储层都不会命中

1.1.2 解决方案

(1)缓存空对象

(2)布隆过滤器拦截
布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。

(3)对前端查询条件加密,比如商品id,使用md5加密,无规律,前端无法模拟;

(4)小批量数据设置可访问的名单(白名单):
使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。

(5)进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务

1.2 缓存击穿

1.2.1 场景描述

key对应的数据存在,但在 redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后 端 DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB压垮。

1.2.2 解决方案

key可能会在某些时间点被超高并发地访问,是一种非常 “热点 ”的数据。这个时候,需要考虑一个问题:缓存 被 “击穿 ”的问题。

解决问题:

  • 1)预先设置热门数据:在 redis高峰访问之前,把一些热门数据提前存入到 redis里面,加大这些热门数据 key的时长
  • 2)设置热点数据永远不过期
  • 3)使用互斥锁

1.3 缓存雪崩

1.3.1 场景描述

大量热点 key同时过期 或者 缓存服务故障,导致请求无法命中缓存,而是直接打到 DB,这个时候大并发的请 求可能会瞬间把后端 DB压垮。

缓存雪崩与缓存击穿的区别在于这里针对很多 key缓存,前者则是某一个 key

1.3.2 正常访问

1.3.3 缓存失效瞬间

1.3.4 解决方案

缓存失效时的雪崩效应对底层系统的冲击非常可怕!

解决方案:

  • 1)构建多级缓存架构: nginx缓存 +本地缓存 + redis缓存
  • 2)考虑用队列或者锁的方式,保证缓存单线程写,但这种方案可能会影响并发量
  • 3)双 key策略,主 key设置过期时间,备 key不设置过期时间,当主 key失效时,直接返回备 key值。
  • 4)避免给大量的数据设置相同的过期时间,比如我们可以在原有的失效时间基础上增加一个随机值,比如 1-5分钟随机,这样每一个缓存的过期时间的重 复率就会降低,就很难引发集体失效的事件。

  • 5)服务降级

  • 6)业务系统中实现服务熔断或请求限流机制

1.4 MySQL与 Redis双写一致性方案

1.4.1 一致性

我们在这里说的一致性,是 redis和 MySQL数据的一致性。 一致性有三种模型:

  • 强一致性
  • 弱一致性
  • 最终一致性

1.4.2 缓存模式

三个经典缓存模式

  • Cache-Aside Pattern (旁路缓存模式)
  • Read-Through/Write through (读取透写模式 )
  • Write behind (写后模式)

1.4.2.1 Cache-Aside (旁路缓存)

Cache-Aside读流程

1、读的时候,先读缓存,缓存命中的话,直接返回数据

2、缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应

Cache-Aside写流程

1、更新数据库

2、删除缓存。

1.4.2.2 Read-Through/Write-Through(读写穿透)

Read/Write Through 模式中,服务端把缓存作为主要数据存储。应用程序跟数据库缓存交互,都是通过抽 象缓存层完成的。

Read-Through读流程 1、从缓存读取数据,读到直接返回 2、如果读取不到的话,从数据库加载,写入缓存后,再返回响应。

Read-Through实际只是在 Cache-Aside 之上进行了一层封装,它会让程序代码变得更简洁,同时也 减少数据源上的负载。

Write-Through 写流程

Write-Through模式下,当发生写请求时,也是由 缓存抽象层 完成数据源和缓存数据的更新。 流程如下:

1.4.2.3 Write behind (异步缓存写入)

Write-Behind模式下,应用程序在写入数据时首先将数据写入缓存,然后异步地将数据写入数据源。

相同 :都是由 Cache Provider来负责缓存和数据库的读写

不同: Read/Write Through是同步更新缓存和数据的, Write Behind则是只更新缓存,不直接更新数据 库,通过批量异步的方式来更新数据库。

这种方式下,缓存和数据库的一致性不强,对一致性要求高的系统要谨慎使用。但是它适合频繁写的场景, MySQL的 InnoDB Buffer Pool机制就使用到这种模式。

1.4.3 不一致问题分析

1.4.3.1 先写数据库再更新缓存不一致问题

更新缓存相对于删除缓存的劣势:

  • 如果写入的缓存值,是经过复杂计算才得到的话,更新缓存频率高的话,就浪费性能了;
  • 在写数据库场景多、读数据场景少的情况下,数据很多时候还没被读取到,又被更新了,这也浪费了性能。

1.4.3.2 先删缓存,读的时候再写不一致问题

下图:缓存和数据库的数据不一致。缓存保存的是老数据,数据库保存的是新数据。

1.4.4 不一致问题解决

1.4.4.1 缓存延时双删

1.4.4.2 删除缓存重试机制

1.4.4.3 读取 binlog异步删除缓存

binlog-->canal-->mq ACK机制确认更新消息 ,删除缓存

二、Redis运维问题及优化案例