背景資訊
本文主要提供資料庫上雲後OLTP+OLAP一體化架構的最佳實務,即HTAP。主要面對的業務應用範圍如下:
混合型HTAP資料庫需求,如Oracle資料庫改造上雲,雲上資料庫方案選型;
OLTP系統查詢慢,存在分析型情境和瓶頸的客戶;
讀寫分離需求。
PolarDB-X HTAP架構更多資訊,請參見混合負載HTAP。
HTAP叢集
您購買的PolarDB-X主執行個體,主要面向線上通用業務情境。如果業務針對同一份資料有分析、專註離線拖數、跑批等情境,您可以在PolarDB-X主執行個體上購買多個唯讀執行個體。
業務如果有線上HTAP混合流量或者讀寫分離的需求,推薦使用叢集地址。PolarDB-X內部會基於智能路由或者讀寫權重將部分流量轉寄給唯讀執行個體;業務上只有離線資料分析需求時,推薦使用唯讀地址,唯讀地址會直接存取唯讀執行個體,唯讀地址的流量會採用MPP加速。關於串連地址資訊,請參見配置讀寫分離。
路由
智能路由
PolarDB-X最佳化器會基於代價分析出查詢物理掃描行數、CPU、記憶體、IO、網路等核心資源消耗量,將請求區分為TP與AP負載。當您在叢集地址上開啟了智能路由,會主動識別SQL的工作負載類型來做路由,比如將識別為AP負載的流量路由給唯讀執行個體。您可以通過explain cost指令查看SQL工作負載類型的識別情況。例如以下查詢,該查詢涉及到物理掃描行數rowcount很小,計算資源(CPU&Memory)也消耗比較少,所以這個查詢被識別為TP負載。
explain cost select a.k, count(*) cnt from sbtest1 a, sbtest1 b where a.id = b.k and a.id > 1000 group by k having cnt > 1300 order by cnt limit 5, 10; |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| TopN(sort="cnt ASC", offset=?2, fetch=?3): rowcount = 1.0, cumulative cost = value = 2.8765038E7, cpu = 37.0, memory = 64.0, io = 3.0, net = 5.75, id = 163602178 |
| Filter(condition="cnt > ?1"): rowcount = 1.0, cumulative cost = value = 2.8765026E7, cpu = 26.0, memory = 47.0, io = 3.0, net = 5.75, id = 163602177 |
| HashAgg(group="k", cnt="COUNT()"): rowcount = 1.0, cumulative cost = value = 2.8765025E7, cpu = 25.0, memory = 47.0, io = 3.0, net = 5.75, id = 163602171 |
| BKAJoin(condition="k = id", type="inner"): rowcount = 1.0, cumulative cost = value = 2.8765012E7, cpu = 12.0, memory = 18.0, io = 3.0, net = 5.75, id = 163602169 |
| Gather(concurrent=true): rowcount = 1.0, cumulative cost = value = 2.3755003E7, cpu = 3.0, memory = 0.0, io = 1.0, net = 4.75, id = 163602164 |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `id`, `k` FROM `sbtest1` AS `sbtest1` WHERE (`id` > ?)"): rowcount = 1.0, cumulative cost = value = 2.3755002E7, cpu = 2.0, memory = 0.0, io = 1.0, net = 4.75, id = 163601451 |
| Gather(concurrent=true): rowcount = 1.0, cumulative cost = value = 5003.0, cpu = 3.0, memory = 0.0, io = 1.0, net = 0.0, id = 163602167 |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `k` FROM `sbtest1` AS `sbtest1` WHERE ((`k` > ?) AND (`k` IN (...)))"): rowcount = 1.0, cumulative cost = value = 5002.0, cpu = 2.0, memory = 0.0, io = 1.0, net = 0.0, id = 163601377 | |
| WorkloadType: TP | |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+工作負載的識別,對於HTAP路由至關重要。這裡也允許您通過HINT WORKLOAD_TYPE指定工作負載。同樣以上述查詢為例,可以將查詢負載強制指定為AP。
explain cost /*+TDDL:WORKLOAD_TYPE=AP*/ select a.k, count(*) cnt from sbtest1 a, sbtest1 b where a.id = b.k and a.id > 1000 group by k having cnt > 1300 order by cnt limit 5, 10; |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| TopN(sort="cnt ASC", offset=?2, fetch=?3): rowcount = 1.0, cumulative cost = value = 2.8765038E7, cpu = 37.0, memory = 64.0, io = 3.0, net = 5.75, id = 163602178 |
| Filter(condition="cnt > ?1"): rowcount = 1.0, cumulative cost = value = 2.8765026E7, cpu = 26.0, memory = 47.0, io = 3.0, net = 5.75, id = 163602177 |
| HashAgg(group="k", cnt="COUNT()"): rowcount = 1.0, cumulative cost = value = 2.8765025E7, cpu = 25.0, memory = 47.0, io = 3.0, net = 5.75, id = 163602171 |
| BKAJoin(condition="k = id", type="inner"): rowcount = 1.0, cumulative cost = value = 2.8765012E7, cpu = 12.0, memory = 18.0, io = 3.0, net = 5.75, id = 163602169 |
| Gather(concurrent=true): rowcount = 1.0, cumulative cost = value = 2.3755003E7, cpu = 3.0, memory = 0.0, io = 1.0, net = 4.75, id = 163602164 |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `id`, `k` FROM `sbtest1` AS `sbtest1` WHERE (`id` > ?)"): rowcount = 1.0, cumulative cost = value = 2.3755002E7, cpu = 2.0, memory = 0.0, io = 1.0, net = 4.75, id = 163601451 |
| Gather(concurrent=true): rowcount = 1.0, cumulative cost = value = 5003.0, cpu = 3.0, memory = 0.0, io = 1.0, net = 0.0, id = 163602167 |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `k` FROM `sbtest1` AS `sbtest1` WHERE ((`k` > ?) AND (`k` IN (...)))"): rowcount = 1.0, cumulative cost = value = 5002.0, cpu = 2.0, memory = 0.0, io = 1.0, net = 0.0, id = 163601377 | |
| WorkloadType: AP | |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+規則路由
除了基於代價的智能路由以外,我們也支援基於讀寫規則的路由。允許您在控制台參數管理中設定讀寫分離權重參數MASTER_READ_WEIGHT,預設值為100,可配置值區間[0,100]。如果配置為Weight=60,意味著佔60%的流量會繼續在主執行個體執行,40%的剩餘流量會路由到唯讀執行個體執行,如果唯讀執行個體有多個會進行自動分配。
智能路由和規則路由這兩者關係是解耦的,具體關係請查看下錶格。
智能路由規則 | 規則路由 (MASTER_READ_WEIGHT) | 路由結果 |
開啟 | 以代價的讀寫分離為主規則路由建議保持預設值為100 |
|
關閉 | 以規則的讀寫分離為主規則路由的可選範圍:[0,100] |
|
執行模式
目前PolarDB-X支援了三種執行模式:
單機單線程(TP_LOCAL):查詢過程是單線程計算,TP負載的查詢涉及到的掃描行數比較少,往往會採樣這種執行模式,比如基於主鍵的點查。
單機並行(AP_LOCAL):查詢過程會利用節點的多核資源做並行計算,您在沒有購買唯讀執行個體的前提下,針對AP負載的查詢,往往會採樣這種執行模式,一般也稱之為Parallel Query模式。
多機並行(MPP):您若購買了唯讀執行個體,針對AP負載的查詢,可以協調唯讀執行個體上多個節點的多核做分布式多機並行加速。
為了讓您可以準確知道執行模式,我們在原有EXPLAIN指令上,擴充出了EXPLAIN PHYSICAL。例如以下查詢,通過指令可以查看到當前查詢採用的是MPP模式,此外還可以擷取到每個執行片段的並發數。
explain physical select a.k, count(*) cnt from sbtest1 a, sbtest1 b where a.id = b.k and a.id > 1000 group by k having cnt > 1300 or
der by cnt limit 5, 10;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| PLAN |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ExecutorType: MPP |
| The Query's MaxConcurrentParallelism: 2 |
| Fragment 1 |
| Shuffle Output layout: [BIGINT, BIGINT] Output layout: [BIGINT, BIGINT] |
| Output partitioning: SINGLE [] Parallelism: 1 |
| TopN(sort="cnt ASC", offset=?2, fetch=?3) |
| Filter(condition="cnt > ?1") |
| HashAgg(group="k", cnt="COUNT()") |
| BKAJoin(condition="k = id", type="inner") |
| RemoteSource(sourceFragmentIds=[0], type=RecordType(INTEGER_UNSIGNED id, INTEGER_UNSIGNED k)) |
| Gather(concurrent=true) |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `k` FROM `sbtest1` AS `sbtest1` WHERE ((`k` > ?) AND (`k` IN (...)))") |
| Fragment 0 |
| Shuffle Output layout: [BIGINT, BIGINT] Output layout: [BIGINT, BIGINT] |
| Output partitioning: SINGLE [] Parallelism: 1 Splits: 16 |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `id`, `k` FROM `sbtest1` AS `sbtest1` WHERE (`id` > ?)") |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+工作負載和執行模式有一定的耦合關係,AP工作負載會採用多機並行計算。同樣的也允許您通過HINT EXECUTOR_MODE指定執行模式。假如主執行個體空閑資源很多,可以考慮強制設定為單機或者多機並行模式來加速。
explain physical /*+TDDL:EXECUTOR_MODE=AP_LOCAL*/select a.k, count(*) cnt from sbtest1 a, sbtest1 b where a.id = b.k and a.id > 1000 group by k having cnt > 1300 order by cnt limit 5, 10; |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ExecutorMode: AP_LOCAL |
| Fragment 0 dependency: [] parallelism: 4 |
| BKAJoin(condition="k = id", type="inner") |
| Gather(concurrent=true) |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `id`, `k` FROM `sbtest1` AS `sbtest1` WHERE (`id` > ?)") |
| Gather(concurrent=true) |
| LogicalView(tables="[000000-000003].sbtest1_[00-15]", shardCount=16, sql="SELECT `k` FROM `sbtest1` AS `sbtest1` WHERE ((`k` > ?) AND (`k` IN (...)))") |
| Fragment 1 dependency: [] parallelism: 8 |
| LocalBuffer |
| RemoteSource(sourceFragmentIds=[0], type=RecordType(INTEGER_UNSIGNED id, INTEGER_UNSIGNED k, INTEGER_UNSIGNED k0)) |
| Fragment 2 dependency: [0, 1] parallelism: 8 |
| Filter(condition="cnt > ?1") |
| HashAgg(group="k", cnt="COUNT()") |
| RemoteSource(sourceFragmentIds=[1], type=RecordType(INTEGER_UNSIGNED id, INTEGER_UNSIGNED k, INTEGER_UNSIGNED k0)) |
| Fragment 3 dependency: [0, 1] parallelism: 1 |
| LocalBuffer |
| RemoteSource(sourceFragmentIds=[2], type=RecordType(INTEGER_UNSIGNED k, BIGINT cnt)) |
| Fragment 4 dependency: [2, 3] parallelism: 1 |
| TopN(sort="cnt ASC", offset=?2, fetch=?3) |
| RemoteSource(sourceFragmentIds=[3], type=RecordType(INTEGER_UNSIGNED k, BIGINT cnt)) |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+多機並行MPP執行模式的並發度是根據物理掃描行數、執行個體規格和計算所涉及到表的分表數計算出來的,整體的並行度要考慮高並發情境,所以並行度的計算會偏保守,您可以通過上述EXPLAIN PHYSICAL指令查看並行度。當然也同樣支援HINT MPP_PARALLELISM強制指定並行度,
/*+TDDL:EXECUTOR_MODE=MPP MPP_PARALLELISM=8*/select a.k, count(*) cnt from sbtest1 a, sbtest1 b where a.id = b.k and a.id > 1000 group by k having cnt > 1300 order by cnt limit 5, 10;調度策略
假設您購買了多個唯讀執行個體並加入到叢集地址中,通過叢集地址的查詢SQL路由到唯讀執行個體的流量,會被均勻調度到唯讀執行個體多個節點上執行,調度會考慮各個節點的資源負載,確保各個節點的負載壓力差不多。比如PolarDB-X會將唯讀執行個體延遲作為調度參考指標,避免將流量調度到延遲較大的唯讀執行個體上執行。
反饋機制
基於統計資訊智能識別負載存在一定的誤差。在負載識別出錯的情況下,PolarDB-X能基於查詢最終真實掃描的物理行數和執行時間,重新修正工作負載,這個過程稱之為負載的自適應反饋。根據執行計畫管理一文,PolarDB-X的執行計畫會被管理起來,同樣其工作負載也會被管理起來,可以通過以下語句,查看執行計畫管理中各個計劃的工作負載。
baseline [Select Statemtnt]比如當一條TP負載的簡單查詢,執行時間和真實掃描行數都大於閾值,這個查詢就會被修正為AP工作負載,寫入到執行計畫管理狀態中,同理反之。除了上述提到了自適應反饋能力以外,也可以通過以下語句,手動修正計劃管理中的工作負載。
baseline fix sql /*+TDDL:WORKLOAD_TYPE=AP*/ [Select Statemtnt]執行計畫管理中的計劃負載被合理修正後,同類型的查詢會從執行計畫管理中擷取正確的工作負載標記。
一致性讀
從配置讀寫分離一文可瞭解,業務流量直連唯讀地址,提供了可配置的全域一致性讀能力。業務流量是通過叢集地址,路由給唯讀執行個體,預設就會開啟全域一致性讀能力。全域一致性讀機制,可以確保業務在主執行個體資料寫入成功後就一定可以在唯讀庫讀到寫入的資料,避免因傳統讀寫分離架構,資料複寫延遲帶來的資料寫後讀不一致問題。
如果業務使用的叢集地址,對資料一致性讀並沒有強烈訴求,例如業務可以忽略當前唯讀執行個體複寫延遲帶來的資料影響,我們也支援在參數管理頁面上配置ENABLE_CONSISTENT_REPLICA_READ,或者通過HINT對某個查詢關閉一致性讀能力。
/*+TDDL:ENABLE_CONSISTENT_REPLICA_READ=false*/ [Select Statemtnt]常見問題
Q: 在叢集地址中開啟了智能路由後,是否還有必要配置MASTER_READ_WEIGHT的規則路由?
A: PolarDB-X智能路由主要的機制是將查詢SQL中偏AP的複雜查詢路由到唯讀執行個體中來減輕主執行個體的壓力,如果此時TP簡單查詢並發很高,佔用了主執行個體比較大的資源,也可以額外開啟規則路由,將部分TP流量分攤到唯讀執行個體中。
Q: 叢集地址的讀寫分離,是否還相容傳統按比例的讀寫入模式,對應的區別點和優勢分別是什嗎?
A: PolarDB-X支援智能路由和規則路由兩種模式,基於規則路由可以相容傳統的讀寫分離模式。PolarDB-X的讀寫分離優勢在於引入了一致性讀的機制,可以很好的規避唯讀執行個體複寫延遲帶來的資料寫後讀的查詢一致性問題。
Q: 智能路由識別的SQL負載類型,是否有辦法查詢,如果識別不正確的情況下對應的影響是什麼,以及如何修正?
A: PolarDB-X可以通過執行計畫管理的功能查詢歷史執行過的SQL模板和執行計畫,針對智能路由自身會基於實際執行後的反饋來對SQL負載類型進行修正。同時也允許業務通過執行計畫管理的方式,基於hint的方式強制設定工作負載類型來做修改。
Q: 基於叢集地址的HTAP模式,和傳統的OLTP + DTS + OLAP組合的解決方案相比,有什麼特點和優勢?
A: PolarDB-X的HTAP模式,基於資料庫原生的多副本能力,簡化了外置DTS同步的營運複雜性和同步成本,同時引入一致性讀機制,並結合MPP的多機並行計算能力,可以很好滿足業務對資料線上計算的即時性和擴充性的要求。