PolarDB-X 針對跑批場景的思考和實踐

背景

金融行業和運營商系統,業務除了在線聯機查詢外,同時有離線跑批處理,跑批場景比較注重吞吐量,同時基於數據庫場景有一定的使用慣性,比如直連MySQL分庫分表的存儲節點做本地化跑批、以及基於Oracle/DB2等數據庫做ETL的數據清洗跑批等。

分佈式數據庫使用多節點的設計,天然非常適合進行高併發的簡單查詢,但針對大規模的數據導入導出和分頁查詢等跑批場景,需要有更多面向併發導數的思考。

首先簡單總結下,目前常見的業務跑批場景:

業務跑批處理

比如阿里的餘額寶(億級別的用戶需要在每天進行利息結算)、淘寶收貨自動確認(每天億級別的用戶訂單需要定時確認),大致流程:

  1. 批量拉取用戶列表,通過跑批框架按用戶維度進行路由,分發需要處理的用戶到應用的分佈式多節點上
  2. 每個應用的跑批進程,會接收一定併發的用戶處理,針對每個用戶進行單獨的select和業務計算,最後將結果寫入數據庫
  3. 跑批框架,針對分佈式多節點各自的處理速度,進行動態調度和狀態收集,確保所有用戶結算任務均處理完畢
阿里雲schedulerx文檔: https://developer.aliyun.com/article/704121

數倉跑批處理

比如XX稅務,在結算期開始會進行納稅信息的批量統計,大致流程:

  1. 大規模數據(億級別),從在線數據庫拖取全量數據到離線數倉(比如MaxCompute)
  2. 在離線數倉內部進行復雜的多階段ETL計算
  3. 將最後的結果數據,批量回寫到數據庫,提供在線查詢服務

特點:數據規模大,跑批過程中不需要關心事務 不足: 同時維護在線數據庫和離線數倉兩套系統,兩套系統的數據同步延遲比較大,滿足不了實時跑批的業務場景

數據庫ETL批處理

數據庫模式

比如電商訂單業務,超時到期自動確認收貨的批量更新,大致流程:

  1. 小規模數據(百萬級別),直接通過在線數據庫進行批量SELECT查詢符合條件的數據
  2. 通過數據庫的DML語句進行批量插入或者更新,業務計算過程中數據暫存會依賴臨時表,同時會出現一定的大事務需求

特點:可支持事務場景,可滿足實時跑批的業務場景;一套系統業務維護成本低,業務接入成本低 不足:支持的數據規模有限

中間件模式

比如很多廣告計算系統,一般是是夜間做跑批校驗,大致流程:

  1. 數據規模比較大(一般規模處於數倉和單機數據庫容量之間),業務表已經按照業務模型做了拆分,業務上通過中間件方式直連各個拆分後的物理分表進行批量SELECT查詢符合條件的數據;
  2. 通過中間件方式直接物理分表進行批量插入或者更新,計算過程中仍然可能通過數據暫存會依賴臨時表(在拆分後的數據庫中創建的);
  3. 在跑批過程中有一些大事務需求,或則事務內有跨拆分表的分佈式事務,這種模式很難滿足,往往會引入柔性事務,確保最終的一致性。

特點:可支持較大規模,也可滿足最終一致性的跑批業務場景;業務接入成本低;相對於純數據庫方案,通過加存儲節點,跑批性能可以做到完全線性擴展; 不足:有一定的開發成本,業務上需要感知DN的切換,自身要去維護DDL元數據一致性以及在擴縮容場景下的數據路由的一致性,只支持柔性事務

PolarDB-X 跑批實踐

方案一:應用正常連接PolarDB-X CN節點

PolarDB-X的整個架構,分爲CN/DN/GMS/CDC 4個核心組件,CN承擔了業務流量的主要入口、以及承擔串聯分佈式事務、DDL元數據、SQL執行路由等.

PolarDB-X作爲分佈式數據庫,原生支持了MPP,支持跑批的大查詢。同時業務正常訪問CN鏈路,相比於直連DN的中間件模型跑批場景,可以有效規避以下架構問題:

  1. 屏蔽DN的多副本信息 (HA切換、讀寫分離)
  2. DDL元數據一致性 (業務動態加減列、ETL流程裏建表)
  3. 數據路由的一致性 (在線擴縮容、熱點遷移和均衡)
  4. 全局CDC的事務一致性 (事務順序性、SQL閃回)

相當於單機數據庫,PolarDB-X分佈式數據庫,可以支持更大規模的跑批場景。但分佈式數據庫由於其數據分片以及架構特點,在分頁場景上往往很難做到跑批性能隨着資源增加做到線性擴展。可參考《分佈式數據庫的分頁場景的最佳實踐》

建議:用戶對於小規模數據(百萬級別),可以正常訪問PolarDB-X當做單機庫來做跑批處理。

方案二:分區hint直連訪問,線性提升跑批處理性能

用戶通過直連PolarDB-X CN進行跑批數據的讀和寫,需要最大化的利用分佈式多節點資源的並行性,達到線性擴展的性能。 PolarDB-X提供了分區Hint直連訪問的方案,例如:

select * from orders partition(p1) order by gmt_create,id desc

引入partition關鍵字,允許用戶直接指定分區id進行直連訪問,規避分佈式下跨多數據分片場景的分頁排序問題。

金融行業用戶的跑批場景適配,在傳統MySQL分庫分表上的典型使用,比如:MySQL分庫分表後分了128個分片,應用會啓動128個併發,每個併發創建一個數據庫連接,一個鏈接固定訪問一個數據分片。 這種分庫分表的併發拆分方案,很好的滿足了吞吐量的併發需求,PolarDB-X 在 5.4.17 版本提供了兼容傳統MySQL分庫分表遷移到PolarDB-X的跑批使用,在分區hint直連訪問的基礎上,允許在鏈接session級別綁定訪問分區(比如:1個鏈接要訪問20張邏輯表,通過在session級別設置分區hint P1,可以讓當前鏈接默認訪問所有分佈式表,都是直接訪問分區P1,減少用戶的改造成本)。

分佈式線性擴展性的設計原則,需要邏輯和物理查詢儘可能做到1:1,這樣隨着DN節點的在線擴容,整體容量和吞吐能力可以達到線性增加。

  • 相較於直接CN的分佈式數據庫跑批方式
  • 業務上更加靈活的配置並行策略:基於DN節點的物理拓撲,按DN維度均勻做並行處理,性能更好。
  • 在完全兼容傳統的中間件跑批模式,可以享受到分佈式數據庫下已有的能力
  • 屏蔽DN的多副本信息 (HA切換、讀寫分離)
  • DDL元數據一致性 (業務動態加減列、ETL流程裏建表)
  • 數據路由的一致性 (在線擴縮容、熱點遷移和均衡)
  • 全局CDC的事務一致性 (事務順序性、SQL閃回)
  • 支持分佈式事務

建議:可支持較大規模,也可滿足最終一致性的跑批業務場景。跑批過程中的DDL需要特殊處理(忽略分區hint)、以及數據分片要遵循相同的分區規則。

Session級別支持分區Hint 設計方案

概述

在 ServerConnection 中配置一個屬性 partition_hint, 當屬性不爲空時,將 DML 請求進行特殊的下推處理.複用 HintPlanner 中的邏輯,並在強制下推時根據 partition_hint 的內容替換請求中的表名.

思路

partition hint 的作用域

通過設置非空串啓用,設置空串使其無效

partition hint 一旦生效後,會影響 session 上面執行的所有 dml,因此需要將該配置嚴格限制在 ServerConnection 上面.避免作爲一般的 CN 配置產生全局影響.

控制鏈路爲 SetHandler -> ServerConnection 單向變更, 不受 meta.inst_config 的影響. 作用鏈路爲 ServerConnection -> TConnection -> ExecutionContext -> HintPlannner ServerConnection 每次請求都會根據自身 partitionHint 屬性刷新 ExecutionContext 中的 partition hint 配置

SQL 類型判斷與 BuildInDB 識別

以下 SQL 類型不受 Session hint 影響

  • Show 指令與 DAL: grant, kill, show tables, show topology 等指令可以正常工作,不受 hint 影響
  • DDL 指令可以正常工作在邏輯層面,不受 hint 影響
  • Information_schema/cdc/polardbx 等 buildin DB 不受 hint 影響
  • 部分 Explain 指令不受影響,注意以下 explain 會忽略 partition hint
  • Explain Optimizer :需要進入 cbo
  • Explain Advisor :需要進入 cbo
  • Explain Statistics :需要進入優化器
  • Explain JsonPlan :需要進入優化器
  • Explain Execute :執行路徑與 direct mode 衝突
  • Explain Sharding :需要進入優化器

物理表名替換邏輯

sharding 庫針對分庫不分表的 direct mode, 會自動進行物理表名替換; 針對分庫又分表的情況會忽略,默認由請求發起方自行感知並處理物理表名 session hint 需要增加以下替換邏輯:

  • drds 模式下,set partition_hint='group_name_0xx',其中0xx代表分庫分表下的分表序號. 與 node hint 不同點在於, partition hint 本質上配置的是分表序號,請求發起端只需要感知需要訪問的表有幾個 group, 每個group 有幾個分表
  • auto 模式下,set partition_hint='分區名' . auto 模式目前一個 partition 中只有一個分表, 因此配置了 auto 表的 partition name, 相當於定位到了具體的分表

約束與行爲

  • partition hint 與原有的下推 hint 在設計上完全不同,因此需要避免下推 hint 對 partition hint 的影響. 如果發現下推 hint 與 partition hint 同時起作用則報錯處理.
  • drds 模式下訪問的各個拆分表, 分表數量應該完全一致.
  • partition hint sql 訪問 braodcast 表時, 直接替換物理表名,不要求 hint 的 partition name 與 broadcast 表一致
  • auto 模式下訪問的所有非 broadcast 邏輯表, 必須處於相同的 tablegroup 下面

FLASH BACK

在跑批場景, 業務方有可能需要各個分庫拖數據時確保數據的一致性.如有這種需求, 理論上可以通過 flash back 的時間戳來確保各分庫處理數據時全局的一致性.

select * from T AS of TIMESTAMP '2022-03-11 09:00:00'

異常路徑及錯誤碼

  • sharding 表, 在 partition hint 設置了 groupname, 但沒有配置分表 index 的情況下, 如果涉及的拆分表有多個分表, 此時會因爲無法找到對應分表而報錯:
Unsupported to use direct HINT for part table that has multi physical tables in one partition
  • sharding 表, 在 partition hint 設置的分表 index 超出最大分表數量的情況下會報錯:
table index overflow in target group from direct HINT for sharding table
  • sharding 表, 根據 partition hint 設置的 groupname 找不到對應的 group 時報錯:
cannot find target group from direct HINT for sharding table
  • sharding 表, 涉及到的各表在 partition hint 配置的 group 中, 分表數不同則會報錯:
different sharding table num in partition hint mode
  • auto 表, 請求涉及到的各表歸屬的 table group 不同則報錯:
different table group in partition hint mode
  • partition hint 模式下不支持跨 schema 訪問,如果跨 schema 訪問則會報錯:
partition hint visit table crossing schema
  • partition hint 不支持與其它下推 hint (/TDDL:node=0/ 等)同時使用, 如果同時使用則報錯:
Unsupported to use direct HINT and session partition hint in the same time
  • partition hint 模式下如果想執行 dml 變更數據內容, 如 insert/update/delete 等操作, 需要確保 ENABLE_FORBID_PUSH_DML_WITH_HINT 參數設置爲 false, 該參數默認爲 true. 否則會報錯:
Unsupported to push physical dml by hint
  • partition hint 模式下如果對 auto 表配置了不存在的 partition name, 則會報錯:
ERR-CODE: [TDDL-4998][ERR_NOT_SUPPORT] Unsupported to use direct HINT for part table that has multi physical tables in one partition not support yet!

特殊場景及注意事項

partition hint 對 broadcast 表不起作用, partition hint 模式下訪問 broadcast 表時,默認使用 default group.如果請求中有其它的非 broadcast 表, 則以其它表的 group 爲準

partition hint 模式下不支持一條請求中僅訪問物理表, 但是可以支持邏輯表與物理表在一條請求中的訪問.注意物理表必須存在於邏輯表 partition hint 設置的 group 中

Session Hint 使用方式

使用語法

提供session級別的變量,控制路由。Polardbx 可以通過設置 partition hint, 直接訪問某 DN 分片. 設置的方式爲 set 指令,語法如下所示 :::info SET PARTITION_NAME=[PARTITION_NAME|GROUP_NAME|GROUP_NAME:TABLE_INDEX] :::

  • PARTITION_NAME 指 auto db 中表的 partition_name 屬性
  • GROUP_NAME 指 drds db 中表所在的 GROUP_NAME
  • drds db 中某 GROUP 存在多個分表時, TABLE_INDEX 指該分表在該 GROUP 中的 index,從 0 開始

示例:

set session PARTITION_HINT= [partion_info] #partion_info代表路由信息
在auto模式下, 可配置partition name
partion_info:
   [p1|p2|p3...|pn]
在drds模式下,分庫不分表下,可配置group name
partion_info:
   [group1|group2|group3...|group4]
在drds模式下,分庫分表下,可配置group name:index,其index表示分表的Table ID
partion_info:
   [[group1:01]|[group1:02]|[group2:03]...|[groupn:04]]

如何獲取PolarDB-X物理拓撲,既partion_info

PolarDB-X提供兩種分區模式,drds模式和auto模式。

相關指令:

  1. show topology from table ,返回某張邏輯表下的物理拓撲,GROUP_NAME代表DN裏的物理庫,TABLE_NAME代表DN裏某個庫下的物理表名
  2. show ds,返回PolarDB-X實例下DN和GROUP物理庫的對應關係

DRDS模式:

Auto模式:

如何配置連接池使用直連DN的session hint

步驟一:選擇Druid連接池 參考文檔:https://help.aliyun.com/document_detail/311220.html

httpsetail/311220.html

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <!-- 基本屬性 URL、user、password -->
    <property name="url" value="jdbc:mysql://ip:port/db?autoReconnect=true&rewriteBatchedStatements=true&socketTimeout=30000&connectTimeout=3000" />
    <property name="username" value="" />
    <property name="password" value="" />
    .....
    <!-- 設置物理連接創建後立即執行的初始SQL,多條語句可用分號分隔 -->
    <property name="connectionInitSqls" value="set partition_hint=XXXX_000001_GROUP;"/>
</bean>

參數說明:connectionInitSqls,是Druid連接池提供的一個通用能力,可以設置物理連接創建後立即執行的初始SQL,多條語句可用分號分隔。

例子:connectionInitSqls="set partition_hint=XXXX_000001_GROUP;" 設置鏈接直連DN的session hint,參數解讀:

  1. drds模式,格式爲:partition_hint=:,對應GROUP_NAME爲拓撲元數據DN裏的物理庫。目前1個連接僅能設置1個GROUP,如果有64個分庫需要配置64個Druid連接池。TABLE_INDEX 是分表的在該 GROUP 中的下標.每個 GROUP 從 0 開始. 如果 GROUP 中只有一張分表則可以不配置
  2. auto模式,格式爲:partition_hint=,對應的PARTITION_NAME爲拓撲元數據裏的partition_name信息。目前1個連接僅能設置1個Partition,如果有64個分片需要配置64個Druid連接池。

通過 show full connection 觀察所有連接的 partition hint 啓用情況

show full connection 會顯示所有 connection 的 PARTITION_HINT 的設置情況。

mysql> show connection\G
*************************** 1. row ***************************
             ID: 2
           HOST: 127.0.0.1
           PORT: 52027
     LOCAL_PORT: 8527
         SCHEMA: drds_polarx1_part_qatest_app
        CHARSET: utf8mb4
         NET_IN: 11238
        NET_OUT: 728997
  ALIVE_TIME(s): 152
LAST_ACTIVE(ms): 0
       CHANNELS: 0
            TRX: 0
 NEED_RECONNECT: 0
1 row in set (0.01 sec)
mysql> show full connection\G
*************************** 1. row ***************************
             ID: 2
           HOST: 127.0.0.1
           PORT: 52027
     LOCAL_PORT: 8527
         SCHEMA: drds_polarx1_part_qatest_app
 PARTITION_HINT: P3
        CHARSET: utf8mb4
         NET_IN: 11238
        NET_OUT: 728997
  ALIVE_TIME(s): 152
LAST_ACTIVE(ms): 0
       CHANNELS: 0
            TRX: 0
 NEED_RECONNECT: 0
1 row in set (0.01 sec)

使用例子

訪問 auto模式 庫下分區

mysql> create database if not exists partition_hint_test mode=auto;
mysql> use partition_hint_test
mysql> CREATE TABLE if not exists `partition_hint_test_tbl1` (
        `a` datetime NOT NULL,
        primary key(a)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4  
PARTITION BY RANGE(YEAR(a))
(PARTITION p0 VALUES LESS THAN (1990) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (2010) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2020) ENGINE = InnoDB);
mysql> insert into partition_hint_test_tbl1(a) values('1989-10-01'), ('1999-11-01'),('2001-08-19');
# 未設置 partition_hint 時, 正常訪問邏輯表
mysql> select * from partition_hint_test_tbl1;
+---------------------+
| a                   |
+---------------------+
| 1999-11-01 00:00:00 |
| 2001-08-19 00:00:00 |
| 1989-10-01 00:00:00 |
+---------------------+
3 rows in set (0.04 sec)
# 設置了 partition_hint 之後, 只會訪問配置分片的物理表
mysql> set partition_hint=p0;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from partition_hint_test_tbl1;
+---------------------+
| a                   |
+---------------------+
| 1989-10-01 00:00:00 |
+---------------------+
1 row in set (0.01 sec)
# 設置 partition_hint 爲空串後, 恢復訪問邏輯表的邏輯
mysql> set partition_hint='';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from partition_hint_test_tbl1;
+---------------------+
| a                   |
+---------------------+
| 1999-11-01 00:00:00 |
| 2001-08-19 00:00:00 |
| 1989-10-01 00:00:00 |
+---------------------+
3 rows in set (0.03 sec)

訪問 drds模式 庫下分庫分表

mysql> create database if not exists partition_hint_test_drds mode=drds;
Query OK, 1 row affected (1.29 sec)
mysql> use partition_hint_test_drds
Database changed
mysql> CREATE TABLE if not exists multi_db_multi_tbl(
    ->  id bigint not null auto_increment,
    ->  bid int,
    ->  name varchar(30),
    ->  primary key(id)
    -> ) dbpartition by hash(id) tbpartition by hash(bid) tbpartitions 3;
Query OK, 0 rows affected (1.37 sec)
mysql> show topology from  multi_db_multi_tbl;
+------+---------------------------------------+---------------------------+----------------+
| ID   | GROUP_NAME                            | TABLE_NAME                | PARTITION_NAME |
+------+---------------------------------------+---------------------------+----------------+
|    0 | PARTITION_HINT_TEST_DRDS_000000_GROUP | multi_db_multi_tbl_tqyJ_0 | -              |
|    1 | PARTITION_HINT_TEST_DRDS_000000_GROUP | multi_db_multi_tbl_tqyJ_1 | -              |
|    2 | PARTITION_HINT_TEST_DRDS_000000_GROUP | multi_db_multi_tbl_tqyJ_2 | -              |
|    3 | PARTITION_HINT_TEST_DRDS_000001_GROUP | multi_db_multi_tbl_tqyJ_0 | -              |
|    4 | PARTITION_HINT_TEST_DRDS_000001_GROUP | multi_db_multi_tbl_tqyJ_1 | -              |
|    5 | PARTITION_HINT_TEST_DRDS_000001_GROUP | multi_db_multi_tbl_tqyJ_2 | -              |
|    6 | PARTITION_HINT_TEST_DRDS_000002_GROUP | multi_db_multi_tbl_tqyJ_0 | -              |
|    7 | PARTITION_HINT_TEST_DRDS_000002_GROUP | multi_db_multi_tbl_tqyJ_1 | -              |
|    8 | PARTITION_HINT_TEST_DRDS_000002_GROUP | multi_db_multi_tbl_tqyJ_2 | -              |
|    9 | PARTITION_HINT_TEST_DRDS_000003_GROUP | multi_db_multi_tbl_tqyJ_0 | -              |
|   10 | PARTITION_HINT_TEST_DRDS_000003_GROUP | multi_db_multi_tbl_tqyJ_1 | -              |
|   11 | PARTITION_HINT_TEST_DRDS_000003_GROUP | multi_db_multi_tbl_tqyJ_2 | -              |
+------+---------------------------------------+---------------------------+----------------+
mysql> insert into multi_db_multi_tbl(bid, name) values(1, 'a'),(2, 'b'),(3,'c');
Query OK, 3 rows affected (0.00 sec)
mysql>  set partition_hint='';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from multi_db_multi_tbl;
+--------+------+------+
| id     | bid  | name |
+--------+------+------+
| 200009 |    3 | c    |
| 200008 |    2 | b    |
| 200007 |    1 | a    |
+--------+------+------+
3 rows in set (0.03 sec)
mysql> set partition_hint='PARTITION_HINT_TEST_DRDS_000003_GROUP:1';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from multi_db_multi_tbl;
+--------+------+------+
| id     | bid  | name |
+--------+------+------+
| 200007 |    1 | a    |
+--------+------+------+
1 row in set (0.02 sec)

訪問 drds模式 庫下分庫不分表

mysql> create database if not exists partition_hint_test_drds mode=drds;
Query OK, 1 row affected (1.29 sec)
mysql> use partition_hint_test_drds
Database changed
mysql> CREATE TABLE if not exists multi_db_single_tbl(
    ->   id bigint not null auto_increment,
    ->   name varchar(30),
    ->   primary key(id)
    -> ) dbpartition by hash(id);
Query OK, 0 rows affected (0.93 sec)
mysql> show topology from multi_db_single_tbl;
+------+---------------------------------------+--------------------------+----------------+
| ID   | GROUP_NAME                            | TABLE_NAME               | PARTITION_NAME |
+------+---------------------------------------+--------------------------+----------------+
|    0 | PARTITION_HINT_TEST_DRDS_000000_GROUP | multi_db_single_tbl_pccs | -              |
|    1 | PARTITION_HINT_TEST_DRDS_000001_GROUP | multi_db_single_tbl_pccs | -              |
|    2 | PARTITION_HINT_TEST_DRDS_000002_GROUP | multi_db_single_tbl_pccs | -              |
|    3 | PARTITION_HINT_TEST_DRDS_000003_GROUP | multi_db_single_tbl_pccs | -              |
+------+---------------------------------------+--------------------------+----------------+
4 rows in set (0.01 sec)
mysql> insert into multi_db_single_tbl(name) values('a'),('b'),('c');
Query OK, 3 rows affected (0.05 sec)
mysql> set partition_hint=PARTITION_HINT_TEST_DRDS_000003_GROUP;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from multi_db_single_tbl;
+--------+------+
| id     | name |
+--------+------+
| 100003 | c    |
+--------+------+
1 row in set (0.01 sec)
mysql> set partition_hint='PARTITION_HINT_TEST_DRDS_000003_GROUP:0';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from multi_db_single_tbl;
+--------+------+
| id     | name |
+--------+------+
| 100003 | c    |
+--------+------+
1 row in set (0.02 sec)
mysql> set partition_hint='';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from multi_db_single_tbl;
+--------+------+
| id     | name |
+--------+------+
| 100003 | c    |
| 100002 | b    |
| 100001 | a    |
+--------+------+
3 rows in set (0.04 sec)

典型場景的附加能力

跨分片寫的分佈式事務

在session hint直連場景,也支持分佈式事務,滿足數據的一致性

# 開啓事務
mysql> begin;
# 設置了 partition_hint 之後, 操作物理表1
mysql> set partition_hint=p0;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into partition_hint_test_tbl1 values ("a", 1);
Query OK, 1 rows affected (0.01 sec)
# 設置了 partition_hint 之後, 操作物理表2
mysql> set partition_hint=p2;
mysql> insert into partition_hint_test_tbl2 values ("b", 1);
Query OK, 1 rows affected (0.01 sec)
# 提交事務
mysql> commit;

跨分片事務讀的強一致性

以轉賬場景爲例,假設用戶賬戶是一張分區表,那麼同一筆轉賬交易的轉入方和轉出方很可能位於不同的數據節點上

BEGIN;
UPDATE account SET balance = balance - 20 WHERE name = 'Alice';
UPDATE account SET balance = balance + 20 WHERE name = 'Bob';
COMMIT;

通過多個物理鏈接,設置不同的直連hint來訪問物理數據,會出現account_04 和 account_07在不同的數據庫連接上,會導致這兩個連接是兩個獨立的MVCC事務,會出現看到轉賬數據總和不一致的情況。 解決辦法:

# 業務跑批開始
# 打開一個鏈接A
set autocommit=0;
mysql> set partition_hint=p0;
# 設置了 partition_hint 之後, 指定時間點T,掃描物理表1
mysql> select * from partition_hint_test_tbl1 AS of TIMESTAMP '2022-03-11 09:00:00'
# 打開一個鏈接B
set autocommit=0;
mysql> set partition_hint=p2;
# 設置了 partition_hint 之後, 同樣指定時間點T,掃描物理表2
mysql> select * from partition_hint_test_tbl2 AS of TIMESTAMP '2022-03-11 09:00:00'

藉助FLASH BACK語法,根據用戶指定時間點的構建一致性視圖。

業務SQL操作總結

需要注意和理解的事項

路由一致性

常見的跑批SQL中,一般會有如下特徵:

  1. 事務語句,比如set autocommit/commit等
  2. DML語句,比如批量insert、複雜update/delete等
  3. create tmp表 + Insert...select語句,暫存中間數據
  4. select語句,比如cursor流式查詢一張大表

在直連hint的設計目標是基於數據分片的物理分佈,最大化的提升吞吐量,儘可能將所有操作都直接下推到單個DN節點,比如:

  1. insert...select場景,全下推DN執行相比於分佈式路由執行,會有2~3倍的提升
  2. 基於session hint,將所有的分佈式事務自動優化爲單機1PC事務,減少2PC的網絡交互

基於直連hint的設計,會對業務有一定的約束和要求:

  1. DML語句,不能修改分片字段的值(比如常見的分片字段,一般爲用戶id),否則會導致數據和實際路由規則不一致
  2. insert...select語句,不能變更自增ID列的值,否則會導致自增ID不唯一,同時insert..select涉及的表分片規則和分片數要保持一致,同時都帶上分片字段,確保數據路由的正確性

分佈式元數據一致性

常見的ORM框架,會通過meta元數據自動構建SQL語句,比如

  1. 查詢information_schema下的庫表和字段等相關信息,比如schemata/tables/columns等
  2. 通過DAL查詢庫表和字段等相關信息,比如show databases/show tables/show columns/show create table等

存在的問題: 物理DN上對應的表名爲orders_0001,如果直連DN訪問元數據,會導致邏輯表和物理分片信息元數據對不齊,導致報錯 解決辦法:元數據不受直連hint的影響,直接訪問分佈式的元數據,確保多分片場景下的元數據對齊。

其他一些注意事項

  • 物理表中的隱藏主鍵。PolarDB-X針對業務創建表不含主鍵,會自動爲該邏輯表建立隱式主鍵, 此時通過 session hint 直接訪問 db 分片的話, select * 會使隱式主鍵暴露, 需要注意此時的返回類型差異;
  • Session hint 默認不支持 dml 變更數據。如果業務上確實需要通過 Session hint 直連 db 變更數據,可以設置set ENABLE_FORBID_PUSH_DML_WITH_HINT=false,設置該參數後直連DB變更數據有以下風險:
  • 數據寫入到錯誤的分片導致邏輯表的訪問無法感知
  • 帶 gsi 的邏輯表會出現數據不一致
  • 廣播表會出現各表不一致
  • 在parition變量的設置的前提下,不支持帶了庫名的查詢,比如
# 設置test庫的p0 hint
mysql> set partition_hint=p0;
#不支持
mysql> select * from test.t1;
  • 在partition變量設置的前提下,不支持業務表和information_schema做關聯查詢,因爲業務庫表會被下推;而information_schema庫不下推。
  • 在drds/auto模式庫下正常的擴縮容不會影響分區名或者GROUP名的變更,但在auto模式庫下支持更靈活的分區變更能力(比如:分區rehash、熱點分區、分裂合併等操作), 由於partition個數或名稱發生了變化,業務上通過partition_hint指定的值需要同步做下修改。

雲原生數據庫PolarDB分佈式版新增標準版形態,基於X-Paxos提供100%兼容MySQL的高可靠性集中式數據庫服務。

作者:方物

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章