先看一段SQL,最後一條SQL的輸出你認為是什麼?
SET AUTOCOMMIT = 1; BEGIN; INSERT INTO t1 VALUES (1); CREATE TABLE t2 (pk int primary key); INSERT INTO t2 VALUES (2); ROLLBACK; SHOW TABLES;
答案是:t1, t2都存在!
mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | t1 | | t2 | +----------------+ 2 rows in set (0.00 sec)
mysql> select * from t1; +----+ | pk | +----+ | 1 | +----+ 1 row in set (0.00 sec)為什麼ROLLBACK沒有生效呢?答案是:Implict Commit
稍微改一下,讓AUTOCOMMIT=0,會怎樣呢?
SET AUTOCOMMIT = 0; BEGIN; INSERT INTO t1 VALUES (1); CREATE TABLE t2 (pk int primary key); INSERT INTO t2 VALUES (2); ROLLBACK; SHOW TABLES;
答案是:t1, t2都存在!插入到t1中的1被提交(插入成功)但插入到t2中得2被回滾(沒有插入成功)。之所以t1中的1被提交,是因為CREATE TABLE導致ImplictCOMMIT(注意,是COMMIT,不是ROLLBACK哦!),執行INSERT的時候,會自動開啟一個新事務(AUTOCOMMIT=0的語義要求的行為)。所以t2中的2被ROLLBACK回滾。
為什麼部分操作會導致Implict Commit?為什麼這樣設計?
為了保證直觀上的原子性。假設不做Implict Commit,看看上面的語句會怎樣:用戶的心理預期是回滾t1的INSERT操作,以及t2的CREATE操作,INSERT操作。如果我們有能力做到這樣,那的確是很完美的。但實際上我們很難做到,特別是在分布式系統中更難!因為CREATE TABLE操作背後涉及到了大量的操作,不僅僅包括對核心表的操作,還包括大量內存數據結構的更新(如Schema),以及存儲系統的變更(如創建相應的數據塊),工程上很難把這些操作做成原子的。
那麼,應該如何做呢?比較折中的方式就是跟用戶做一個約定:CREATE TABLE操作總默認COMMIT它之前的事務,這就是implict commit。
從MySQL文檔看,他們做這一塊的時候遇到了很多問題,至少在這裡踩過兩個坑。並且,隨著版本的進化,他們還不斷的讓更多語句能引發implict commit。