✅阿里的数据库能抗秒杀的原理

典型回答

其实,在阿里电商的秒杀等(据我了解,淘宝、天猫、猫超、大麦等都是这么干的)场景中,主要还是基于MySQL数据库在做扣减的,主要是因为这样做最可靠了(避免了redis扣减方案中的数据不一致、少卖等问题)。

但是我们都知道,数据库是抗不了热点行的并发更新的,于是阿里内部就对MySQL做了patch。

这个方案其实云上数据库RDS也支持了,所以我就可以讲了。(没开放的技术确实不敢讲,怕被请喝茶。。。)

这个技术叫做Inventory Hint,其实就是一个补丁。(官方介绍:https://help.aliyun.com/zh/rds/apsaradb-rds-for-mysql/inventory-hint

PS:这里主要是介绍通过Inventory Hint来提升热点更新的并发,但是并不意味着内容只用这个方案来抗热点并发,但是这个是基础,先把这个讲清楚。后续其他的方案,我也会挑一些能讲的介绍。

使用方法

他的用法很简单,只需要在正常的update语句中增加上特殊的hint语句就行了,如:

UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1)*/ T
SET c = c - 1
WHERE id = 1;

这里面的COMMIT_ON_SUCCESSROLLBACK_ON_FAILTARGET_AFFECT_ROW都是一些Hint语法:

  • COMMITONSUCCESS:当前语句执行成功就提交事务上下文。
  • ROLLBACKONFAIL:当前语句执行失败就回滚事务上下文。
  • TARGETAFFECTROW(NUMBER):如果当前语句影响行数是指定的就成功,否则语句失败。

hint:MySQL 中的 "Hint" 是一种特殊的语法,允许开发者向数据库引擎提供如何执行特定查询的额外信息或建议。这些提示不改变查询的结果,但可以影响查询的执行路径,比如如何选择索引、是否使用缓存等。使用 Hint 的目的是为了优化查询性能。

很显然,前面我们提到的这几个hint是阿里自己支持的。所以只有内部的数据库, 或者阿里云的RDS才支持。

原理介绍

当我们是使用COMMIT_ON_SUCCESS等hint标记了一条SQL之后,就相当于告诉MySQL内核,这行可能是热点更新。

于是,MySQL的内核层就会自动识别带此类标记的更新操作,在一定的时间间隔内,将收集到的更新操作按照主键或者唯一键进行分组,这样更新相同行的操作就会被分到同一组中。

1705316172477-39e212c9-0317-4110-8032-bd18d6002b32.png

为了进一步提升性能,在实现上,使用两个执行单元。当第一个执行单元收集完毕准备提交时,第二个执行单元立即开始收集更新操作;当第二个执行单元收集完毕准备提交时,第一个执行单元已经提交完毕并开始收集新一批的更新操作,两个单元不断切换,并行执行。

根据热点行做了分组之后,就可以作进一步优化了,这个过程主要有3个关键的优化点:

1、减少行级锁的申请等待

在同一组中,需要更新的都是同一条记录,那么根据SQL的提交顺序,就可以排队了。

然后我们只需要在第一条更新SQL(Leader)执行的时候,尝试去获取目标行的锁,如果获取成功,则开始操作。

然后这一组中后续的更新操作(Follower)也会尝试获取锁,但是会先判断是不是已经被第一条更新操作获取到了,如果是的话,那么就不需要等待,直接获取锁。

这样就可以大大降低行级锁的申请的阻塞等待时长。

2、减少B+树的索引遍历操作

MySQL是以B+索引的方式管理数据的,每次执行查询时,都需要遍历索引才能定位到目标数据行,数据表越大,索引层级越多,遍历时间就越长。

如果针对热点行更新操作做了分组之后,我们只需要在每组的第一条SQL执行过程中,通过遍历索引定位数据行,之后就可以把这些数据行缓存到Row Cache中,并且在Row Cache进行修改。

在同组的后续操作时,也不再需要进行数据索引了,直接从Row Cache获取数据并修改就行了。

这样就大大降低了B+树的索引遍历操作的耗时。

3、减少事务提交次数


如果是没有用这种方式,我们的多条update语句会是多条事务,那么每一个事务都要单独做一次提交。

有了分组、排队、组提交之后,就只需要一组中的并发操作都执行完,然后做一次组提交即可,大大降低提交次数。

扩展知识

statement queue

除了inventory hint,还有一个技术叫做Statement Queue(https://help.aliyun.com/zh/rds/apsaradb-rds-for-mysql/statement-queue ),也是一种减少冲突开销,有效提高实例性能的手段。

通过statement queue,可以配置桶的数量和一个桶中允许的并发数。同时提供了两个接口便于查询Statement Queue状态,方便查看并发情况。详细的就不展开了(大家知道有这么个东西就行),在上面的链接文档中可以查看使用方式。

原文: https://www.yuque.com/hollis666/xkm7k3/gwg64tg0g107wgz3