背景介绍
由于历史原因,我们部门的 MySQL 中间件既有 Mycat, 也有 Cobar。 Cobar 号称支持事务, 但是居然不支持 START TRANSACTION
和 BEGIN
显式地开启事务。
单库事务完全支持,分布式事务不能保持强一致性。
分布式事务采用两阶段执行,即分为执行阶段和提交阶段
执行阶段:把前端连接上当前事务所使用到的后端连接绑定下来,并执行SQL语句
提交阶段:将commit命令分发到这些绑定的后端连接中。
在整个事务过程中,执行阶段出错,可以回滚。提交阶段出错不可以回滚。可以说只要是commit之前,执行出现不一致,cobar会自动回滚。
如何兼容
可以通过 SET AUTOCOMMIT=0
来开启事务, 但是事务结束后,马上又开启了新的事务,如果后面的语句不希望起事务,那么一定要在这次事务中提交 SET AUTOCOMMIT=1
,使改动立即生效。
我们用的 mysql 驱动是 go-sql-driver/mysql,为了适配 Cobar,只能改代码了。
connection.go
1 | - err := mc.exec("START TRANSACTION") |
transaction.go
1 | err = tx.mc.exec("COMMIT") |
详细改动见此
改完是能用的。但是后来这个项目迁移到我们的 SOA 框架去了,那个框架的基础库也有一份 go-sql-driver/mysql (没改过的),而且因为框架的设计原因,这个库一定会被引入的
_ "github.com/go-sql-driver/mysql"
问题是,我还得用改过的那份代码呢,并且二者是不能同时引入的。这就蛋疼了。
为何不能同时引入 github.com/go-sql-driver/mysql
和 github.com/yiyulantian/mysql
(就是上面提到改动的那份fork) 呢? 其实它俩对于 database/sql 而言,是同一个数据库驱动。
go-sql-driver/mysql
是这样注册进去的
1 | func init() { |
而 sql.Register
做了名称的唯一性检查:
1 | // Register makes a database driver available by the provided name. |
对于这种 panic, 总不能为了绕过去而做一次 recover 吧。
于是我决定曲线救国, 改个名字。
1 | func init() { |
在使用上,创建一个 DB 对象就是这样啦:
1 | db, err = sql.Open("mysql_cobar", "YOUR_DSN") |
希望 DBA 快点废掉 cobar !
Cobar 和 Mycat 对 BEGIN 和 START TrANSACTION 的代码实现
先看下 Cobar(不贴代码了,自己点链接去看)
再看下 Mycat
代码链接都是截止至 2017.01.19 最新的commit。