本文介紹了在5.2及以下版本適用HINT的方法。

基本語法

 /!TDDL:hint command*/

PolarDB-X 1.0自定義HINT是借助于MySQL 注釋實現的,也就是PolarDB-X 1.0的自定義HINT語句位于/!*/之間,并且必須以TDDL:開頭。其中hint commandPolarDB-X 1.0自定義HINT命令,與具體的操作相關。

例如下面的SQL語句通過PolarDB-X 1.0的自定義HINT展示每個分庫的表名。

/!TDDL:SCAN*/SHOW TABLES;

該SQL語句中/!TDDL:SCAN*/PolarDB-X 1.0自定義HINT部分,以TDDL:開頭,SCANPolarDB-X 1.0自定義HINT命令。

讀寫分離

由于RDS主實例與只讀實例之間數據的同步存在著毫秒級別的延遲,如果在主庫中變更后需要馬上讀取變更的數據,則需要保證將讀取數據的SQL下發到主實例中。針對這種需求,PolarDB-X 1.0提供了讀寫分離自定義HINT,指定將SQL下發到主實例或者只讀實例。

語法

/!TDDL:MASTER|SLAVE*/

在該自定義HINT中可以指定SQL是在主實例上執行還是在只讀實例上執行。對于/!TDDL:SLAVE*/這個自定義HINT,如果一個主RDS實例存在多個只讀實例,那么PolarDB-X 1.0會根據所分配的權重隨機選擇一個只讀實例執行SQL語句。

示例

  • 指定SQL在主實例上執行:
    /!TDDL:MASTER*/SELECT * FROM table_name;

    在SQL語句前添加/!TDDL:MASTER*/這個自定義HINT后,這條SQL將被下發到主實例上執行。

  • 指定SQL在只讀實例上執行:
    /!TDDL:SLAVE*/SELECT * FROM table_name;

    在SQL語句前添加/!TDDL:SLAVE*/這個自定義HINT后,這條SQL將會根據所分配的權重被隨機下發到某個只讀實例上執行。

注意
  • 此讀寫分離自定義HINT僅僅針對非事務中的讀SQL語句生效,如果SQL語句是寫SQL或者SQL語句在事務中,那么還是會下發到RDS的主實例執行。
  • PolarDB-X 1.0針對/!TDDL:SLAVE*/自定義HINT,會從只讀實例中按照權重隨機選取一個下發SQL語句執行。若只讀實例不存在時,不會報錯,而是選取主實例執行。

只讀實例延遲切斷

正常情況下,如果給PolarDB-X 1.0數據庫的RDS主實例配置了只讀實例,并且給主實例和只讀實例都設置了讀流量,那么PolarDB-X 1.0會根據讀寫比例將SQL下發到主實例或者是只讀實例執行。但是如果主實例與只讀實例的異步數據復制存在較大的延遲,將SQL下發到只讀實例執行就會導致出錯或者返回錯誤結果。

只讀實例延時切斷會根據主備復制最大延時時間判斷將所執行的SQL下發到主實例還是只讀實例。

語法

/!TDDL:SQL_DELAY_CUTOFF=time*/

在自定義HINT中指定SQL_DELAY_CUTOFF的值,當備庫的 SQL_DELAY值(MySQL主備復制延遲)達到或超過time的值(單位秒)時,查詢語句會被下發到主實例。

示例

  • 指定主備復制延遲時間為5秒:
    /!TDDL:SQL_DELAY_CUTOFF=5*/SELECT * FROM table_name;

    在SQL語句指定了SQL_DELAY_CUTOFF的值為5,當備庫的SQL_DELAY值達到或超過5秒時,查詢語句會下發到主實例執行。

  • 配合其他自定義HINT使用:
    /!TDDL:SLAVE AND SQL_DELAY_CUTOFF=5*/SELECT * FROM table_name;

    備庫延遲切斷注釋也可以配合其他注釋使用,該SQL查詢請求默認會被下發到只讀實例,但是當出現主備復制延時達到或超過5秒時,會下發到主實例。

自定義SQL超時時間

PolarDB-X 1.0中,PolarDB-X 1.0節點與RDS的默認的SQL執行超時時間是900秒(可以調整),但是對于某些特定的慢SQL,其執行時間可能超過了900秒 。針對這種慢SQL,PolarDB-X 1.0提供了調整超時時間的自定義HINT。通過這個自定義HINT可以任意調整SQL執行時長。

語法

/!TDDL:SOCKET_TIMEOUT=time*/

其中,SOCKET_TIMEOUT的單位是毫秒。通過該 HINT 用戶可以根據業務需要,自由調整SQL語句的超時時間。

示例

設置SQL超時時間為40秒:

/!TDDL:SOCKET_TIMEOUT=40000*/SELECT * FROM t_item;
說明 超時時間設置得越長,占用數據庫資源的時間就會越長。如果同一時間長時間執行的SQL過多,可能消耗大量的數據庫資源,從而導致無法正常使用數據庫服務。所以,對于長時間執行的SQL語句,盡量對SQL語句進行優化。

指定分庫執行SQL

在使用PolarDB-X 1.0的過程中,如果遇到某個PolarDB-X 1.0不支持的SQL語句,可以通過PolarDB-X 1.0提供的自定義HINT,直接將SQL下發到一個或多個分庫上去執行。此外如果需要單獨查詢某個分庫或者已知分庫的某個分表中的數據,也可以使用該自定義HINT,直接將SQL語句下發到分庫中執行。

指定分庫執行SQL自定義HINT有兩種使用方式,即通過分片名指定SQL在分庫上執行或者通過分庫鍵值指定SQL在分庫上執行。其中分片名是PolarDB-X 1.0中分庫的唯一標識,可以通過SHOW NODE控制指令得到。

語法

  • /!TDDL:NODE='node_name'*/

    node_name為分片名,通過這個PolarDB-X 1.0自定義HINT,就可以將SQL下發到node_name對應的分庫中執行。

  • /!TDDL:NODE IN ('node_name'[,'node_name1','node_name2'])*/

    使用in關鍵字指定多個分片名,將SQL下發到多個分庫上執行,括號內分片名之間使用逗號分隔。

    說明 使用該自定義HINT時,PolarDB-X 1.0會將SQL直接下發到分庫上執行,所以在SQL語句中,表名必須是該分庫中已經存在的表名。
  • /!TDDL:table_name.partition_key=value [and table_name1.partition_key=value1]*/

    在這個PolarDB-X 1.0自定義HINT中table_name為邏輯表名,該表是一張拆分表,partition_key是拆分鍵,value為指定的拆分鍵的值。在該自定義注釋中,可以使用and關鍵字指定多個拆分表的拆分鍵。通過這個PolarDB-X 1.0自定義HINT,PolarDB-X 1.0會計算出SQL語句應該在哪些分庫和分表上執行,進而將SQL語句下發到相應的分庫。

示例

對于名為drds_testPolarDB-X 1.0數據庫,SHOW NODE的結果如下:

mysql> SHOW NODE\G
*************************** 1. row ******************
                 ID: 0
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0000_RDS
  MASTER_READ_COUNT: 212
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 2. row ******************
                 ID: 1
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0001_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 3. row ******************
                 ID: 2
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0002_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 4. row ******************
                 ID: 3
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0003_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 5. row ******************
                 ID: 4
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0004_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 6. row ******************
                 ID: 5
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0005_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 7. row ******************
                 ID: 6
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0006_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
*************************** 8. row ******************
                 ID: 7
               NAME: DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0007_RDS
  MASTER_READ_COUNT: 29
   SLAVE_READ_COUNT: 0
MASTER_READ_PERCENT: 100%
 SLAVE_READ_PERCENT: 0%
8 rows in set (0.02 sec)

可以看到每個分庫都有NAME這個屬性,這就是分庫的分片名。每個分片名都唯一對應一個分庫名,比如DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0003_RDS這個分片名對應的分庫名是drds_test_vtla_0003。得到了分片名,就可以使用PolarDB-X 1.0的自定義HINT指定分庫執行SQL語句了。

  • 指定SQL在第0個分庫上執行:
    /!TDDL:NODE='DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0000_RDS'*/SELECT * FROM table_name;
  • 指定SQL在多個分庫上執行:
    /!TDDL:NODE  IN('DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0000_RDS','DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0006_RDS')*/SELECT * FROM table_name;

    這條SQL語句將在DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0000_RDSDRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0006_RDS這兩個分片上執行。

  • 查看某個分庫的執行計劃:
    /!TDDL:NODE='DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0000_RDS'*/EXPLAIN SELECT * FROM table_name;

    這條SQL語句將會展示SELECT語句在分片DRDS_TEST_1473471355140LRPRDRDS_TEST_VTLA_0000_RDS中的執行計劃。

  • 通過鍵值指定SQL在分庫上執行:

    對于UPDATE語句,PolarDB-X 1.0不支持SET子句中的子查詢,由于UPDATE語句在PolarDB-X 1.0中必須指定拆分鍵,所以可以使用PolarDB-X 1.0的自定義HINT將該語句直接下發到分庫上執行。

    比如有兩張邏輯表,分別是t1和t2,它們都是分庫分表,建表語句如下:

      CREATE TABLE `t1` (
        `id` bigint(20) NOT NULL,
        `name` varchar(20) NOT NULL,
        `val` varchar(20) DEFAULT NULL,
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8 dbpartition by hash(`id`) tbpartition by hash(`name`) tbpartitions 3
    
      CREATE TABLE `t2` (
        `id` bigint(20) NOT NULL,
        `name` varchar(20) NOT NULL,
        `val` varchar(20) DEFAULT NULL,
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8 dbpartition by hash(`id`) tbpartition by hash(`name`) tbpartitions 3

    需要執行的語句是:

    UPDATE t1 SET val=(SELECT val FROM t2 WHERE id=1) WHERE id=1;

    這條語句直接在PolarDB-X 1.0上執行會報不被支持的錯誤,但是可以給這條語句加上PolarDB-X 1.0的自定義HINT,再提交到PolarDB-X 1.0執行。具體SQL語句如下:

    /!TDDL:t1.id=1 and t2.id=1*/UPDATE t1 SET val=(SELECT val FROM t2 WHERE id=1) WHERE id=1;

    這條語句會被下發到t1ID為1的分庫上執行。通過explain命令可以看到執行這條SQL語句的執行計劃:

      mysql> explain /!TDDL:t1.id=1 and t2.id=1*/UPDATE t1 SET val=(SELECT val FROM t2 WHERE id=1) WHERE id=1\G
      *************************** 1. row ***************************
      GROUP_NAME: TEST_DRDS_1485327111630IXLWTEST_DRDS_IGHF_0001_RDS
             SQL: UPDATE `t1_2` AS `t1` SET `val` = (SELECT val FROM `t2_2` AS `t2` WHERE `id` = 1) WHERE `id` = 1
          PARAMS: {}
      *************************** 2. row ***************************
      GROUP_NAME: TEST_DRDS_1485327111630IXLWTEST_DRDS_IGHF_0001_RDS
             SQL: UPDATE `t1_1` AS `t1` SET `val` = (SELECT val FROM `t2_1` AS `t2` WHERE `id` = 1) WHERE `id` = 1
          PARAMS: {}
      *************************** 3. row ***************************
      GROUP_NAME: TEST_DRDS_1485327111630IXLWTEST_DRDS_IGHF_0001_RDS
             SQL: UPDATE `t1_0` AS `t1` SET `val` = (SELECT val FROM `t2_0` AS `t2` WHERE `id` = 1) WHERE `id` = 1
          PARAMS: {}
      3 rows in set (0.00 sec)

    explain命令的結果集可以看到,SQL語句被改寫成3條語句下發到分庫上執行。還可以繼續指定分表鍵值,將SQL執行范圍縮小到一張分表:

      mysql> explain /!TDDL:t1.id=1 and t2.id=1 and t1.name='1'*/UPDATE t1 SET val=(SELECT val FROM t2 WHERE id=1) WHERE id=1\G
      *************************** 1. row ***************************
      GROUP_NAME: TEST_DRDS_1485327111630IXLWTEST_DRDS_IGHF_0001_RDS
             SQL: UPDATE `t1_1` AS `t1` SET `val` = (SELECT val FROM `t2_1` AS `t2` WHERE `id` = 1) WHERE `id` = 1
          PARAMS: {}
      1 row in set (0.00 sec)
說明 使用該自定義注釋需要保證兩張表的分庫和分表數量一致,否則PolarDB-X 1.0計算出的兩個鍵值對應的分庫不一致,就會報錯。

掃描全部分庫分表

除了可以將SQL單獨下發到一個或多個分庫執行,PolarDB-X 1.0還提供了掃描全部分庫與分表的自定義HINT。通過這個自定義HINT,您可以一次將SQL下發到每一個分庫執行。比如通過這個自定義HINT,可以查看某個分庫上的所有分表。還可以通過這個自定義HINT,查看某個邏輯表的每個分庫的分表數據量。

掃描全部分片的PolarDB-X 1.0自定義HINT有兩種使用方式,第一種方式是將SQL語句下發到每個分庫執行,第二種方式是將SQL語句下發到每個分庫上對某個邏輯表進行操作。

  • 將SQL下發到全部分庫上執行:
    /!TDDL:SCAN*/
  • 對某個邏輯表進行操作:
    /!TDDL:SCAN='table_name'*/

    其中table_namePolarDB-X 1.0數據庫的某個邏輯表名。該自定義HINT是為分庫分表提供的,請盡量確保table_name為分庫分表。