前言
你有没有过这种经历?
生产环境数据库跑着跑着,一个SQL语句把表结构搞乱了,回滚?对不起,没有备份。想看看上周的数据长什么样?翻日志?可能都找不到。
大多数开发者对代码版本控制驾轻就熟——git commit、git branch、git revert,信手拈来。但对数据库呢?我们通常靠手动导出的 .sql 文件、或者 Flyway/Liquibase 这类迁移工具,处理的是结构变更,不是数据变更。
直到我发现了 Dolt。
Dolt 做的事很简单粗暴:把 Git 的版本控制能力,搬到数据库里来了。你可以对数据 branch、merge、diff、commit、revert——而且它兼容 MySQL 协议,大部分 MySQL 客户端直接就能连。
听起来像科幻小说?我装了之后发现,它确实有真实价值,但也有一些你需要提前知道的坑。
Dolt 到底是什么?
先说结论:Dolt 不是一个"更好的 MySQL"。它是一个带版本控制层的 MySQL 兼容数据库。
它的核心思路是把 Git 的分布式版本控制理念引入数据库领域:
- 每一张表都可以看作一个"仓库"
- 每一次
COMMIT都会保存数据的快照 - 你可以创建分支、合并冲突、回滚到任意历史版本
- 甚至可以做行级别的 diff——看看某一行数据从周一到周五变了什么
技术实现上,Dolt 底层用的是 Go 语言编写,存储引擎基于 LevelDB 的变体。它不依赖传统的 MySQL 文件系统存储,而是自己管理数据持久化——这意味着它可以在任何能跑 Go 的环境上运行,不只是 Linux。
架构拆解:Git 的 branch/merge 在数据库层怎么实现?
这是我最好奇的部分。代码可以 diff,因为它是文本。但数据库存的是二进制或编码后的行数据,怎么做到 git diff 的效果?
数据存储模型
Dolt 把每张表的数据组织成一种不可变的、版本化的行集合。每次 commit,它不会复制整张表,而是用类似 Git 的"对象存储"方式:
- 新增的行 → 新建对象
- 删除的行 → 标记删除
- 修改的行 → 新旧版本共存,commit 指向新版本
这和 Git 的 blob/tree/commit 对象模型异曲同工。区别在于,Dolt 是在行级别做增量,而不是文件级别。
Branch 的实现
在 Git 中,branch 就是一个指向某个 commit 的指针。Dolt 同理——每个 branch 指向该分支上的最新 commit。你可以在不同 branch 上编辑同一张表的不同数据,互不干扰。
Merge 的难点
这才是真正有意思的部分。两个分支合并时,如果它们修改了同一行数据,怎么办?
Dolt 的做法是:冲突检测 + 手动解决。它会标记出冲突的行,让你决定保留哪一边,或者手动合并。这和 git merge 的行为几乎一致——只是冲突粒度从文件变成了行。
我实测中发现,当两个分支都修改了同一行的多个字段时,Dolt 能精确识别哪些字段冲突了,哪些可以安全合并。这个精细程度出乎意料。
安装与上手实测
环境准备
我用的是 Ubuntu 22.04,Dolt 提供了预编译的二进制包,安装非常简单:
# 下载最新 release
wget https://github.com/dolthub/dolt/releases/latest/download/dolt-linux-amd64.tar.gz
tar -xzf dolt-linux-amd64.tar.gz
sudo cp dolt /usr/local/bin/
# 验证安装
dolt version
初始化仓库
和 Git 一样,Dolt 需要先初始化仓库:
mkdir my-dolt-db
cd my-dolt-db
dolt init
# 启动 Dolt Server
dolt sql-server --user root --password '' --port 3306
启动后,任何 MySQL 客户端都能连接——Navicat、DBeaver、命令行 mysql 都行。
第一次 commit
# 创建表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(200),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# 插入数据
INSERT INTO users (name, email) VALUES ('张三', '[email protected]');
INSERT INTO users (name, email) VALUES ('李四', '[email protected]');
# 提交
dolt add .
dolt commit -m "Initial commit: create users table and insert sample data"
注意这里用的是 dolt commit 而不是 COMMIT。Dolt 在标准 SQL 之上扩展了自己的命令体系。
分支操作
# 创建并切换到 feature 分支
dolt branch feature-email-validation
dolt checkout feature-email-validation
# 在分支上添加邮箱验证逻辑
ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE;
# 提交
dolt add .
dolt commit -m "Add email verification column"
回到主分支:
dolt checkout main
# 此时看不到 email_verified 列——因为它只在 feature 分支上
和传统方案的对比
vs Flyway / Liquibase
Flyway 和 Liquibase 是业界最主流的数据库迁移工具,但它们解决的是架构变更(schema migration)问题——创建表、加字段、改索引。它们不管数据。
Dolt 两者都管。你既可以用它管理表结构变更,也可以追踪每一行数据的变动历史。
| 维度 | Flyway/Liquibase | Dolt |
|---|---|---|
| 管理对象 | 表结构(schema) | 表结构 + 行数据 |
| 版本控制 | 迁移脚本 | 数据快照 |
| 回滚能力 | 反向迁移脚本 | 一键 revert 到任意 commit |
| 分支协作 | 不适用 | 支持多分支并行开发 |
| 冲突检测 | 无 | 行级别冲突 |
| 学习曲线 | 低(SQL 基础即可) | 中高(需要理解 Git 概念) |
vs PostgreSQL 时间点恢复(PITR)
PostgreSQL 的 PITR 能让你恢复到某个时间点,但它是一个全局级别的操作——整个数据库回滚到那个时刻。你不能"只回滚这张表的这一行"。
Dolt 的优势在于细粒度:你可以精确到某张表、某个 commit、甚至某个分支来回滚。
vs 手动 .sql 备份
说实话,很多中小团队还在用 mysqldump > backup.sql 的方案。简单粗暴,但有几个致命问题:
- 你不知道 dump 之间具体差了什么(diff 不了)
- 恢复时只能整体恢复,不能局部
- 多个开发者各自备份,容易覆盖
Dolt 解决了这些问题,代价是你需要一个新的思维模式。
踩坑记录
坑1:性能差异明显
Dolt 不是 MySQL 的替代品,它在性能上和原生 MySQL 有明显差距。我在测试环境下跑了一个简单的查询benchmark:
- 原生 MySQL:单表 10 万行数据,简单 SELECT 平均 5ms
- Dolt:同样的查询,平均 45ms
差了将近 9 倍。原因是 Dolt 在每一层都加了版本控制逻辑——读数据时要解析 commit graph,写数据时要计算 diff。
结论:Dolt 适合读不频繁、写可控的场景。如果你指望用它替代线上 MySQL 做高并发服务,趁早打消这个念头。
坑2:dolt commit 不是 COMMIT
这是一个新手最容易踩的坑。在 Dolt 里:
COMMIT—— 提交当前事务(和标准 SQL 一样)dolt commit—— 创建一个版本控制 commit(类似 Git)
很多人以为执行了 COMMIT 就万事大吉了,结果下次 dolt log 一看——什么都没有。数据确实提交了,但没有版本记录。
最佳实践:在 Dolt 里,建议用事务包裹一组操作,然后 dolt commit 一次性提交:
START TRANSACTION;
UPDATE users SET email_verified = TRUE WHERE id = 1;
UPDATE users SET email_verified = TRUE WHERE id = 2;
COMMIT;
dolt commit -m "Verify emails for users 1 and 2";
坑3:Merge 冲突比你想象的频繁
当你同时在两个分支上修改同一张表时,merge 冲突几乎是必然的。特别是当你的业务逻辑涉及跨表关联时——改了 A 表的结构,B 表的 foreign key 约束也跟着报错。
我实测中发现,Dolt 的冲突检测主要针对单表内的行数据,跨表约束的冲突需要你自己处理。这意味着在复杂数据库架构下,merge 的成本会比 Git 高不少。
坑4:备份恢复不是银弹
Dolt 的数据存储在仓库目录下,理论上 cp -r 就是备份。但实际上,Dolt 有自己的备份机制——dolt backup 命令可以把数据推送到远程存储(S3、GCS 等)。
问题是:如果你的仓库目录损坏了,恢复起来比 mysqldump 复杂得多。mysqldump 恢复就是一行命令,Dolt 恢复需要你重建仓库、导入数据、replay commit log。
适用场景判断
经过三天实测,我认为 Dolt 最适合以下场景:
✅ 适合
- 数据审计场景:需要追踪"谁在什么时候改了什么数据",Dolt 天然就是为此设计的
- 多分支数据开发:比如你在一个分支上做数据清洗,另一个分支跑分析查询,互不干扰
- 小规模数据版本管理:几百到几万行的数据集,Dolt 的性能完全可以接受
- 学习/实验环境:教学生版本控制概念时,用 Dolt 比用 Git 直观得多——数据看得见摸得着
❌ 不适合
- 高并发线上服务:性能差距太大,别折腾
- 超大规模数据:百万级以上行的表,Dolt 的 commit/merge 操作会非常慢
- 简单 CRUD 应用:如果你的数据库只需要增删改查,没有版本控制需求,原生 MySQL/PostgreSQL 就够了
结语
Dolt 不是要取代 MySQL。它要做的是给数据库加一层 Git。
这个想法听起来很浪漫——毕竟我们对 Git 的信任已经根深蒂固了。但现实是,数据库和代码不一样。代码是静态的文本,数据是动态的、有状态的、相互关联的。把 Git 的版本控制模型直接套用在数据上,必然会产生摩擦。
我用了三天,最大的感受是:Dolt 的价值不在于"替代传统数据库",而在于"在传统数据库之上增加一层版本控制"。 它解决的是一个特定的痛点——数据变更追踪——而不是所有数据库问题。
如果你正在为一个需要精细数据版本控制的项目发愁,Dolt 值得一试。如果你只是想找个更快的 MySQL,绕道吧。
最后说一句题外话:技术圈总是热衷于"把 A 的概念搬到 B 上"——Git 搬到数据库、Kubernetes 搬到边缘计算、LLM 搬到嵌入式设备。但真正有价值的创新,往往不是简单的概念移植,而是理解本质后重新设计。Dolt 做到了前者,距离后者还有很长的路。
项目地址:https://github.com/dolthub/dolt 官方文档:https://docs.dolt.com/ MySQL 兼容客户端:支持 Navicat、DBeaver、HeidiSQL 等主流工具