您现在的位置是:首页 >其他 >redis从零开始----缓存及三种问题和布隆过滤器网站首页其他

redis从零开始----缓存及三种问题和布隆过滤器

back2childhood 2024-06-17 10:20:00
简介redis从零开始----缓存及三种问题和布隆过滤器

缓存

用户的数据都是存储在数据库中的,而数据库又是存储在磁盘上的。众所周知,磁盘的读写速度是很慢的,如果用户的每次请求都要去访问磁盘,会大大降低系统性能,数据库也很容易崩溃。
为了避免频繁访问数据库,可以使用redis作为缓存,用户数据保存在redis中,而redis中的数据是保存在内存中的,大大提高了系统性能。
但是,这又引入了缓存的三个常见问题:

  • 缓存雪崩
  • 缓存击穿
  • 缓存穿透

缓存雪崩

概念

通常为了保证redis中的数据和数据库中的一致,会给redis中的数据添加过期时间,当缓存过期后,用户的请求会直接发往数据库,在数据库取得数据后重新写入redis缓存。当redis中的缓存在同一时间过期或redis出现故障后,redis无法处理用户请求,大量的用户请求会涌向数据库,导致数据库压力骤增,可能造成数据库宕机或系统崩溃。这就是缓存雪崩。

情况1:大量数据同时过期

解决办法1:均匀设置过期时间

可以把缓存的过期时间设为一个随机数,保证缓存不会同时过期

解决办法2:互斥锁

缓存过期后,用户想要访问数据库获取资源,就加一个互斥锁,在同一时间只能有一个请求来访问数据库并更新redis,未能获取互斥锁的请求就返回空值或等待锁。
实现互斥锁时还需要设置超时时间,防止一个请求拿到锁之后意外阻塞而导致所有请求都无法执行

解决办法3:后台更新缓存

redis不再设置过期时间,而是让缓存永久有效,将缓存更新的工作交给后台线程定时更新。
当内存紧张时,有些缓存数据会被淘汰,这时从用户的视角来说数据又丢失了。为了解决这种情况,有以下两种方案:

  • 后台线程不仅负责更新缓存,还要检查缓存数据是否有效,如果缓存因系统紧张而淘汰,就要马上从数据库更新缓存。但是检查缓存的时间间隔不能太短,应该在毫秒级。
  • 当业务线程发现缓存失效后,通过消息队列发送一条消息提醒后台线程,由后台线程检查缓存数据是否需要更新。这种方案用户体验较好。也适合做缓存预热(在业务未启动时就先把一部分数据缓存起来,而不是在缓存失败后才更新)。

情况2:redis宕机

这种情况也有以下两种方案解决:

  • 牺牲正常工作:
    • 服务熔断:暂停所有对缓存服务的访问,直接返回错误,保护数据库
    • 请求限流:只把少部分请求发给数据库,其余的请求直接拒绝
  • 可以构建redis缓存高可靠集群,如果主节点因为故障宕机,从节点可以切换成主节点,继续提供缓存服务,避免因redis宕机而导致的缓存雪崩

缓存击穿

概念

一般把数据库中会被频繁访问的数据称为热点数据,如果某个热点数据过期了,也会导致请求直接发给数据库,导致数据库被高并发的请求冲垮,这就是缓存击穿。

解决办法

  • 互斥锁方案:同上
  • 后台更新缓存:同上

缓存穿透

概念

以上两种情况只要重新构建了缓存就可以恢复正常,但是缓存穿透则不行。
当用户要访问的数据既不存在于缓存中也不存在于数据库中时,这种情况导致的数据库大量访问叫做缓存穿透。缓存穿透主要发生于这两种情况:

  • 误删除数据
  • 黑客故意访问不存在的数据

方案1:限制非法请求

在api入口处就判断请求参数是否合理,避免恶意请求进一步访问缓存和数据库

方案2:缓存空值或默认值

针对查询的数据在缓存中设置一个空值或默认值,也可以避免查询数据库

方案3:布隆过滤器

使用布隆过滤器给数据库中的数据做个标记,在请求到来时,可以通过查询布隆过滤器来检查快速判断数据是否在数据库中,redis自身也支持布隆过滤器。

番外:布隆过滤器

布隆过滤器由「n个哈希函数」和「一个初始值为0的位图」组成。

  • 首先,对于要插入数据库的元素,经过n个哈希函数计算得到n个哈希值
  • 然后将这n个哈希元素对位图长度取模,得到每个哈希值在哈希数组中的位置
  • 将对应位置的值设为1

向数据库中插入数据x,如果有3个哈希函数,计算结果分别是1、2、3,并将位图对应位置设为1。当应用要查询数据 x 是否数据库时,通过布隆过滤器只要查到位图数组的第 1、2、3位置的值是否全为 1,只要有一个为 0,就认为数据 x 不在数据库中。

由于哈希冲突的存在,布隆过滤器也不一定准确。比如,x数据在位图中对应的位置是1、2、3,有两种可能导致哈希冲突:y的数据也是1、2、3或者y的位置是1、2,z的位置是3、4。
也就是说,查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。