Redis基础知识(学习笔记20--高并发场景)

一. 高并发场景下常见的3种问题

1.1 缓存穿透

当用户访问的数据既不存在缓存中也不在数据库中时,就会导致每个用户查询都会“穿透”缓存“直抵”数据库。这种情况就成为缓存穿透。当高并发的请求到达时,缓存穿透不仅增加了响应时间,而且还会引发对DBMS的高并发查询,这种高并发查询很可能会导致DBMS的奔溃。
缓存穿透产生的主要原因有两个:一是在数据库中没有相应的查询结果,二是查询结果为空时,不对查询结果进行缓存。所以,针对以上两点,解决方案也有两个:
* 对非法请求进行限制;
* 对结果为空的查询给出默认值。

1.2 缓存击穿

对于某一个缓存,在高并发情况下若其访问量特别巨大,当该缓存的有效时间达到时,可能会出现大量的访问都要重建该缓存,即这些访问请求发现缓存中没有该数据,则立即到DBMS中进行查询,那么这就有可能会引发对DBMS的高并发查询,从而导致DBMS的崩溃。这种情况称为缓存击穿,而该缓存数据称为热点数据。
对于缓存击穿的解决方案,较典型的是使用“双重检测锁”机制。

1.3 缓存雪崩

对于缓存中的数据,很多都是有过期时间的。若大量缓存的过期时间在同一很短的时间段内几乎同时达到,那么在高并发访问场景下就可能会引发对DBMS的高并发查询,而这将可能直接导致DBMS的奔溃。这种情况称为缓存雪崩。
对于缓存雪崩没有很直接的解决方案,最好的解决方案就是预防,即提前规划好缓存的过期时间。要么就是让缓存永久有效,当DB中数据发生变化时清除相应的缓存。如果DBMS采用的是分布式部署,则将热点数据均匀分布在不同数据库节点中,则可能到来的访问负载均衡开来。

二. 数据库缓存双写不一致

以上三种情况都是针对高并发 读 场景中可能会出现的问题,而数据库缓存双写不一致问题,则是在高并发 写 场景下出现的问题。
对于数据库缓存双写不一致问题,以下两种场景均有可能会发生:

2.1 “修改DB 更新缓存”场景

对于具有缓存warmup功能的系统,DBMS中常用数据的变更,都会引发缓存中相关数据的更新。

在高并发 操作场景下,若多个请求要对DBMS中同一个数据进行修改,修改后还需要更新缓存中相关数据,那么就有可能出现缓存与数据库中数据不一致的情况。

概况举例如下,

理想情况下的访问:

异常情况

2.2 "修改DB 删除缓存”场景

在很多系统中是没有缓存warmup功能的,为了保持缓存与数据库数据的一致性,一般都是在对数据库执行了写操作后,就会删除相应缓存。
在高并发 读写 请求场景下,若这些请求对DBMS中同一个数据的操作既包含写也包含读,且修改后还要删除缓存中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况。

概况举例如下:

仍然为秒杀系统,库存stock的值为10,请求a要秒杀3个商品,请求b仅查看商品剩余数量,暂时不参与抢购。

理想情况下的访问:

 异常情况(即 数据库与缓存双写数据不一致问题)

 三. 解决方案

3.1 延迟双删

延迟双删方案是专门针对于“修改DB删除缓存”场景的解决方案。但该方案并不能彻底解决数据不一致的状况,其只可能降低发生数据不一致的概率。

延迟双删方案是指,在写操作完毕后会立即执行一次缓存的删除操作,然后再停止一段时间(一般为几秒)后再进行一次删除。而两次删除中间的间隔时长,要大于一次缓存写操作的时长。

 难点在于双删之间的停顿时间大小的设置。

3.2 队列

以上两种场景中,之所以会出现数据库与缓存中数据不一致,主要是因为对请求的处理出现了并行。只要将请求写入到一个统一的队列,只有处理完一个请求后才可以处理下一个请求,即 系统对用户的请求处理串行化,这样就可以解决数据不一致的问题。

主要问题:并发性差。

3.3 分布式锁

分布式锁是控制分布式系统间同步访问共享资源的一种方式,其可以保证共享资源在并发场景下的数据一致性。

使用队列的串行化虽然可以解决数据库与缓存中数据不一致,但系统失去了并发性,降低了性能。使用分布式锁可以在不影响并发的前提下,协调各处理线程间的关系,使数据库与缓存中的数据达成一致性。

只需要对数据库中的这个共享数据的访问,通过分布式锁来协调对其的操作即可。

4. 高性能 方略 

此段落来自 京东技术公众号 《万字长文浅谈三高系统建设方法论和实践》【https://mp.weixin.qq.com/s/KexjVJOsGYN50MxqzVT3mA】

针对缓存和数据库的结合使用,我们一般需要识别出系统是读多写少的系统,还是写多读少的系统。

4.1 读多写少的系统

针对读多写少的系统,我们一般采用同步更新数据库,后删除缓存;数据库来应对写的流量,缓存来应对读的流量,提高读的性能;此种方案我们是以数据库数据为主,缓存数据为辅,这是前司大部分团队采用的技术方案。

 4.2 写多读少的系统

针对写多读少的系统,我们一般采用同步更新缓存,异步更新数据库,通过缓存来进行抗写的流量,异步化更新数据库,通过缓存和异步化提高系统的性能;此种技术方案以缓存数据为主,数据库数据为辅,这是我了解到的京东物流这边大部分团队的技术方案,例如我们物流平台-统一平台小组的订运关系单据的存储采用的就是这种技术方案。

 4.3 写优化:秒杀场景下的异步化

针对于这种流量洪峰下的秒杀场景,对于接单接口的性能是很大的考验,所以接单接口不会有很多同步交互的复杂逻辑。我们一般都是先异步将订单接下来,返回给用户成功,通过消息队列来削峰处理订单,缓存存储相关sku的库存,当扣减库存成功后,再短信通知用户支付订单。

4.4 热key处理

垂直的扩展,在百亿补贴,大促,秒杀场景下,相关sku会成为访问的热点,如果将相关sku存储到某个单一的分片则可能出现流量访问倾斜的问题,某个分片会承担大部分的流量出现性能变差甚至分片被打垮的情况;针对热key的问题我们可以采用如下两种解决方案:

  • 本地缓存:在应用层增加本地缓存;先查本地缓存,本地缓存没有查询分布式缓存,分布式缓存没有查询数据库;

  • 机数法:针对某个key,我们可以在这个key后面增加一个随机数,比如增加两位的随机,就可以将该key分散到100个分片上,避免热点分片。

总之无论是本地缓存还是key+随机数,都是将热key分散到不同的节点上来分散流量,提高并发的同时,也避免流量倾斜,将某个节点打垮。

 

学习笔记--参阅特别声明

1.【Redis视频从入门到高级】

【https://www.bilibili.com/video/BV1U24y1y7jF?p=11&vd_source=0e347fbc6c2b049143afaa5a15abfc1c】

2.《万字长文浅谈三高系统建设方法论和实践》

【https://mp.weixin.qq.com/s/KexjVJOsGYN50MxqzVT3mA】

posted @ 2024-08-29 08:41  东山絮柳仔  阅读(33)  评论(0编辑  收藏  举报