go 迭代string陣列操作 go for string[]
資料庫中事務的重要性,就不多說了!本篇博文主要針對MySQL資料庫介紹事務的重要性!
一、事務概述
事務是程式中一系列嚴密的操作,所有操作執行必須成功完成,否則每個操作所有的更改將會被撤銷,這也是事務的原子性(要麼成功,要麼失敗)。
MySQL的事務是在儲存引擎曾實現的。MySQL的事務有ACID:
- A:原子性(atomicity):一個事務必須被視為一個不可分割的單元;
- C:一致性(consistency):資料庫是從一種狀態切換到另一種狀態;
- I:隔離性(isolation):事務在提交之前,對於其他事務不可見;
- D:永續性(durablity):一旦事務提交,所修改的將永久儲存到資料庫;
二、事務的基本語法
2.1 示例
mysql> create table bank ( -> name varchar(25), -> money float ); #建立表 mysql> insert into bank values('lu','1000'),('qi','5000'); #插入資料 mysql> select * from bank; +------+-------+ | name | money | +------+-------+ | lu | 1000 | | qi | 5000 | +------+-------+ #檢視資料 mysql> begin; #begin開啟事務,start transaction也可開啟事務 mysql> update bank set money=money - 1000 where name='qi'; mysql> update bank set money=money+1000 where name ='lu'; #更新資料 mysql> select * from bank; +------+-------+ | name | money | +------+-------+ | lu | 2000 | | qi | 4000 | +------+-------+ #檢視資料 mysql> rollback; #回滾事務 mysql> select * from bank; +------+-------+ | name | money | +------+-------+ | lu | 1000 | | qi | 5000 | +------+-------+ #再次查詢資料,發現已經便會了原來的值 mysql> commit; #提交事務 mysql> select * from bank; +------+-------+ | name | money | +------+-------+ | lu | 1000 | | qi | 5000 | +------+-------+ #查詢資料
一個事務涉及到的命令:
- 事務的開始:start transaction或begin;
- 事務提交:commit;
- 事務回滾:rollback;
2.2 檢視提交模式並更改
mysql> show variables like 'AUTOCOMMIT'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ #ON表示自動提交 mysql> set AUTOCOMMIT=0; #關閉自動提交,0是關閉,1是開啟 mysql> show variables like 'AUTOCOMMIT'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ #OFF表示已經關閉自動提交
2.3 事務的四種隔離級別
事務在提交之前對其他事務不可見。
- read unaommitted(未提交讀)
- read committed(已提交讀)
- Repeatable read(可重複讀)
- seaializable(可序列化)
2.4 未提交讀
事務中修改沒有提交對其他事務也是可見的,俗稱髒讀。
mysql> create table student (
-> id int not null auto_increment,
-> name varchar(32) not null default '',
-> primary key(id)
-> )engine=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
#建立測試表
接下來需要自行開啟兩個MySQL會話終端,A和B,兩個終端都需執行以下命令:
mysql> set session tx_isolation='read-uncommitted';
#設定為未提交讀
客戶端A
mysql> begin;
mysql> select * from student;
mysql> insert into student(name) values('zhangsan');
#注意,此時事務未提交
客戶端B
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
+----+----------+
#查詢表,即可看到客戶A沒有提交的事務
總結:以上可以看出未提交讀隔離級別非常危險,對於一個沒有提交事務所做修改對另一個事務是可見狀態,出現了髒讀!非特殊情況不建議使用此級別。
2.5 已提交讀
多數資料庫系統預設為此級別(MySQL不是)。已提交讀級別為一個事務只能已提交事務所做的修改,也就是解決了未提交讀的問題。
需執行完成以下命令,再進行測試!
mysql> set session tx_isolation='read-committed';
#設定為已提交讀
客戶端A
mysql> begin;
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
+----+----------+
mysql> insert into student(name) values('lisi');
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | lisi |
+----+----------+
客戶端B
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
+----+----------+
#並沒有檢視到客戶端A剛剛插入的資料
客戶端A
mysql> commit;
客戶端B
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | lisi |
+----+----------+
#客戶端A提交完成後便可檢視到已經更新的資料
總結:從上面可以看出,提交讀沒有了未提交讀的問題,但是我們可以看到客戶端A的一個事務中客戶端B執行了兩次同樣的SELECT語句,得到不同的結果,因此已提交讀又被稱為不可重複讀。同樣的篩選條件可能得到不同的結果。
2.6 可重複讀
可重複讀解決了不可重複讀的問題,資料庫級別沒有解決幻讀的問題。
以下是客戶端A和客戶端B同時操作(都設定為可重複讀,然後兩邊都開啟一個事務):
mysql> set session tx_isolation='repeatable-read';
mysql> begin;
客戶端A
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | lisi |
+----+----------+
mysql> update student set name='wangwu' where id=5;
mysql> commit;
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | wangwu |
+----+----------+
客戶端B
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | lisi |
+----+----------+
mysql> commit;
mysql> select * from student;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | wangwu |
+----+----------+
即可看到客戶端A更新的資料
總結:上面可以看出,可重複讀兩次讀取的內容不一樣。資料庫的幻讀問題並沒有得到解決。幻讀只讀鎖定裡面的資料,不能讀鎖定外的資料,解決幻讀出了mvcc機制Mvcc機制。
2.7 可序列化
是最高隔離級別,強制事務序列執行,執行串行了也就解決問題了,這個只有在對資料一致性要求非常嚴格並且沒有併發的情況下使用。
在客戶端A及客戶端B進行以下操作(設定為可序列讀):
mysql> set session tx_isolation='serializable';
客戶端A
mysql> begin;
mysql> select * from student where id < 5;
+----+----------+
| id | name |
+----+----------+
| 2 | zhangsan |
| 4 | wangwu |
+----+----------+
客戶端B
mysql> insert into student(name) values('maliu');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
#此時進行插入操作時,會一直卡在這裡,然後出現下面的報錯資訊,除非客戶端Acommit提交事務
總結:我們發現INSERT 語句被阻塞執行,原因是A執行了查詢表student同時滿足id<10,已被鎖定。如果查詢表student同時滿足id<5,則新增語句可以正常執行。
以上幾種的隔離界別對比如下:
隔離級別 | 髒讀 | 不可重複 | 幻讀 | 加鎖讀 |
---|---|---|---|---|
未提交讀 | 是 | 是 | 是 | 否 |
提交讀 | 否 | 是 | 是 | 否 |
可重複讀 | 否 | 否 | 是 | 否 |
序列讀 | 否 | 否 | 否 | 是 |