一张优惠券引发的血案

本文涉及的产品
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
简介: 整个优惠券中心分为前端和后端,小灰所负责的是后端RPC接口的开发。接口中包含“查券”和“领券”两个方法,项目大体结构如下。


640.jpg640.jpg640.jpg640.jpg

           

一个月前——



640.jpg640.jpg


整个优惠券中心分为前端和后端,小灰所负责的是后端RPC接口的开发。接口中包含“查券”和“领券”两个方法,项目大体结构如下图:

640.jpg640.jpg

image.gif


两周后——


       小灰:看,这是优惠券查询功能的效果!        

image.gif

640.jpg


 小灰:看,这是优惠券领取功能的效果!  


image.gif

640.jpg640.jpg


三天后——


640.jpg640.jpg640.jpg640.jpg640.jpg

小灰原本的优惠券查询接口是这样实现的:

640.jpg

image.gif

优惠券列表在Redis中以List的形式存储,查询时的逻辑很简单:


1.查询缓存,如果缓存存在,返回结果

2.缓存不存在,查询数据库

3.把查询数据库的结果循环放入缓存

然而,当某个时间点缓存不存在,请求量又很大的时候,会出现缓存并发的问题。也就是多个线程会重复去查询DB,又重复去更新缓存。(注意,这并不是缓存击穿,很多人在这两个概念上混淆。)

这其中重复查询DB是次要问题,而重复更新缓存则是主要问题。假如有两个线程同时进入上述的第三个阶段,各自进行rpush操作,那么最终会在优惠券列表的缓存中插入两组同样的数据。

怎么解决呢?用Java的锁机制?显然不行,因为线上环境通常都是多个服务器组成的集群。于是小灰想到了利用分布式锁。

image.gif

640.jpg

所谓分布式锁有很多种,可以利用ZooKeeper、MemCache、Redis来实现。其中Redis的方式比较简单,无非是利用一个服务器之间共享的Key,以及Setnx指令。

当第一个线程执行Setnx,会存储对应的键值,相当于成功获得锁。当后续再有线程对同于的Key执行Setnx指令,则会返回空,相当于抢锁失败。同时,为了防止一个线程因意外情况而长久把持着锁,程序对Key设置了1秒的过期时间。


归纳一下修改后的逻辑:


1.查询缓存,如果缓存存在,返回结果

2.缓存不存在,查询数据库

3.争夺分布式锁

4.成功获得锁,把查询数据库的结果循环放入缓存

5.释放分布式锁

640.jpg640.jpg


三天后——


640.jpg640.jpg640.jpg640.jpg

image.gif


640.jpg


诡异的bug又重现了,因为小灰上次的改动仍然存在一个致命的漏洞。在这里我们假定缓存不存在,刚好有两个线程A和B一后一先进入到代码块。


第一阶段,线程A刚开始查询优惠券缓存,线程B正尝试获取分布式锁:

             

image.gif640.jpg


第二阶段,由于缓存不存在,线程A开始查询数据库,线程B成功获得锁,开始更新缓存:

640.jpg

image.gif


第三阶段,线程A尝试获得分布式锁,而线程B已经释放分布式锁


image.gif640.jpg


第四阶段,线程A获得了锁,又一次更新缓存,而线程B已经成功返回:

640.jpg

image.gif

                       

就这样,缓存被重复更新了两次,所以再次出现数据重复的bug。

这种局面如何破解呢?其实不难,只需在线程成功得到锁以后,再次判断优惠券缓存的存在:


image.gif640.jpg


归纳一下修改后的逻辑:

1.查询缓存,如果缓存存在,返回结果

2.缓存不存在,查询数据库

3.争夺分布式锁

4.成功获得锁,再次判断缓存的存在

5.如果缓存仍旧不存在,把查询数据库的结果循环放入缓存

6.释放分布式锁

这种二次判断存在性的机制有一个专门的名字,叫做双重检测。该方法在线程安全的单例模式中也常常被用到。



image.gif640.jpg640.jpg


小灰的回忆告一段落——


640.jpg640.jpg640.jpg

image.gif


几点补充:


1.文中所使用的分布式锁,其实并不是“正宗”的分布式锁,当线程争夺锁失败的时候,会直接返回查询DB的结果,而不会依靠自旋机制来等锁。

2.为什么优惠券列表的信息要使用List类型来存入缓存,而不是把整个列表存为一个很长的Json字符串?这是由于业务需要,使用List在某些情况下更方便对单个优惠券信息进行修改(LSET指令)。

3.为什么优惠券列表的信息不使用Redis的Set或者Hash数据类型来存储,实现自动去重呢?对于Set类型,去重前需要对比整个字符串是否完全相同,而每一张优惠券是一个较长的Json字符串,对比的效率会比较低。使用Hash倒是可以实现高效的去重,但并未在根本上解决重复更新的问题。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
11月前
商家券
商家券是一种由商家发行的优惠券,通常用于吸引顾客购买商品或服务。商家券可以用于特定商品或服务的折扣、满减、赠品等促销活动。
84 2
|
3月前
|
关系型数据库 MySQL 应用服务中间件
拼团+秒杀+优惠折扣+个人免签双端商城源码
可用拼团+秒杀+优惠折扣+个人免签双端商城源码,全功能完美双端,对接个人免签支付。 这款商城源码非常完整,整体也非常简洁,功能全面,没有那么多冗杂的多余页面和无用代码,拿到后优化了下整体代码,对接了免签支付。
22 1
|
4月前
|
存储 弹性计算 物联网
阿里云代金券、提货券、优惠券、储值卡领取及使用常见问题汇总
阿里云代金券、优惠券、提货券、储值卡是是阿里云最常见的几个优惠券种,官方发布这些券种的目的旨在为更多用户提供优惠上云的福利,代金券、优惠券、提货券、储值卡在性质及领取和使用上既有相同也有不同,下面是小编根据官方2024年的文档资料整理汇总的阿里云代金券、优惠券、提货券、储值卡领取及使用常见问题。
阿里云代金券、提货券、优惠券、储值卡领取及使用常见问题汇总
|
4月前
|
存储 弹性计算 数据库
阿里云优惠券是什么?2024年阿里云优惠券领取地址及使用教程汇总
阿里云作为国内领先的云计算服务提供商,为广大用户提供了丰富的云产品和解决方案。为了吸引用户上云,阿里云经常推出各种优惠活动,其中最受用户欢迎的就是阿里云优惠券。那么,阿里云优惠券究竟是什么呢?我们又该如何领取它呢?本文将为大家详细解答。
704 2
|
弹性计算 固态存储 小程序
阿里云优惠券最新领取(2023年最新)
阿里云优惠券最新领取(2023年最新),2023年阿里云优惠活动大全如阿里云新人特惠、学生服务器、免费云服务器、域名1元购、阿里云CLUB领券中心等,阿里云服务器包括云服务器ECS和轻量应用服务器配置优惠价格和购买攻略完整版,阿里云百科分享阿里云服务器优惠活动大全和代金券领取
1783 0
阿里云优惠券最新领取(2023年最新)
|
存储 监控 NoSQL
阿里云满减优惠券,适用于阿里云250种云产品,领取及使用介绍
阿里云满减优惠券也称上云礼包,是一种满减抵扣性质的优惠券,只要用户领取之后,购买阿里云产品时订单金额达到优惠券的满减抵扣条件即可使用优惠券获得相应金额的满减优惠,目前满减优惠券最低可抵扣20元,最高可抵扣1000元,而且最重要的,满减优惠券不仅适用于阿里云服务器类产品,同时也适用于其他阿里云官方自营的各种云产品,根据官方公布的规则,有250种阿里云产品都支持购买时使用满减优惠券。
641 0
阿里云满减优惠券,适用于阿里云250种云产品,领取及使用介绍
R7-8 魔法优惠券
R7-8 魔法优惠券
72 0
|
弹性计算 固态存储 小程序
阿里云优惠服务器及特价活动大全(可领代金券)
阿里云优惠服务器及特价活动大全(可领代金券),2023年阿里云优惠活动大全如阿里云新人特惠、学生服务器、免费云服务器、域名1元购、阿里云CLUB领券中心等,阿里云服务器包括云服务器ECS和轻量应用服务器配置优惠价格和购买攻略完整版,阿里云百科分享阿里云服务器优惠活动大全和代金券领取:
176 0
阿里云优惠服务器及特价活动大全(可领代金券)
|
弹性计算
阿里云服务器续费优惠券可领代金券和续费折扣
阿里云服务器续费优惠券300元免费领取,云服务器ECS和轻量应用服务器续费优惠折扣
457 0
阿里云服务器续费优惠券可领代金券和续费折扣
|
数据库 对象存储 黑灰产治理
阿里云购物车在哪?购物车满减代金券如何参与?
阿里云购物车满减代金券可领取三张,总面值1950元,分别为250元代金券、500元代金券和1200元代金券,阿小云来详细说下购物车使用方法及满减代金券说明
557 0
阿里云购物车在哪?购物车满减代金券如何参与?