本文介紹了優化熱點更新場景的方法。
背景介紹
數據庫中數據更新的順序為lock -> update -> unlock,當對數據庫中的同一條記錄有大量修改請求時,會造成大量的鎖爭搶與鎖等待。請求量增加會導致TPS下降,延遲飆升。例如秒殺場景中對于商品庫存的扣減。
您可以選擇在數據庫內核中進行批處理,即對該條記錄進行的更新操作使用組提交,數據更新的順序變為lock-> group update -> unlock,從而減少鎖爭搶。結合流水線處理等優化,可以大大提高該場景的TPS,詳情可見測試結果。
使用方法
開啟hotspot相關功能,使用高權限賬號打開以下兩個開關項。
set global hotspot=on; set global hotspot_lock_type=on
設置后可使用
show global variables like '%hotspot%'
語句查看是否設置成功,若未設置成功,可先檢查是否使用的是高權限賬號。檢查共享ReadView狀態。
# 查看共享ReadView是否打開 show variables like '%share_read_view%';
如果共享ReadView是打開的狀態,則可通過如下指令全局關閉:
set global share_read_view = false;
重要全局關閉共享ReadView可能會導致一部分查詢變慢,請謹慎考慮。
檢查事務類型。
show variables like 'transaction_policy';
如果事務類型不是XA,則需要將事務類型切換為XA,然后才能使用熱點更新能力,命令如下(或者在每次使用前設置session級別)。
set global transaction_policy = 'xa';
執行以下命令,可查看設置是否生效。
#首先需要執行begin BEGIN; #其次查看是否已成功修改 show variables like 'transaction_policy'; rollback;
重要該修改會導致隔離級別由RR改為RC,請自行判斷該變化是否會產生非預期結果。
在業務的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; #取決于更新成功還是失敗
注意事項
僅適用于PolarDB-X 2.0。
where條件應為主鍵或唯一鍵的等值條件,且不支持帶有全局索引的表(可包含本地索引)。
若已開啟共享read_view,則應當先將共享read_view進行關閉,而后使用熱點更新的能力。
Inventory hint的使用場景為單分片事務,無法在跨庫場景中使用。
用戶需使用COMMIT | ROLLBACK以提交或回滾事務。
Inventory Hint各參數含義
commit_on_success(必選)
如果該語句成功,則進行提交,連同該語句之前的未提交語句一起提交。
rollback_on_fail
如果該語句失敗,則進行回滾,連同該語句之前的未提交語句一并回滾。
target_affect_row(number)
校驗更新的行數是否符合預期,若不符合則更新失敗。
示例
添加commit_on_success以使用組提交等針對熱點更新場景的優化(id為主鍵,使用如下語句對id=1的記錄進行更新時,若更新成功則自動提交)
BEGIN; UPDATE /*+ commit_on_success*/ table_test SET c = c - 1 WHERE id = 1; COMMIT | ROLLBACK;
使用rollback_on_fail,可使得更新失敗時自動進行回滾
BEGIN; UPDATE /*+ commit_on_success rollback_on_fail*/ table_test SET c = c - 1 WHERE id = 1; COMMIT | ROLLBACK;
使用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 | ROLLBACK;
在帶有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 | ROLLBACK;
查看inventory hint是否生效
使用命令
show global status like "%Group_update%"
查看組提交狀態,Group_update_leader_count一直增加則說明觸發了熱點組提交的優化邏輯。mysql> 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 | +---------------------------------------+--------+ 13 rows in set (0.01 sec)
使用
show physical full processlist
查看update的狀態,是否出現hotspot字樣。mysql> 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 | +-------+------+-----+---------+-------------+---------+------+-------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 4 rows in set (0.33 sec)
熱點更新測試
測試表定義
CREATE TABLE sbtest(id INT UNSIGNED NOT NULL PRIMARY KEY, c BIGINT UNSIGNED NOT NULL);
測試語句
UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c=c+1 WHERE id = 1;
測試工具
sysbench
機器規格
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 |
以上結果的單位為TPS,即每秒處理的交易數(Transaction per second);
熱點更新的TPS與機器規格、并發請求數和更新語句有關,測試結果僅供參考。