如果您的MySQL的服務層和引擎層在語句並發執行過程中衝突較多,可以使用AliSQL提供的Statement Queue功能,該功能針對語句的排隊機制,將語句進行分桶排隊,盡量把可能具有相同衝突的語句(例如操作相同行)放在一個桶內排隊,減少衝突的開銷,有效提高執行個體效能。
背景資訊
MySQL的服務層和引擎層在語句並發執行過程中,有很多串列的點容易導致衝突。例如在DML語句中,事務鎖衝突比較常見,InnoDB中事務鎖的最細粒度是行級鎖,如果語句針對相同行進行並行作業,會導致衝突比較嚴重,系統輸送量會隨著並發的增加而遞減。AliSQL提供Statement Queue功能,能夠減少衝突開銷,有效提高執行個體效能。
前提條件
執行個體版本滿足以下要求:
RDS MySQL 8.0基礎系列或高可用系列(核心小版本20191115及以上)
RDS MySQL 5.7基礎系列或高可用系列(核心小版本20200630及以上)
變數
AliSQL提供了兩個變數來定義語句隊列的桶數量和大小:
ccl_queue_bucket_count:表示桶的數量。
取值範圍:1~64
預設值:4
ccl_queue_bucket_size:表示一個桶允許的並發數。
取值範圍:1~4096
預設值:64
您可以在RDS控制台修改變數值,詳情請參見設定執行個體參數。
文法
AliSQL支援兩種hint文法:
ccl_queue_value
根據值進行hash分桶。
文法:
/*+ ccl_queue_value([int | string]) */
樣本:
update /*+ ccl_queue_value(1) */ t set c=c+1 where id = 1; update /*+ ccl_queue_value('xyz') */ t set c=c+1 where name = 'xyz';
ccl_queue_field
根據where條件中的欄位值進行hash分桶。
文法:
/*+ ccl_queue_field(string) */
樣本:
update /*+ ccl_queue_field(id) */ t set c=c+1 where id = 1 and name = 'xyz';
說明上述兩種hint都是位置敏感的,需要放在update之後。
ccl_queue_field每次只能指定一個field,
/*+ ccl_queue_field(id name) */
方式為語法錯誤,CCL queue不會生效。/*+ ccl_queue_field(id) ccl_queue_field(name) */
為重複hint,以第一個hint指定的field為準。ccl_queue_field中指定的field必須在where條件中出現。
在ccl_queue_field文法中,where條件只支援原始欄位(沒有在欄位上使用任何函數、計算等)的二元運算,並且二元運算的右側值必須是數字或者字串。
介面
AliSQL提供兩個介面便於您查詢Statement Queue狀態:
dbms_ccl.show_ccl_queue()
查詢當前Statement Queue狀態。
mysql> call dbms_ccl.show_ccl_queue(); +------+-------+-------------------+---------+---------+----------+ | ID | TYPE | CONCURRENCY_COUNT | MATCHED | RUNNING | WAITTING | +------+-------+-------------------+---------+---------+----------+ | 1 | QUEUE | 64 | 1 | 0 | 0 | | 2 | QUEUE | 64 | 40744 | 65 | 6 | | 3 | QUEUE | 64 | 0 | 0 | 0 | | 4 | QUEUE | 64 | 0 | 0 | 0 | +------+-------+-------------------+---------+---------+----------+ 4 rows in set (0.01 sec)
參數說明如下。
參數
說明
CONCURRENCY_COUNT
最大並發數。
MATCHED
命中規則的總數。
RUNNING
當前並發的數量。
WAITTING
當前等待的數量。
dbms_ccl.flush_ccl_queue()
清理記憶體中的資料。
mysql> call dbms_ccl.flush_ccl_queue(); Query OK, 0 rows affected (0.00 sec) mysql> call dbms_ccl.show_ccl_queue(); +------+-------+-------------------+---------+---------+----------+ | ID | TYPE | CONCURRENCY_COUNT | MATCHED | RUNNING | WAITTING | +------+-------+-------------------+---------+---------+----------+ | 1 | QUEUE | 64 | 0 | 0 | 0 | | 2 | QUEUE | 64 | 0 | 0 | 0 | | 3 | QUEUE | 64 | 0 | 0 | 0 | | 4 | QUEUE | 64 | 0 | 0 | 0 | +------+-------+-------------------+---------+---------+----------+ 4 rows in set (0.00 sec)
實踐
功能測試
為避免冗長的應用業務代碼的修改,Statement Queue可以配合Statement Outline進行線上業務修改,方便快捷。下文使用SysBench的update_non_index為例進行示範。
測試環境
測試表結構
CREATE TABLE `sbtest1` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `k` int(10) unsigned NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `k_1` (`k`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 MAX_ROWS=1000000;
測試語句
UPDATE sbtest1 SET c='xyz' WHERE id=0;
測試指令碼
./sysbench \ --mysql-host= {$ip} \ --mysql-port= {$port} \ --mysql-db=test \ --test=./sysbench/share/sysbench/update_non_index.lua \ --oltp-tables-count=1 \ --oltp_table_size=1 \ --num-threads=128 \ --mysql-user=u0
測試過程
線上增加Statement Outline。
mysql> CALL DBMS_OUTLN.add_optimizer_outline('test', '', 1, ' /*+ ccl_queue_field(id) */ ', "UPDATE sbtest1 SET c='xyz' WHERE id=0"); Query OK, 0 rows affected (0.01 sec)
查看Statement Outline。
mysql> call dbms_outln.show_outline(); +------+--------+------------------------------------------------------------------+-----------+-------+------+--------------------------------+------+----------+---------------------------------------------+ | ID | SCHEMA | DIGEST | TYPE | SCOPE | POS | HINT | HIT | OVERFLOW | DIGEST_TEXT | +------+--------+------------------------------------------------------------------+-----------+-------+------+--------------------------------+------+----------+---------------------------------------------+ | 1 | test | 7b945614749e541e0600753367884acff5df7e7ee2f5fb0af5ea58897910f023 | OPTIMIZER | | 1 | /*+ ccl_queue_field(id) */ | 0 | 0 | UPDATE `sbtest1` SET `c` = ? WHERE `id` = ? | +------+--------+------------------------------------------------------------------+-----------+-------+------+--------------------------------+------+----------+---------------------------------------------+ 1 row in set (0.00 sec)
驗證Statement Outline生效。
mysql> explain UPDATE sbtest1 SET c='xyz' WHERE id=0; +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ | 1 | UPDATE | sbtest1 | NULL | range | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using where | +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> show warnings; +-------+------+-----------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+-----------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | update /*+ ccl_queue_field(id) */ `test`.`sbtest1` set `test`.`sbtest1`.`c` = 'xyz' where (`test`.`sbtest1`.`id` = 0) | +-------+------+-----------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
查詢Statement Queue狀態。
mysql> call dbms_ccl.show_ccl_queue(); +------+-------+-------------------+---------+---------+----------+ | ID | TYPE | CONCURRENCY_COUNT | MATCHED | RUNNING | WAITTING | +------+-------+-------------------+---------+---------+----------+ | 1 | QUEUE | 64 | 0 | 0 | 0 | | 2 | QUEUE | 64 | 0 | 0 | 0 | | 3 | QUEUE | 64 | 0 | 0 | 0 | | 4 | QUEUE | 64 | 0 | 0 | 0 | +------+-------+-------------------+---------+---------+----------+ 4 rows in set (0.00 sec)
開啟測試。
sysbench \ --mysql-host= {$ip} \ --mysql-port= {$port} \ --mysql-db=test \ --test=./sysbench/share/sysbench/update_non_index.lua \ --oltp-tables-count=1 \ --oltp_table_size=1 \ --num-threads=128 \ --mysql-user=u0
驗證測試效果。
mysql> call dbms_ccl.show_ccl_queue(); +------+-------+-------------------+---------+---------+----------+ | ID | TYPE | CONCURRENCY_COUNT | MATCHED | RUNNING | WAITTING | +------+-------+-------------------+---------+---------+----------+ | 1 | QUEUE | 64 | 10996 | 63 | 4 | | 2 | QUEUE | 64 | 0 | 0 | 0 | | 3 | QUEUE | 64 | 0 | 0 | 0 | | 4 | QUEUE | 64 | 0 | 0 | 0 | +------+-------+-------------------+---------+---------+----------+ 4 rows in set (0.03 sec) mysql> call dbms_outln.show_outline(); +------+--------+-----------+-----------+-------+------+--------------------------------+--------+----------+---------------------------------------------+ | ID | SCHEMA | DIGEST | TYPE | SCOPE | POS | HINT | HIT | OVERFLOW | DIGEST_TEXT | +------+--------+-----------+-----------+-------+------+--------------------------------+--------+----------+---------------------------------------------+ | 1 | test | xxxxxxxxx | OPTIMIZER | | 1 | /*+ ccl_queue_field(id) */ | 115795 | 0 | UPDATE `sbtest1` SET `c` = ? WHERE `id` = ? | +------+--------+-----------+-----------+-------+------+--------------------------------+--------+----------+---------------------------------------------+ 1 row in set (0.00 sec)
說明查詢結果顯示Statement Outline命中了115,795次規則,Statement Queue狀態顯示命中了10,996次排隊,當前運行並發63個,排隊等待4個。
效能測試
測試環境
應用伺服器:阿里雲ECS執行個體
RDS執行個體規格: 8 核16 GB 記憶體、ESSD雲端硬碟
執行個體類型:高可用系列(資料複製方式為非同步複製)
測試案例
對id=1的記錄進行並發更新,使用的Sysbench的測試案例如下:
pathtest = string.match(test, "(.*/)") if pathtest then dofile(pathtest .. "oltp_common.lua") else require("oltp_common") end function thread_init() drv = sysbench.sql.driver() con = drv:connect() end function event() local val_name val_name = "'sdnjkmoklvnseajinvijsfdnvkjsnfjvn".. sb_rand_uniform(1, 4096) .. "'" query = "UPDATE sbtest1 SET c=" .. val_name .. " WHERE id=1" rs = db_query(query) end
測試結果
開啟Statement Queue功能後,在高並發情境下QPS提升顯著,並發數越高,提升越明顯。
說明未開啟Statement Queue功能,在4096線程壓測下執行個體發生主備切換,因此QPS為0。