秒杀场景是实际业务中常见的一种业务场景,对于数据库而言,这个场景的特点就是大量并发针对同一行数据进行更新。热点数据更新的核心问题在于行锁的冲突,为了解决这个问题,云数据库 MySQL 版采用了相同热点行的更新批量执行的方式。对于带有热点更新标识的 SQL 进入数据库后,数据库会对更新相同数据的 SQL 进行合并处理,从而提升热点高并发更新的性能。
只支持单表更新操作。
不支持表结构上带有触发器。
不支持分区表。
不支持带有 OFFSET/LIMIT/ORDER BY
的 UPDATE
语句。
查询必须带有WHERE
条件,且为等值索引条件。
在事务块中使用秒杀,秒杀语句必须是事务块中最后一条 SQL 。
UPDATE 列中不能包含主键和当前使用的索引列,除非 where
条件完全命中索引。
云数据库 MySQL 版的 8.0.26 版本序列的实例暂不支持该功能。
用户使用 SQL HINT 的方式指定秒杀更新操作,HINT 关键字为 LOGIC_UPDATE
:
update /*+ LOGIC_UPDATE */ t_stock set num = num - 1 where id = X and num > 0;
上述的批量执行设计只能支持非事务块的热点更新操作。如果需要在事务中使用秒杀,需要增加 HINT 关键字 AUTO_LOGIC_COMMIT_ROLLBACK
,而且需要把热点更新的 SQL 作为事务的最后一条 SQL。带了这个 HINT 以后,数据库会根据 UPDATE 是否成功来主动提交或者回滚事务。
begin; insert into t_order(userid, stockid, num) values(X, X, X); update /*+ LOGIC_UPDATE AUTO_LOGIC_COMMIT_ROLLBACK */ t_stock set num = num - 1 where id = X and num > 0; commit;
测试环境为 MySQL 5.7 版本,48C502GB 实例。
CREATE TABLE sbtest(id INT UNSIGNED NOT NULL AUTO_INCREMENT, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id)); UPDATE /*+ LOGIC_UPDATE */ sbtest SET c=c-1 WHERE id = 1;
测试结果如下。可以看出在没有使用热点更新优化时,QPS 会随着并发加大而急剧下降;而打开优化后,QPS 可以保持稳定。
16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | ||
---|---|---|---|---|---|---|---|---|---|---|
关闭秒杀 | tps | 8309.06 | 7708.74 | 6408.44 | 4006.42 | 1786.19 | 606.19 | 247.76 | 243.04 | 245.75 |
pct99/ms | 2.78ms | 5.03ms | 11.61ms | 36.68ms | 147.62ms | 863.30ms | 6464.70ms | 10195.54ms | 18021.98ms | |
打开秒杀 | tps | 13002.15 | 25056.90 | 39866.60 | 64778.30 | 94073.50 | 85589.48 | 79741.77 | 76620.24 | 75845.25 |
pct99/ms | 1.30ms | 1.37ms | 2.67ms | 3.03ms | 4.81ms | 11.03ms | 23.26ms | 42.14ms | 74.00ms |