所謂事務性,一句話概括:一個組操作的各個單元,執行情況要么都成功,要么都執行失敗。
開門見山:
事務的四大特性(ACID):
1.原子性(automicity):一個事物必須看做一個不可分割的最小工作單元,整個事務中的所有操作要么都提交成功,要么全部失敗回滾。對于事務而言,不可能只執行其中一部分,這就是事務的原子性。
2.一致性(consistency):數據庫總是從一個一致性的狀態轉換到另一種一致性的狀態。
3.隔離性(isolation):一個事務所做的修改在最終提交之前,對其他的事務是不可見的。
4.持久性(durability):一旦事務提交,其所做的一切修改必須永久保存在數據庫中,此時即使系統崩潰,修改的數據也不會丟失。
MySQL的是先將所有操作到日志中記錄下來,再去真正操作。而這種機制就是靠事務日志來實現的
而MySQL是有鎖粒度的。
1.鎖的類型被分為了讀鎖和寫鎖。
可以這么想:增刪改就是寫鎖。
查詢才是讀鎖。
2.鎖的粒度也就是鎖的范圍,分為表鎖,頁鎖,行鎖。MySQL支持表級鎖,行鎖則需要存儲引擎的支持
MyISAM支持的是表鎖。
DBD支持的頁面鎖,
InnoDB支持的行鎖。
表鎖:開銷小,加鎖快,不會出現死鎖;鎖粒度大,發生鎖沖突的概率最高,并發度最低。
頁面鎖:開銷和加鎖的時間界于表鎖和行鎖之間,會出現死鎖,并發度一般。
行鎖:開銷大,加鎖慢,會出現死鎖;鎖粒度小,發生鎖沖突的概率最低,并發度最高。
鎖的基本概念:
從數據庫角度來看鎖有3類:
1.獨占鎖:
獨占鎖的鎖粒度是行或者是多行。只允許鎖定操作的程序使用,其他任何對它的操作均不被接收。當對象有其他鎖的時候,無法對其加獨占鎖。
2.共享鎖:
共享鎖的鎖粒度是行或者是多行。當被鎖定后其他程序可以讀,但不能修改它。
3.更新鎖: 更新鎖是為了防止死鎖的。當數據庫要更新數據時,它對數據對象做更新鎖鎖定,這樣數據不能被修改,但可以讀取。等到確定是要更新數據時才會自動將更新鎖換成獨占鎖。
從程序員的角度來看鎖有2類:
1.樂觀鎖: 程序員很高興。程序代碼不需要做任何事,數據庫自己加鎖
2.悲觀鎖:
需要程序員自己手動加鎖處理。
Read Uncommitted:
讀未提交
例子:老板要發工資了。程序員一個月的工資是2.6萬/月。但發工資時老板錯按成了2.9萬。該錢已經打到了程序員的賬戶上,但是事務還沒有提交。這個時候程序員查自己的賬戶發現多了3000元。程序員很高興,以為漲工資了。但這時老板發現了這個操作失誤。馬上就回滾了差點提交了的事務。將數據改成了2.6萬后提交。
分析:
實際程序員的工資還是2.6萬。程序員看到了老板還沒提交的事務。這就是臟讀。
Read Commintted:
讀提交
例子:程序員拿著工資卡去消費(卡里只有2.6萬元),當他要埋單的時候(事務開啟),收費系統檢測卡里有2.6萬元。就在此時。程序員的妻子登錄了程序員的支付寶將所有的錢都轉了出去充當家用,并提交(期間有耗時)。當收費系統準備扣款時,發現卡里沒錢了。程序員就很不理解,卡里明明有錢的啊。
分析:若有事務對數據進行了更新操作時,讀操作事務必須要等待這個更新操作事務提交了才能讀。這個方法雖然解決了臟讀,但是出現了一個事務中查詢出了兩個不同的數據。這就是不可重復讀。
Repeated read:重復讀
例子:還是上面的例子。程序員要消費了,當他埋單時。(事務開啟,并不允許其他事務做更新(UPDATE)操作),收費系統檢測到他的卡里有2.6萬元。這個時候程序員妻子想轉出錢是失敗的。接下來收費系統正常的扣錢。完成交易。
分析:
重復讀可以解決不可重復讀,但是這只解決了update操作。還是有可能出現幻讀的(即插入的Insert操作)。
例子:程序員出去消費,花了2000元。程序員妻子查賬。(全表掃描FTS,妻子事務開啟)看到確實是花了2千元。就在這個時候。程序員花了2萬元買了一臺電腦,瞬間多了一條操作記錄,并立馬提交。當程序員妻子打印程序員賬單時,發現消費成了2.2萬元,感覺好像出了幻覺。這就是幻讀。
Serializable
串行化
這就是最高的隔離性。在該級別下,任何操作必須一個完
MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 180 || Zhang | 20 | 2000 |+-----------+-----+-------+2 rows in set (0.00 sec)MariaDB [CargoWarehouse]> commit;Query OK, 0 rows affected (0.00 sec)成了才能接著做下一個,輕輕松松解決臟讀,不可重復讀,和幻讀。但是你知道的。。。效率奇低!!參考博客:Mysql的事務四個特性以及四個隔離級別
MySQL,Mariadb,Sql Server 一般都是默認Repeatable read。
MariaDB [(none)]> select @@session.tx_isolation;+------------------------+| @@session.tx_isolation |+------------------------+| REPEATABLE-READ |+------------------------+1 row in set (0.00 sec)接下來實際操作一下。首先開啟兩個MySQL,看看第一個事務的操作會不會影響第二個事務的操作。
測試read uncommitted
先修改MySQL的隔離級別:
MariaDB [CargoWarehouse]> set tx_isolation='READ-UNCOMMITTED';Query OK, 0 rows affected (0.00 sec)MariaDB [CargoWarehouse]> select @@tx_isolation;+------------------+| @@tx_isolation |+------------------+| READ-UNCOMMITTED |+------------------+1 row in set (0.00 sec)兩邊都開啟事務:MariaDB [CargoWarehouse]> start transaction;Query OK, 0 rows affected (0.00 sec)老板的在事務中的操作:MariaDB [CargoWarehouse]> insert into employee_list values -> ( -> 'Zhang san',20,29000 -> );Query OK, 1 row affected (0.01 sec)程序員在事務中查看賬戶時發現賬戶上成了29000元:MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 29000 |+-----------+-----+-------+1 row in set (0.00 sec)老板發現錯了,修改列表:MariaDB [CargoWarehouse]> update employee_list set wages=26000 -> where name='Zhang san';Query OK, 1 row affected (0.11 sec)Rows matched: 1 Changed: 1 Warnings: 0老板改完立馬提交:MariaDB [CargoWarehouse]> commit;Query OK, 0 rows affected (0.00 sec)程序員懵逼了,怎么同一個事務里查看出了兩個不同的結果:(所謂的臟讀)MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 29000 |+-----------+-----+-------+1 row in set (0.00 sec)MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 26000 |+-----------+-----+-------+1 row in set (0.00 sec)測試read committed修改隔離級別
(兩邊都需要改)
MariaDB [(none)]> select @@tx_isolation;+----------------+| @@tx_isolation |+----------------+| READ-COMMITTED |+----------------+1 row in set (0.00 sec)MariaDB [(none)]> start transaction;Query OK, 0 rows affected (0.00 sec)程序員要消費,系統檢測卡里的剩余錢數。MariaDB [(none)]> select * from CargoWarehouse.employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 26000 |+-----------+-----+-------+1 row in set (0.00 sec)程序員妻子去轉賬。MariaDB [CargoWarehouse]> update employee_list set wages=200 where name='Zhang san';Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0程序員這里再次檢測。(還是26000,沒毛病)MariaDB [(none)]> select * from CargoWarehouse.employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 26000 |+-----------+-----+-------+1 row in set (0.00 sec)MariaDB [(none)]> select * from CargoWarehouse.employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 26000 |+-----------+-----+-------+1 row in set (0.00 sec)程序員妻子提交申請。MariaDB [CargoWarehouse]> update employee_list set wages=200 where name='Zhang san';Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0MariaDB [CargoWarehouse]> commit;Query OK, 0 rows affected (0.01 sec)程序員再次查看自己的賬戶。MariaDB [(none)]> select * from CargoWarehouse.employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 26000 |+-----------+-----+-------+1 row in set (0.00 sec)MariaDB [(none)]> select * from CargoWarehouse.employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 26000 |+-----------+-----+-------+1 row in set (0.00 sec)MariaDB [(none)]> select * from CargoWarehouse.employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 200 |+-----------+-----+-------+1 row in set (0.00 sec)程序員再次一臉懵逼,明明在一個事務里,竟然查詢結果還是不一樣。太不符合事務的特性了測試repeatable read先修改隔離級別
MariaDB [CargoWarehouse]> set tx_isolation='REPEATABLE-READ';Query OK, 0 rows affected (0.00 sec)MariaDB [CargoWarehouse]> select @@tx_isolation;+-----------------+| @@tx_isolation |+-----------------+| REPEATABLE-READ |+-----------------+1 row in set (0.00 sec)MariaDB [CargoWarehouse]> start transaction;Query OK, 0 rows affected (0.00 sec)程序員的工資卡只剩200元了,這時程序員妻子開始查賬(并開啟事務)。MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 200 |+-----------+-----+-------+1 row in set (0.00 sec)程序員買了一包煙,并立馬提交了事務。MariaDB [CargoWarehouse]> update employee_list set wages=180 where name='Zhang san';Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0MariaDB [CargoWarehouse]> commit;Query OK, 0 rows affected (0.00 sec)程序員妻子查賬還是200,并沒有因為程序員的提交,而改變一個事務中的查詢。(事務的隔離性提高了)MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 200 |+-----------+-----+-------+1 row in set (0.00 sec)MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 200 |+-----------+-----+-------+1 row in set (0.00 sec)看似這樣做很棒了,但是這只解決了update,并沒有解決insert。假設程序員這個月表現的好,發了獎金。(再開個事務做insert操作)
MariaDB [CargoWarehouse]> update employee_list set wages=180 where name='Zhang san';Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0MariaDB [CargoWarehouse]> commit;Query OK, 0 rows affected (0.01 sec)程序員妻子打印工資單MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 200 |+-----------+-----+-------+1 row in set (0.00 sec)MariaDB [CargoWarehouse]> commit;Query OK, 0 rows affected (0.00 sec)MariaDB [CargoWarehouse]> select * from employee_list;+-----------+-----+-------+| name | age | wages |+-----------+-----+-------+| Zhang san | 20 | 180 || Zhang | 20 | 2000 |+-----------+-----+-------+2 rows in set (0.00 sec)串行就不演示了
新聞熱點
疑難解答