03 | 淺談mysql事務
mysql事務有了解多少?
-
在mysql中,事務是在引擎層實現的,目前mysql常用的兩種儲存引擎分別為:myisam和innodb,innodn支援,而myisam是不支援事務的(第二篇文章有說到原因),這也是為什麼現在mysql的預設儲存引擎是innodb
-
提到事務,我們肯定會想到mysql的ACID(Atomicity,Consistency,Isolation,Durability,即原子性,一致性,隔離性,永續性)屬性.
A:原子性指的是同生共死,一個事物中的一組SQL語句要麼一起執行成功,要麼全部執行失敗。例如:事務a中包含6條sql語句,只要其中有一條執行失敗則其他執行成功的語句都會回滾到執行前的狀態
B:一致性指的是事務執行前後資料要保持一致,例如:b卡里有500塊錢,a給b轉1000塊錢,同時b給c轉300,這個時候b賬戶只會是1200而不會是其他的
C:隔離性指的是事務間的可讀性,當多個事務同時進行的時候就可能會出現髒讀,不可重複讀,幻讀等現象,為了解決這些問題,就有了事務隔離的概念,事務隔離分4個級別:讀未提交,讀提交,可重複讀,序列化,隔離級別越高,成本越大,mysql預設隔離級別為可重複讀
a.讀未提交:一個事務還沒有提交時,它做的變更其他事務也可以看到
b.讀提交:一個事務提交之後,它做的變更才能被其它事務看到(可以解決髒讀)
c.可重複讀:一個事務在執行過程中看到的資料,總是跟事務開始的時候一樣,同時它所做的變更在提交之 前對其它事也不可見(可以解決髒讀,不可重複讀)
d.序列化:通過加鎖的方式實現,讀的時候會加上讀鎖,寫的時候則會加上寫鎖,當同時存在多個事物對同一行資料進行讀寫的時候則會出現衝突,這個時候就會排隊一個個執行(可以解決髒讀,不可重複讀,幻讀)
D: 永續性指的是資料的狀態,事物完成後資料永久性儲存
-
總是要記得使用 set autocommit=1,在同一個執行緒中自動提交事務!如果autocommit=0,則不會自動提交事務,如果是長連線則很可能形成長事務。
autocommit的測試,對於innodb表: 首先建立兩個innodb表: mysql> create table tab_kx(a int auto_increment,primary key(a)); Query OK, 0 rows affected (0.16 sec) mysql> create table tab_kx2(a int auto_increment,primary key(a)); Query OK, 0 rows affected (0.17 sec) 在session1,設定autocommit為OFF: mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ 1 row in set (0.00 sec) 在session2設定autocommit為ON: mysql> show variables like '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) 在session1,對tab_kx插入三行資料但不提交: mysql> insert into tab_kx values(''); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into tab_kx values(''); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into tab_kx values(''); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> select * from tab_kx; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec) mysql> select * from tab_kx2; Empty set (0.00 sec) 此時檢視session2是否可以看到這三行資料: mysql> select * from tab_kx; Empty set (0.00 sec) 【說明】,session2看不到其他session沒有提交的DML; 此時在session2插入四行資料到表tab_kx2(由於是auto commit的,所以不需要提交): mysql> insert into tab_kx2 values(); Query OK, 1 row affected (0.07 sec) mysql> insert into tab_kx2 values(); Query OK, 1 row affected (0.04 sec) mysql> insert into tab_kx2 values(); Query OK, 1 row affected (0.04 sec) mysql> insert into tab_kx2 values(); Query OK, 1 row affected (0.02 sec) mysql> select * from tab_kx2; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec) 【注意】但此時session1仍然看不到tab_kx2的資料, mysql> select * from tab_kx2; Empty set (0.00 sec) 當手動執行commit時,才可以看到這些資料,同時session2也可以看到tab_kx表的資料。 mysql> commit; Query OK, 0 rows affected (0.03 sec) mysql> select * from tab_kx2; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec) 引用: http://www.cnblogs.com/chedanlangren/p/8318673.html
-
事務隔離級別中的讀提交和可重複讀是通過 read view實現的
a. 讀提交:每一次讀取都會建立檢視,隔離效果僅限於該條語句
b. 可重複讀:第事務開始後的第一次讀操作建立檢視,在該事務提交之前都使用同一個檢視以確保可重複讀
事務的起始點其實是以執行的第一條語句為起始點的,而不是以begin作為事務的起始點的。
實驗1:
sesseion A
session B
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)
上面的實驗說明:RR隔離級別下的一致性讀,不是以begin開始的時間點作為snapshot建立時間點,而是以第一條select語句的時間點作為snapshot建立的時間點。
實驗2:
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
mysql> begin;
mysql> select * from t;
mysql> insert into t1(c1,c2) values(1,1);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
Empty set (0.00 sec)
該使用說明:RR隔離級別下的一致性讀,是以第一條select語句的執行點作為snapshot建立的時間點的,即使是不同表的select語句。這裡因為session A在insert之前對 t 表執行了select,所以建立了snapshot,所以後面的select * from t1 不能讀取到insert的插入的值。
實驗3:
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
mysql> begin;
mysql> select * from t1;
Empty set (0.00 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1);
mysql> select * from t1;
Empty set (0.01 sec)
該實驗中:session A 的第一條語句,發生在session B的 insert語句提交之前,所以session A中的第二條select還是不能讀取到資料。因為RR中的一致性讀是以事務中第一個select語句執行的時間點作為snapshot建立的時間點的。而此時,session B的insert語句還沒有執行,所以讀取不到資料。
實驗4:
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1),(2,2);
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
| 2 | 2 |
+----+------+
2 rows in set (0.01 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> update t1 set c2=100 where c1=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 100 |
+----+------+
1 row in set (0.00 sec)
該實驗說明:本事務中進行修改的資料,即使沒有提交,在本事務中的後面也可以讀取到。update 語句因為進行的是“當前讀”,所以它可以修改成功。
引用: https://www.cnblogs.com/digdeep/p/4947694.html
- 儘量避免使用長事務,長事務可能會拖垮整個資料庫。假如一個長事務查詢使用了1000條資料,那其他事務只要查詢使用了這1000行資料中的一條,即使這些事務完成提交掉,但伴隨著這些事務產生的涉及到那1000條資料的日誌及檢視快照都會保留下來,直到那個長事務結束後,引擎認為再沒有其他事務使用這些日誌和檢視快照時才會進行清理。