計(jì)解讀:utf8mb4_zh_pinyin_tidb_as_cs 校對(duì)規(guī)則的原理與落地)
TiDB 中文拼音排序方案設(shè)計(jì)解讀utf8mb4_zh_pinyin_tidb_as_cs 校對(duì)規(guī)則的原理與落地【免費(fèi)下載鏈接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ti/tidb本篇圍繞 TiDB 倉庫中的設(shè)計(jì)文檔 2020-09-12-utf8mb4-pinyin-order.md 展開系統(tǒng)講解 TiDB 為utf8mb4字符集引入中文拼音排序所設(shè)計(jì)的全新校對(duì)規(guī)則collationutf8mb4_zh_pinyin_tidb_as_cs。文章將帶你理解該規(guī)則的命名語義、底層排序權(quán)重的編碼規(guī)則、Collation ID 2048 的選取理由、與既有排序規(guī)則的兼容矩陣并對(duì)照當(dāng)前倉庫源碼parser 注冊(cè)表、collator 映射、測(cè)試用例梳理該特性從設(shè)計(jì)到代碼的真實(shí)落地狀態(tài)。讀完你可以掌握中文按拼音 ORDER BY 的問題根源、TiDB 自定義排序規(guī)則的完整設(shè)計(jì)套路以及在當(dāng)前版本中該 collation 已注冊(cè)到哪一層、實(shí)現(xiàn)到什么程度。背景為什么 TiDB 需要中文拼音排序在關(guān)系型數(shù)據(jù)庫中字符串ORDER BY的結(jié)果完全由列上聲明的 collation 決定。對(duì)于中文場(chǎng)景用戶常見的訴求之一是按拼音排序——例如姓名、城市名、商品名等希望以拼音字母a–z為序。設(shè)計(jì)文檔指出彼時(shí)的 TiDB 無法基于拼音對(duì)一列漢字做排序。文檔給出了一個(gè)具體示例create table t( a varchar(100) ) charset utf8mb4 collate utf8mb4_zh_0900_as_cs; # insert some data: insert into t values (中文), (啊中文); # a query requires to order by column a in its pinyin order: select * from t order by a; ----------- | a | ----------- | 啊中文 | | 中文 | ----------- 2 rows in set (0.00 sec)utf8mb4_zh_0900_as_cs是 MySQL 提供的 UCA 9.0.0 中文排序規(guī)則zh即 Chinese、as_cs即 accent-sensitive / case-sensitive。文檔引用該示例的用意在于說明當(dāng)中文排序的語義無法被正確、可預(yù)期地實(shí)現(xiàn)時(shí)僅靠直接ORDER BY得到的結(jié)果并不能穩(wěn)定滿足業(yè)務(wù)對(duì)拼音序的預(yù)期。要真正解決這類問題需要數(shù)據(jù)庫內(nèi)核提供語義正確且可復(fù)現(xiàn)的拼音排序能力。補(bǔ)充本提案討論的背景演進(jìn)可參見設(shè)計(jì)文檔末尾列出的 issue #19747 與 #10192。方案總覽utf8mb4_zh_pinyin_tidb_as_cs是什么本提案的核心產(chǎn)出是新增一個(gè)名為utf8mb4_zh_pinyin_tidb_as_cs的校對(duì)規(guī)則。命名本身就是一份完整的規(guī)格說明拆解如下命名片段含義utf8mb4適用的字符集為utf8mb4zh面向中文Chinese語言pinyin提供基于拼音pinyin的排序次序tidb表示這是 TiDB 的特殊custom版本as_csaccent-sensitive 且 case-sensitive區(qū)分重音與大小寫能力邊界該規(guī)則支持全部 Unicode 字符參與排序且能根據(jù) CLDR24 中zh.xml文件定義的拼音 collation 次序?qū)χ形淖址M(jìn)行正確排序目前只支持zh.xml中擁有拼音條目的漢字對(duì)于字形與漢字相同、但 Unicode 歸類為 Symbol符號(hào)的 CJK 字符以及拼音字符本身均不提供拼音排序支持。為什么不自研utf8mb4_zh_0900_as_csAdvantages文檔明確給出了選擇自研而非實(shí)現(xiàn) MySQL 同款規(guī)則的理由實(shí)現(xiàn)utf8mb4_zh_0900_as_cs工作量大MySQL 的實(shí)現(xiàn)方式涉及權(quán)重重排weight reorders、魔法數(shù)字magic numbers與大量技巧復(fù)雜且難維護(hù)。相比之下utf8mb4_zh_pinyin_tidb_as_cs實(shí)現(xiàn)路徑簡單直接它覆蓋全部漢字、按拼音次序排序?qū)Ρ咎岚傅哪繕?biāo)場(chǎng)景足夠好。代價(jià)Disadvantages它不兼容 MySQL——MySQL 中并不存在名為utf8mb4_zh_pinyin_tidb_as_cs的校對(duì)規(guī)則。這一不兼容性是后續(xù)所有遷移與同步方案設(shè)計(jì)的出發(fā)點(diǎn)。核心設(shè)計(jì)排序權(quán)重Compare / Key如何編碼排序的本質(zhì)是比較排序鍵sort key / weight。設(shè)計(jì)文檔給出了三條權(quán)值計(jì)算規(guī)則它們決定了每個(gè)字符在utf8mb4_zh_pinyin_tidb_as_cs下的相對(duì)次序中文字符凡在zh.xml中按 gb18030 編碼查到非零seq No.的漢字其最終權(quán)重為0xFFA00000 seq No.。非中文的 gb18030 雙字節(jié)字符 C最終權(quán)重取C本身。非中文的 gb18030 四字節(jié)字符 C最終權(quán)重為0xFF000000 diff(C)其中diff通過算法求得。從這套編碼可以讀出幾個(gè)設(shè)計(jì)意圖結(jié)合規(guī)則推演屬合理推斷而非文檔明示以gb18030 編碼作為漢字與zh.xml條目之間的橋梁是因?yàn)?gb18030 覆蓋整個(gè) Unicode 碼域gb18030_chinese_ci.go 中即注釋 Unicode code points up to U10FFFF can be encoded as GB18030任何 Unicode 字符都能先落到一個(gè)確定的 gb18030 字節(jié)序列再映射為拼音序號(hào)拼音序號(hào)被平移到0xFFA00000這個(gè)高基地址上與非中文雙字節(jié)字符權(quán)重即其自身量級(jí)明顯更小天然拉開區(qū)間從而任意中文字符的拼音權(quán)重落在同一高位段、且嚴(yán)格按 seq 遞增四字節(jié)非中文字符落在0xFF000000基線之上需要額外保證其diff值不會(huì)與漢字拼音區(qū)間0xFFA00000起產(chǎn)生重疊。按文檔表述這與雙字節(jié)規(guī)則共同構(gòu)成一個(gè)非中文在前、中文按拼音在后的整體次序模型。整體看這套方案的巧妙之處在于不需要復(fù)刻 MySQL 那套繁復(fù)的 UCA 中文權(quán)重表而是用gb18030 編碼 zh.xml 拼音序號(hào) 區(qū)間化權(quán)值三要素直接生成可比較的排序鍵。Parser 側(cè)落地為 collation 挑選 ID 2048一個(gè)可被 SQL 層識(shí)別的 collation首先要在 parser 的字符集/排序規(guī)則注冊(cè)表中擁有唯一 ID。提案選擇 ID2048并寫入 parser。文檔援引了 MySQL 官方的 ID 規(guī)劃MySQL 支持雙字節(jié) collation ID其中1024–2047 區(qū)間預(yù)留給用戶自定義排序規(guī)則user-defined collations。因此選擇2048恰好落在該保留區(qū)間之外可避免與 MySQL 用戶自定義規(guī)則的 ID 空間沖突。對(duì)照當(dāng)前倉庫源碼這條注冊(cè)記錄確實(shí)已經(jīng)落地pkg/parser/charset/charset.go 中存在{2048, utf8mb4, utf8mb4_zh_pinyin_tidb_as_cs, false, 1, PadNone}即字符集為utf8mb4、ID 為2048、padding 方式為PadNone。與現(xiàn)有 collation 的兼容性矩陣設(shè)計(jì)文檔規(guī)定utf8mb4_zh_pinyin_tidb_as_cs與utf8mb4_unicode_ci、utf8mb4_general_ci擁有相同優(yōu)先級(jí)三者彼此不兼容——即兩個(gè) collation 一旦混用例如 JOIN 兩表、比較不同 collation 的列TiDB 不會(huì)自動(dòng)將其視為可兼容并做隱式轉(zhuǎn)換。這個(gè)兼容/不兼容的判定在引擎?zhèn)扔袑iT實(shí)現(xiàn)可參考 collate.go 的CompatibleCollate其中對(duì)general_ci家族、bin家族、unicode_ci家族分別做了同族互認(rèn)的處理其余情況嚴(yán)格按名字相等判定。中文拼音規(guī)則作為新成員不在任何既有家族內(nèi)自然只能與自身相等與unicode_ci、general_ci均判定為不兼容與文檔描述一致。與 MySQL 的兼容性及遷移建議由于 MySQL 沒有utf8mb4_zh_pinyin_tidb_as_cs這一 collation文檔給出的遷移指引很直接當(dāng)用戶需要把數(shù)據(jù)從 TiDB 復(fù)制replicate到 MySQL 時(shí)應(yīng)當(dāng)對(duì)使用該 collation 的列做處理如注釋/改寫 collation避免下游 MySQL 無法解析。換句話說該規(guī)則是TiDB 內(nèi)可用、出 TiDB 需轉(zhuǎn)換的方言特性適合在純 TiDB 生態(tài)內(nèi)部使用排序、索引、主從均為 TiDB 的場(chǎng)景一旦涉及 MySQL 下游同步就必須在 schema 層顯式改寫。從設(shè)計(jì)到代碼該特性在當(dāng)前倉庫的落地現(xiàn)狀設(shè)計(jì)文檔是 2020 年提出的方案那么它在當(dāng)前倉庫中落地到哪一步了結(jié)合源碼可以給出精確答案。1. 注冊(cè)已就位名字與 ID 雙雙進(jìn)入映射表除了 parser 側(cè)的 ID 注冊(cè)外運(yùn)行期的 collator 映射表也已登記collate.go 同時(shí)將utf8mb4_zh_pinyin_tidb_as_cs寫入newCollatorMap與newCollatorIDMap指向zhPinyinTiDBASCSCollator由于utf8mb4家族默認(rèn) collation 之外的名字統(tǒng)一經(jīng)GetCollator/GetCollatorByID分發(fā)見 collate.go只要命中映射表即可取到對(duì)應(yīng) collator 實(shí)例。2. 運(yùn)行時(shí)實(shí)現(xiàn)仍是占位樁stub打開 collator 本體文件 pinyin_tidb_as_cs.go會(huì)發(fā)現(xiàn)結(jié)構(gòu)體zhPinyinTiDBASCSCollator雖然實(shí)現(xiàn)了Collator接口的全部方法Compare、Key、ImmutableKey、KeyWithoutTrimRightSpace、MaxKeyLen、Pattern、Clone但所有方法體目前都是panic(implement me)占位。這意味著從當(dāng)前倉庫快照看utf8mb4_zh_pinyin_tidb_as_cs的運(yùn)行時(shí)排序邏輯尚未真正實(shí)現(xiàn)仍處于開發(fā)中的狀態(tài)。這一點(diǎn)與 collate.go 的注釋相互印證// utf8mb4_zh_pinyin_tidb_as_cs is under developing, should not be shown to user. if name utf8mb4_zh_pinyin_tidb_as_cs { continue }即GetSupportedCollations()對(duì)應(yīng)SHOW COLLATION會(huì)顯式過濾掉該 collation不向用戶展示。3. 新排序規(guī)則開關(guān)與回退行為TiDB 的新排序規(guī)則new collations是否啟用對(duì)應(yīng)NewCollationEnabled()判定會(huì)顯著影響該 collation 的表現(xiàn)new collations開啟時(shí)GetCollator(utf8mb4_zh_pinyin_tidb_as_cs)與GetCollatorByID(2048)能命中上述 stub 實(shí)例new collations關(guān)閉時(shí)GetCollatorWithCollate 與 GetCollatorByID 會(huì)直接回退到二進(jìn)制binarycollator另外new collations 開啟時(shí)引擎還會(huì)把 collation ID 編碼為負(fù)數(shù)下發(fā) TiKVRewriteNewCollationIDIfNeeded讓存儲(chǔ)側(cè)感知自定義排序語義而無需改動(dòng)協(xié)議。上述兩種開關(guān)下的行為都有測(cè)試覆蓋collate_test.go 分別在新排序規(guī)則啟用/關(guān)閉兩組用例中用require.IsType斷言GetCollator(utf8mb4_zh_pinyin_tidb_as_cs)與GetCollatorByID(2048)返回的分別是zhPinyinTiDBASCSCollator啟用時(shí)與derivedBinCollator回退時(shí)。4. 可參照的同思路已完成范本gb18030 中文排序提案中漢字 → gb18030 編碼 → 權(quán)值數(shù)據(jù)文件的技術(shù)路線在倉庫中已有完成度很高的同族實(shí)現(xiàn)可供參照即gb18030字符集的中文排序規(guī)則gb18030_chinese_ci.go 通過//go:embed gb18030_weight.data內(nèi)嵌權(quán)重?cái)?shù)據(jù)文件運(yùn)行期逐字符把 gb18030 序列換算為排序鍵后比較parser 側(cè)對(duì)應(yīng)注冊(cè)了 ID 248/249/250 的gb18030_chinese_ci、gb18030_bin、gb18030_unicode_520_ci見 charset.go。可以推斷未來若完成utf8mb4_zh_pinyin_tidb_as_cs的運(yùn)行時(shí)實(shí)現(xiàn)最自然的路徑就是參照gb18030_*系列預(yù)生成/內(nèi)嵌一份基于 gb18030 碼點(diǎn)與拼音 seq 的權(quán)值數(shù)據(jù)用數(shù)據(jù)驅(qū)動(dòng)替代手寫算法與設(shè)計(jì)文檔中0xFFA00000 seq No.的公式完全吻合。延伸討論替代路線與后續(xù)工作設(shè)計(jì)文檔提到的替代路線是 MySQL 官方utf8mb4_zh_0900_as_cs——它是 MySQL 用于拼音序的語言專屬 collation 之一。TiDB 之所以沒有直接復(fù)刻核心障礙已在前文說明其實(shí)現(xiàn)依賴大量權(quán)重重排與魔法數(shù)值維護(hù)成本與出錯(cuò)風(fēng)險(xiǎn)高。而本提案的取舍是用一個(gè)命名上明確標(biāo)注tidb方言、實(shí)現(xiàn)上依賴 CLDR zh.xml 拼音序號(hào) gb18030 編碼橋接的自研規(guī)則換取實(shí)現(xiàn)簡單與語義正確代價(jià)則是與 MySQL 的不兼容并在跨庫同步時(shí)需要顯式改寫。當(dāng)前倉庫的狀態(tài)表明該 collation 的規(guī)格與注冊(cè)層已經(jīng)定型名字、ID 2048、collator 類型映射、兼容性定位、對(duì)外隱藏策略而運(yùn)行時(shí)比較邏輯仍待實(shí)現(xiàn)。后續(xù)工作可沿著兩個(gè)方向推進(jìn)其一為zhPinyinTiDBASCSCollator填充真實(shí)實(shí)現(xiàn)并配套中文拼音排序的單元/集成測(cè)試其二在實(shí)現(xiàn)完成后放開 GetSupportedCollations 中的過濾邏輯使其對(duì)用戶可見可用。設(shè)計(jì)文檔末尾亦將 issue #19747、#10192 列為開放問題供持續(xù)跟蹤。參考與延伸閱讀設(shè)計(jì)文檔原文docs/design/2020-09-12-utf8mb4-pinyin-order.mdParser 側(cè) collation 注冊(cè)表含 ID 2048pkg/parser/charset/charset.goCollator 實(shí)例注冊(cè)與展示過濾 pkg/util/collate/collate.go、collate.go運(yùn)行時(shí)占位實(shí)現(xiàn) pkg/util/collate/pinyin_tidb_as_cs.go排序規(guī)則兼容性判定與 collator 分發(fā) pkg/util/collate/collate.go行為測(cè)試啟用/關(guān)閉兩種狀態(tài) pkg/util/collate/collate_test.go同思路的 gb18030 中文排序?qū)崿F(xiàn)可作實(shí)現(xiàn)范本 pkg/util/collate/gb18030_chinese_ci.go、charset.go【免費(fèi)下載鏈接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ti/tidb創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考