全部產品
Search
文件中心

ApsaraDB RDS:Statement Queue

更新時間:Jun 08, 2024

如果您的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
  • 測試過程

    1. 線上增加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)
    2. 查看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)
    3. 驗證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)
    4. 查詢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)
    5. 開啟測試。

      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
    6. 驗證測試效果。

      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提升顯著,並發數越高,提升越明顯。

    image.png

    說明

    未開啟Statement Queue功能,在4096線程壓測下執行個體發生主備切換,因此QPS為0。