Redis 做缓存虽减轻了 DBMS 的压力,减小了 RT,但在高并发情况下也是可能会出现各 种问题的。
当用户访问的数据既不在缓存也不在数据库中时,就会导致每个用户查询都会“穿透” 缓存“直抵”数据库。这种情况就称为缓存穿透。一个两个请求无所谓,当高并发的访问请求到达时,缓存穿透不 仅增加了响应时间,而且还会引发对 DBMS 的高并发查询,这种高并发查询很可能会导致DBMS 的崩溃。
缓存穿透产生的主要原因有两个:一是在数据库中没有相应的查询结果,二是查询结果为空时,不对查询结果进行缓存。所以,针对以上两点,解决方案也有两个:
对非法请求进行限制 。
对结果为空的查询给出默认值 。
可以使用布隆过滤器解决缓存穿透的问题

对于缓存中的数据,很多都是有过期时间的。
解决办法
事前:尽量保证整个 Redis 集群的高可用性,发现机器宕机尽快补上,选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉, 通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
事后:利用 Redis 持久化机制保存的数据尽快恢复缓存
以上三种情况都是针对高并发读场景中可能会出现的问题,而数据库缓存双写不一致问 题,则是在高并发写场景下可能会出现的问题。 对于数据库缓存双写不一致问题,以下两种场景下均有可能会发生:
对于具有缓存 warmup 功能的系统,DBMS 中常用数据的变更,都会引发缓存中相关数 据的更新。在高并发写请求场景下,若多个请求要对 DBMS 中同一个数据进行修改,修改后 还需要更新缓存中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况。


第一种没有问题。
第二种:a修改DB中的数据后stock=7,刚想从本地缓存写入redis缓存,此时出问题了(有可能是时间片到了也有可能redis出现问题),这是b过来了,也修改了数据stock=2,然后更新缓存也是2,然后出现问题的a请求又好了,更新redis stock为7。(覆盖了2)
最经典的缓存+数据库读写的模式,就是 预留缓存模式Cache Aside Pattern。
题外话:
本地缓存和Redis缓存是两种不同的缓存方式。
本地缓存是指将数据缓存在本地内存中,通常使用内存缓存库(如Memcached、Ehcache等)来实现。本地缓存的优点是访问速度快,缓存的数据可以快速地被读取,同时数据在本地内存中,可以减轻数据库的压力。然而,本地缓存的缺点是容易受到服务器重启、应用重启等因素的影响,同时可用空间有限。
Redis缓存是一种基于内存的(也可以持久化到磁盘)键值对存储系统,具有高性能、高可用性和可扩展性等优点。Redis可以作为分布式缓存来使用,可以减轻数据库的压力并提高应用程序的响应速度。Redis还提供了多种数据结构,如字符串、哈希、列表、集合和有序集合等,还可以设置过期时间,可以满足复杂的应用场景。
相比之下,本地缓存通常只适用于单机环境,适用于只有一个应用程序使用的数据,而Redis适用于分布式、多应用程序场景下的数据缓存。虽然Redis的性能和可用性优于本地缓存,但是使用Redis也需要考虑到数据一致性、网络延迟等问题。因此,具体的缓存方案应该根据应用场景来选择。
在很多系统中是没有缓存 warmup 功能的,为了保持缓存与数据库数据的一致性,一般 都是在对数据库执行了写操作后,就会删除相应缓存。
在高并发读写请求场景下,若这些请求对 DBMS 中同一个数据的操作既包含写也包含读, 且修改后还要删除缓存中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况。
问题:

延迟双删方案是专门针对于“修改 DB 删除缓存”场景的解决方案。但该方案并不能彻 底解决数据不一致的状况,其只可能降低发生数据不一致的概率。
延迟双删方案是指,在写操作完毕后会立即执行一次缓存的删除操作,然后再停上一段 时间(一般为几秒)后再进行一次删除。而两次删除中间的间隔时长,要大于一次缓存写操作的时长。