PolarDB-X 分區建列類型變更

背景

縱觀數據庫領域數十年來的發展,關係型數據庫脫穎而出的一個重要原因是,它支持用戶靈活地定義和修改“數據模型”。

PolarDB-X 作爲一款雲原生關係型數據庫,同樣支持通過各種 DDL 語句對數據模型進行修改,以滿足用戶業務的不斷髮展,例如:可以使用 ALTER TABLE 語句對錶進行添加列,刪除列,修改列類型等操作。然而,PolarDB-X 作爲一款分佈式數據庫,其一張邏輯表通常通過某種分區方式將數據劃分成多個分片(又稱爲物理表),並且這些分片分佈在不同的數據節點中[1][2],這使得 DDL 語句的實現會更加複雜。

本文以列類型變更爲例,簡單介紹在 PolarDB-X 中如何執行 ALTER TABLE 語句。首先,列類型變更分爲兩種:一種是變更分區鍵列類型,另一種是變更非分區鍵列類型。對於非分區鍵的列類型變更,可以直接將邏輯 DDL 拆分成多個物理 DDL,直接下推到對應分片上執行;對於分區鍵的列類型變更,則相對複雜,在修改列類型的同時,還需要對數據進行重分佈,因爲分區鍵列類型修改會影響到分片的路由,如果只是簡單的下推執行,會導致使用分區鍵進行查詢時,查詢不到數據。

實際上,作爲一款分佈式數據庫,變更表的列類型,無論變更的是否是分區鍵,都需要保證各個分片以及元數據的一致性,因此對於非分區鍵的列類型變更也不只是簡簡單單的下推執行就可以的,後續會有一篇文章做詳細的闡述,本文主要闡述的是如何對分區鍵的列類型做變更。

傳統實現

傳統分佈式數據庫中間件採用分庫分表的方式對錶進行拆分,通常是不允許對拆分鍵列類型進行變更,如果想要做變更,一般需要重新建一張表,並且停寫之後重新導入數據。

如果想要變更的時候不停寫,則需要在導入存量數據的同時,自行維護一套雙寫的邏輯,這種操作方式不僅複雜,而且很難校驗最後數據的正確性,導致很容易出現數據不一致的問題。

實現

前文中介紹了PolarDB-X拆分規則變更的實現原理[3],該變更過程同樣需要對數據進行重分。

作爲數據重分佈的經典案例,拆分規則變更過程需經歷建新表、雙寫、導入存量數據、數據校驗、流量切換等步驟,整個流程已經非常成熟。很容易想到的是,變更分區鍵列類型可以基於該流程修改來完成,下面介紹該功能的詳細實現。

數據重分佈過程中的增量數據雙寫、存量數據同步以及如何進行流量切換在文章中已經詳細描述了,這裏不再贅述,強烈建議沒有看過的同學,再看一下這篇文章以及 Online Schema Change 這篇論文[4]。

需要補充的一點是,我們還做了基於 TSO 快照[5]的物理數據校驗功能,以保證變更前後數據的正確性。 回到本文主題,分區建列類型變更具體與拆分規則變更有以下幾點不同,下面詳細闡述。

  • 創建新表
  • 對於全局二級索引(GSI)處理
  • 數據校驗

創建新表

對於拆分規則變更而言,該功能只會修改分區規則,並不會修改列定義,因此新建表的表結構與原表完全一致,列定義不會做修改。而分區鍵列類型變更需要修改分區鍵的列定義,因此新建表的表結構與原表不完全一致,除了分區鍵的列定義以外其他定義是一樣的。

因爲新表的列定義與原表的列定義不一致,所以原有的增量數據雙寫以及存量數據同步流程都存在類型隱式轉換,那需不需要對這兩個過程做修改呢?答案是不需要的,這是因爲對於分區鍵而言,在 CN(計算節點)上兼容了 DN(數據節點)的類型隱式轉換,能夠保證使用隱式轉換前的數據和隱式轉換後的數據都可以路由到同一個分片中,不需要擔心路由問題。

另外,熟悉 MySQL 的同學可能知道在 MySQL 中,ALTER TABLE MODIFY COLUMN 的轉換和DML的隱式轉換邏輯存在差異,這樣可能會導致通過 BINLOG 同步的下游與上游數據不一致,這個問題會在數據校驗章節進行解答。

全局二級索引處理

文章中介紹了 PolarDB-X 的全局二級索引[6],全局二級索引爲了方便回表查詢,默認包含了主表的主鍵以及分區鍵作爲 Cover 列。

爲了保證 GSI 和主表數據的一致性,在變更主表分區鍵列類型時,所有 GSI 的對應 Cover 列的類型也需要同時做變更,因此變更主表的分區鍵列類型其實會將GSI表的數據也進行重分佈。

如果主表的分區鍵和 GSI 的分區鍵不一致,且對 GSI 的分區鍵列類型做變更,爲了保證數據的一致性,還是需要走相同流程。

數據校驗

爲了保證變更的正確性,在創建新表、開啓增量數據雙寫以及存量數據同步都完成之後,需要穿插一個數據校驗步驟,數據校驗通過之後,纔是流量切換以及原表優雅下線過程。

這裏先簡單介紹一下數據校驗的邏輯,首先我們在DN端實現了一種順序無關的哈希算法並將其封裝稱爲 UDF,在 CN 開始進行校驗時,首先利用 TSO 事務獲取到源表和目標表的一致性快照,然後分別對源表和目標表對應的 DN 端每個分片進行全表的 hashcheck計算(並行),並將結果拉取到 CN節點彙總,計算出源表的 checksum 和目標表的 checksum,最後進行比較即可。

同構表(分片間並行):

異構表(分片間並行):

對於分區鍵列類型變更而言,源表和目標表在列定義上不完全一致,直接進行校驗肯定會導致校驗失敗。例如,源表分區鍵列類型是 VARCHAR,存在一條數據是'123abc',現將分區鍵列類型修改爲 INT,那麼目標表分區鍵對應的數據則轉換成了 123,123和'123abc'的 hashcheck值自然是不一樣的,因此校驗會失敗。爲了解決上述問題,在創建新表後,爲源表添加了一個僅用於數據校驗虛擬列(對外不可見),並且該虛擬列是在分區鍵列的基礎上調用列類型轉換函數。例如,在上面的例子中,對源表添加一個虛擬列,那麼該虛擬列的值即爲'123abc'調用轉換函數後的結果123,結果與目標表一致。利用添加虛擬列的方式,即可完成數據校驗,並且該虛擬列調用的列類型轉換函數與 MySQL 中 ALTER TABLE MODIFY COLUMN 轉換的處理邏輯一致,因此還可以校驗出 DML 隱式轉換與 ALTER TABLE MODIFY COLUMN 轉換不一致的情況。

查看DDL執行計劃

分區鍵列類型變更並不是一定需要數據重分佈,例如對於字符串列類型來說,如果只是想變長,並不修改字符串的 CHARSET 和 COLLATE,那麼其實是不需要進行數據重分佈的,執行過程與非分區鍵列類型變更相似。另外,用戶可能剛好修改的是 GSI 的分區鍵列類型,不是主表的分區鍵,這樣仍然會產生數據重分佈。可以看到列類型變更其實存在好幾種場景,爲了便於用戶快速區分列類型變更具體是走的什麼流程,我們提供了類似 explian 的操作給 DDL語句來使用,下面以sysbench 表舉幾個例子進行說明。

建表語句:

例1,修改非分區鍵列類型:

例2,修改分區鍵列類型,並且需要數據重分佈:

其中 CREATE TABLE 爲創建新表,DROP TABLE 則爲完成校驗之後刪除舊錶,ALTER TABLE 爲添加虛擬列用於數據校驗。

例3,修改分區鍵列類型,無需數據重分佈:

首先將分區鍵id列修改爲 varchar(30),該過程需要數據重分佈,然後再將其類型修改爲 varchar(60),explain 結果如下,可以看到無需數據重分佈(不需要建表刪表)。

總結

靈活的對錶列類型變更是分佈式數據庫的重要特性。PolarDB-X 支持了分區鍵列類型變更的同時,保證了數據的強一致、高可用、對業務透明、去除了分佈式帶來的限制並且使用起來非常方便。本文在 PolarDB-X 拆分規則變更的基礎上,簡單闡述了實現分區鍵列類型變更過程中使用到的各項技術點,PolarDB-X 之所以能夠支持該功能,使用到了很多諸如 TSO 事務之類的特性,這也是分佈式數據庫區別於分佈式數據庫中間件的重要特性之一。

作者:無沐

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

原文鏈接

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

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