本文介绍了优化热点更新场景的方法。
背景
数据库中数据更新的顺序为LOCK -> UPDATE -> UNLOCK,当对数据库中的同一条记录有大量修改请求时,会造成大量的锁争抢与锁等待。请求量增加会导致TPS下降,延迟飙升。例如秒杀场景中对于商品库存的扣减。
为解决以上问题PolarDB-X推荐您使用在数据库内核中进行批处理的方案,即对该条记录进行的更新操作使用组提交,其数据更新的顺序变为LOCK-> GROUP UPDATE -> UNLOCK,从而减少锁争抢。结合流水线处理等优化,可以大大提高该场景的TPS,详细信息请参见测试结果。
版本限制
仅支持PolarDB-X企业版MySQL 5.7实例。
注意事项
热点更新功能(INVENTORY HINT)只支持单分片事务场景,不支持跨数据库场景。
前置参数配置
使用SET GLOBAL
设置参数后,对当前会话是不生效的,需要开启新会话才能生效。
开启HOTSPOT相关功能,使用高权限账号执行。
SET GLOBAL HOTSPOT=ON; SET GLOBAL HOTSPOT_LOCK_TYPE=ON
关闭共享ReadView。
若已开启共享ReadView,则应当先将共享ReadView进行关闭,而后使用热点更新的能力。
-- 查看共享ReadView是否打开,false是关闭状态,true是开启状态 SHOW VARIABLES LIKE '%SHARE_READ_VIEW%';
如果共享ReadView是开启状态,则可通过如下SQL全局关闭:
SET GLOBAL SHARE_READ_VIEW = FALSE;
重要全局关闭共享ReadView可能会导致一部分查询变慢。
修改事务类型。
-- 全局级别设置 SET GLOBAL TRANSACTION_POLICY = 'XA'; SET GLOBAL ENABLE_XA_TSO = FALSE; -- 或设置为session级别 SET SESSION TRANSACTION_POLICY = 'XA'; SET SESSION ENABLE_XA_TSO = FALSE;
检查事务类型:
SHOW VARIABLES LIKE 'TRANSACTION_POLICY';
说明如果事务类型不是XA,则需要将事务类型切换为XA,然后才能使用热点更新能力,命令如下(或者在每次使用前设置Session级别)。
该修改会导致事务类型变更为XA,请自行判断该变化是否会产生非预期结果。
使用语法
在业务的UPDATE
语句中添加INVENTORY HINT,并将该语句置为最后的一条。
BEGIN;
UPDATE /*+ commit_on_success rollback_on_fail target_affect_row(number)*/ table_reference
SET assignment_list
[WHERE where_condition];
COMMIT | ROLLBACK; -- 取决于更新成功还是失败,选择其一即可
WHERE条件应为主键或唯一键的等值条件,且不支持带有全局索引的表(可包含本地索引)。
参数说明:
名称 | 是否必选 | 说明 |
commit_on_success | 必选 | 如果该语句成功,则进行提交,连同该语句之前的未提交语句一起提交。 |
rollback_on_fail | 可选 | 如果该语句失败,则进行回滚,连同该语句之前的未提交语句一并回滚。 |
target_affect_row(number) | 可选 | 校验更新的行数是否符合预期,若不符合则更新失败。 |
使用示例
示例表。
CREATE TABLE table_test ( id INT AUTO_INCREMENT PRIMARY KEY, c INT NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE table_test_2 ( id INT AUTO_INCREMENT PRIMARY KEY, c INT NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
添加
commit_on_success
以使用组提交等针对热点更新场景的优化(id
为主键,使用如下语句对id=1
的记录进行更新时,若更新成功则自动提交)。BEGIN; UPDATE /*+ commit_on_success*/ table_test SET c = c - 1 WHERE id = 1; COMMIT;
说明COMMIT | ROLLBACK按需选择其一即可。
使用
rollback_on_fail
,可使得更新失败时自动进行回滚。BEGIN; UPDATE /*+ commit_on_success rollback_on_fail*/ table_test SET c = c - 1 WHERE id = 1; COMMIT;
使用
target_affect_row(number)
,使得该UPDATE语句的预期更新行数为number
,若不等于number
,则更新失败。BEGIN; UPDATE /*+ commit_on_success rollback_on_fail target_affect_row(1)*/ table_test SET c = c - 1 WHERE id = 1; COMMIT;
在带有INVENTORY HINT的UPDATE语句前,可对同一个物理库中的表进行DML操作。
BEGIN; INSERT INTO table_test_2 VALUES (1,1); UPDATE /*+ commit_on_success rollback_on_fail target_affect_row(1)*/ table_test SET c = c - 1 WHERE id = 1; COMMIT;
查看热点组提交是否生效
使用如下SQL查看组提交状态,
Group_update_leader_count
增加则说明触发了热点组提交的优化逻辑。SHOW GLOBAL STATUS LIKE "%Group_update%"; +---------------------------------------+--------+ | Variable_name | Value | +---------------------------------------+--------+ | Group_update_fail_count | 54 | | Group_update_follower_count | 962869 | | Group_update_free_count | 2 | | Group_update_group_same_count | 0 | | Group_update_gu_leak_count | 0 | | Group_update_ignore_count | 0 | | Group_update_insert_dup | 0 | | Group_update_leader_count | 168292 | | Group_update_lock_fail_count | 0 | | Group_update_mgr_recycle_queue_length | 0 | | Group_update_recycle_queue_length | 0 | | Group_update_reuse_count | 23329 | | Group_update_total_count | 2 | +---------------------------------------+--------+
使用
show physical full processlist
查看UPDATE的状态,是否出现HOTSPOT字样。SHOW physical FULL processlist WHERE command != 'Sleep'; +-------+------+-----+---------+-------------+---------+------+-------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Group | Atom | Id | User | db | Command | Time | State | Info | +-------+------+-----+---------+-------------+---------+------+-------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | 0 | 0 | 56 | diamond | test_000001 | Query | 0 | hotspot wait for commit | /*DRDS /127.0.0.1/12e774cab8800000-128/0// */UPDATE /*+COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ `test_hotline_lZTr` AS `test_hotline` SET `b` = (`b` + 1) WHERE (`a` = 1) | | 0 | 0 | 822 | diamond | test_000001 | Query | 0 | query end | /*DRDS /127.0.0.1/12e774c4e9400000-563/0// */UPDATE /*+COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ `test_hotline_lZTr` AS `test_hotline` SET `b` = (`b` + 1) WHERE (`a` = 1) | | 0 | 0 | 831 | diamond | test_000001 | Query | 0 | hotspot wait for commit | /*DRDS /127.0.0.1/12e774c551000000-509/0// */UPDATE /*+COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ `test_hotline_lZTr` AS `test_hotline` SET `b` = (`b` + 1) WHERE (`a` = 1) | | 0 | 0 | 838 | diamond | test_000000 | Query | 0 | starting | show full processlist | +-------+------+-----+---------+-------------+---------+------+-------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
热点更新性能测试
测试准备
测试表
CREATE TABLE sbtest(id INT UNSIGNED NOT NULL PRIMARY KEY, c BIGINT UNSIGNED NOT NULL);
测试SQL
UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c=c+1 WHERE id = 1;
测试工具安装以及使用,请参见Sysbench测试。
PolarDB-X企业版实例计算节点和存储节点的规格均为4C8 GB*2。
测试结果
场景 | 1线程 | 4线程 | 8线程 | 16线程 | 32线程 | 64线程 | 128线程 | 256线程 | 512线程 |
热点更新 | 298 | 986 | 1872 | 3472 | 6315 | 10138 | 13714 | 15803 | 23262 |
普通更新 | 318 | 423 | 409 | 409 | 412 | 428 | 448 | 497 | 615 |
以上测试结果的测试样本均为双1模式。
以上结果的单位为TPS,即每秒处理的交易数(Transaction per second)。
热点更新的TPS与机器规格、并发请求数和更新语句有关,测试结果仅供参考。