MatrixOne 的測試檔裡有一行 data branch merge t2 into t1;,t1 和 t2 是從同一張表分出去的兩份,各自插過不同的資料。

這行讓我想到一個老問題:上線前想先在資料庫上試一次危險的改動,你現在怎麼試?

多數人的做法大概是這樣:整庫匯出,在另一台機器還原,把改動跑一遍,再拿兩邊的資料一筆一筆對。對完滿意了,同一份改動還得在正式環境重做一次,因為試跑的那份資料沒辦法「合回去」。這是我對一般做法的描述,不是哪份文件寫的,你的流程可能早就比這個好。

MatrixOne 想把最後那個「合回去」變成一行指令。它是 Matrix Origin 做的開源資料庫,授權 Apache-2.0,GitHub 上的專案描述寫 “AI-native HTAP database with Git-for-Data and built-in vector search”。2026 年 10 月 10 日用 GitHub API 查到 2,049 顆星、338 個 fork。這篇只看 Git-for-Data 那一塊,向量搜尋和 HTAP 不談。

先交代手上有什麼。MatrixOne 我沒有安裝,沒跑過任何一條 SQL。下面的 SQL 是從官方文件和 repo 的測試案例整理來的,版本與日期出自 GitHub 的 release 頁,沒有一個數字是我自己量的。只有 Data Branch 那段程式碼,我是直接讀過測試檔原文。

第一代:存檔點,只能回到過去

先從 snapshot 和 PITR 說起。snapshot 就是遊戲的手動存檔:create snapshot sp01 for cluster; 打下一個點,之後可以用 restore account acc01{snapshot="sp01"}; 整個退回去,也可以在查詢裡指定 snapshot,把舊版本的資料讀出來。PITR 是持續錄影的版本,create pitr acc_pitr0 for database test1 range 2 'h'; 的意思是替這個資料庫保留兩小時的歷史,想退到哪個時間點再挑。這兩行都是文件裡的範例寫法。

它們給你的動詞是「還原」和「讀舊版」。出事了可以回去,這很重要,但回去之後,現在這一版就沒了。想拿舊版跟現在並排比,只能自己用查詢把兩邊讀出來對。

第二代:CLONE,分叉出一份可改的副本

2025 年 8 月 26 日的 v3.0.0 加進 CLONE。release 說明的原文有兩句,一句是 “Implement Fast-Shallow-Copy of table data”,另一句是支援新的 CLONE 語法,涵蓋 cluster、tenant、database、table 四個層級。測試案例裡的寫法長這樣:create database copied_db clone src;,單張表是 create table dst.copied clone src.secret;。

Fast-Shallow-Copy 這個詞,我的理解是不用把資料整份搬一遍,所以應該很快。實際怎麼做、快多少,我沒查,也不會替它報一個數字。

有了 CLONE,上面那個「先試一次」的前半段就解了:你拿到一份可以隨便改的副本,正式的那份不受影響。後半段還在。兩份各自長大之後,哪邊多了哪幾列、哪邊改了哪個值,沒有指令告訴你,合回去更別提。這是我讀完前兩代的判斷。

第三代:Data Branch,讓分叉的資料合回來

Data Branch 在 release 說明裡最早出現在 v3.0.5(2026 年 1 月 5 日),寫的是支援 create、diff、stage 這些操作。merge 的字樣,我在 3.0 線最早找到的是 v3.0.9(2026 年 4 月 1 日):修「GC 之後立刻 branch merge 會讀到舊資料」的條目。DATA BRANCH PICK 在 v3.0.10(2026 年 4 月 22 日)。這些都是 3.0 線的正式版 release。

v4.0.0-rc1 是 2026 年 6 月 1 日發的,注意它掛著 prerelease 標記,是 release candidate。它的說明把 Data Branch 列在 4.0 新增項目的第一個,內容是:核心是 branch 的 create、merge、diff 流程,diff 的結果可以匯出成 SQL 或 CSV,另外有 DATA BRANCH PICK,依主鍵把指定的列 cherry-pick 過去。所以 rc1 不是它第一次出現,是它第一次被擺到 4.0 的門面上。

repo 的測試檔 test/distributed/cases/git4data/branch/merge/merge_1.sql 把這個流程寫得很直白。下面是 case 2 的前半段,我拿掉了最後再 diff 一次的那行和收尾的 drop,並加了註解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table t0 (a int, b int, primary key(a));
insert into t0 values (1,1),(2,2),(3,3);

-- 從 t0 分出兩條分支,各自加一筆
data branch create table t1 from t0;
insert into t1 values(4,4);

data branch create table t2 from t0;
insert into t2 values(5,5);

-- 看 t2 比 t1 多了什麼,再把 t2 合進 t1
data branch diff t2 against t1;
data branch merge t2 into t1;
select * from t1 order by a asc;

照語意,合完之後 t1 該多出 (5,5)。這是推論。我沒有看過這個案例的預期輸出檔,更沒有跑過它。

這份測試檔有意思的地方在 case 的標題:case 1 是 “no lca”,case 2 是 “has lca t0”,case 3 是 t1 本身就是 lca。lca 是共同祖先的縮寫,也就是兩條分支當初從哪一版分出去。case 1 連分支關係都沒有,測試檔直接對兩張各自獨立建出來的表跑 diff 和 merge。

程式碼的 merge 做得動,是因為歷史永遠在,三方比對隨時找得到祖先那一版。資料庫的舊版本就沒這麼好命。v4.0.0-rc1 的 release 說明裡有一條 “Branch Protect Snapshot: Protect LCA history from GC during branch operations”。從這行反推,平常資料庫的垃圾回收會把舊版本清掉,分支操作期間得特別把共同祖先那一版保住。(這是我的推論,GC 的細節我沒讀。)

用影印名冊來想。兩個人各影印一份同樣的班級名冊去改,一個加了新同學,一個改了電話。最後要合併,你得手上有當初那份原稿,才知道誰動了什麼、哪些動作互不相干。原稿要是中途被回收了,你只剩兩份各自改過的版本,除了逐列比對,什麼都做不了。這就是為什麼資料的 merge 比程式碼難一截:祖先那一版要有人替你留著,而且得留到你合完為止。

衝突是第二個難處。兩邊改了同一個主鍵,總要有人決定誰贏。repo 的 docs/design/data_branch_pick.md 寫了三種策略:WHEN CONFLICT { FAIL | SKIP | ACCEPT },預設是 FAIL,遇到衝突就中止並回錯誤;SKIP 跳過衝突的列、保留目的端的值;ACCEPT 以來源端的值覆蓋。這份文件講的是 PICK。MERGE 能不能用同一組選項,我沒去翻 parser,不確定。

把日期排一次

能力 本質 版本與日期 成熟度
Snapshot/PITR 還原、讀舊版 版本未查 未查
CLONE 分叉出可改的副本 v3.0.0,2025-08-26 正式版
Data Branch 分叉後 diff、merge、pick 說明最早見於 v3.0.5,2026-01-05;v4.0.0-rc1,2026-06-01 列入 4.0 3.0 線為正式版 release;rc1 為 prerelease

GitHub 的 release 列表裡我找得到 v4.0.0-rc1 到 rc4,沒有 v4.0.0 這個條目,其中 rc4(2026 年 6 月 30 日)在 API 裡沒有標 prerelease,這個我沒追究原因。最新一版是 v4.2.5,2026 年 9 月 28 日發的,離 rc1 119 天;中間的 v4.1.0(2026-07-11)和 v4.2.0(2026-08-21)有沒有動到 Data Branch,那幾份 release 說明我沒讀。所以我講不出 Data Branch 現在穩不穩,只知道它在 3.0 線 1 月就有 release 說明,在 4.0 線第一次出現時掛的是 rc1。

有一件事有一說一得擺出來:issue #28026,標題是 “DATA BRANCH cannot apply primary-key updates with ON UPDATE CASCADE”,2026 年 9 月 2 日開的,10 月 10 日還是 open 狀態,底下 95 則留言。我沒有逐則讀,留言數只能說明有很多人在講它,不能說明怎麼了。不過主鍵更新要怎麼套用,正好撞在 merge 最難的那塊,我會把它當成這功能還沒穩的訊號。

我的立場

如果你的需求是「上線前先在一份真實資料上試一次改動」,先用 snapshot 加 CLONE,Data Branch 先別放進任何會出事的流程。理由有三個。它在 3.0 線 1 月進 release,到 10 月 10 日也才九個月出頭。我手上沒有任何可信的效能或穩定度數字。專案自己的 issue 列表裡,有一個關於主鍵更新的 bug 開在那裡。

rc1 的說明裡,「production-ready」這個詞是用在向量與全文索引上,Data Branch 那一條沒有。你的流程如果本來就需要「分叉之後合回來」,而不是只要一份副本,那才值得為它冒險。

站上 8 月那篇〈六天 vibe code 出來的東西,現在有 26,000 顆星:Beads 把 agent 的待辦清單換成資料庫〉用的資料庫也有分支的概念,是另一個專案。這篇不拿兩邊比較,我沒查過那個專案現在的狀況,下斷言沒有依據。

備份回答的是「壞了怎麼回去」,分支回答的是「我想試的時候,怎麼不碰到別人」。前者只需要一個點,後者要同時養兩個版本、記得它們從哪分開、最後還要有辦法合起來。

要把既有的 MySQL 應用搬過去的話,repo 裡有一份 MySQL 相容例外的文件(docs/cn/mysql-compatibility-exceptions.md),我只確認裡面至少有三節:遞迴 CTE 深度的計算、TIME 與 DATETIME 混用的 TIMEDIFF、零值日期寫入(issue #29207)。內容沒細讀,這份得自己從頭讀一遍。


參考資料