數據庫事務和鎖

事務的概念

一個數據庫事務一般包含了一個序列的對數據庫的讀/寫操做。它的存在包含有如下兩個目的:html

  1. 爲數據庫操做序列提供了一個從失敗中恢復到正常狀態的方法,同時提供了數據庫即便在異常狀態下仍能保持一致性的方法。
  2. 當多個應用程序在併發訪問數據庫時,能夠在這些應用程序之間提供一個隔離方法,以防止彼此的操做互相干擾。

當事務被提交給了DBMS(數據庫管理系統),則DBMS須要確保該事務中的全部操做都成功完成且其結果被永久保存在數據庫中,若是事務中有的操做沒有成功完成,則事務中的全部操做都須要被回滾,回到事務執行前的狀態;同時,該事務對數據庫或者其餘事務的執行無影響,全部的事務都好像在獨立的運行。mysql

事務的ACID性質

  • 原子性(Atomicity):事務做爲一個總體被執行,包含在其中的對數據庫的操做要麼所有被執行,要麼都不執行。
  • 一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變爲另外一個一致狀態。一致狀態的含義是數據庫中的數據應知足完整性約束。
  • 隔離性(Isolation):多個事務併發執行時,一個事務的執行不該影響其餘事務的執行。
  • 持久性(Durability):已被提交的事務對數據庫的修改應該永久保存在數據庫中。

事務的隔離級別

數據庫中事務有4種隔離級別:sql

隔離級別 髒讀 不可重複讀 幻讀
Read Uncommitted 可能 可能 可能
Read Committed 不能 可能 可能
Repeatable Read 不能 不能 可能
Serializable 不能 不能 不能

數據庫數據以下:數據庫

select * from isolation;
+----+------+
| id | name |
+----+------+
|  1 | 1    |
|  2 | 2    |
|  3 | 3    |
+----+------+
3 rows in set

髒讀

可能讀取到其餘會話中未提交事務修改的數據。併發

----------------------------------------------------------------------------------
事務A                            |                       事務B
----------------------------------------------------------------------------------
begin                            |                       begin
----------------------------------------------------------------------------------
                                 |   update isolation set name = '-1' where id = 1
----------------------------------------------------------------------------------
select name where id = 1         |
----------------------------------------------------------------------------------
commit                           |                       commit
----------------------------------------------------------------------------------

Console:1性能

不可重複讀

在一個事務中,屢次讀取的數據不同。主要指另外一個事務update、delete操做後會影響該事務select.net

----------------------------------------------------------------------------------
事務A                            |                       事務B
----------------------------------------------------------------------------------
begin                            |                       begin
----------------------------------------------------------------------------------
                                 |   
----------------------------------------------------------------------------------
select name where id = 2         |
----------------------------------------------------------------------------------
                                 |   update isolation set name = '-1' where id = 2
----------------------------------------------------------------------------------
                                 |                       commit
----------------------------------------------------------------------------------
select name where id = 2         |      
----------------------------------------------------------------------------------
commit                           |                       
----------------------------------------------------------------------------------

Console:2 Console:-1線程

幻讀

在一個事務中,屢次讀取的數據不同。主要指另外一個事務insert操做後會影響該事務selectcode

----------------------------------------------------------------------------------
事務A                            |                       事務B
----------------------------------------------------------------------------------
begin                            |                       begin
----------------------------------------------------------------------------------
                                 |   
----------------------------------------------------------------------------------
select count(1) where name = '3' |
----------------------------------------------------------------------------------
                                 |      insert into isolation(name) values('3')
----------------------------------------------------------------------------------
                                 |                       commit
----------------------------------------------------------------------------------
select count(1) where name = '3' |     
----------------------------------------------------------------------------------
commit                           |                       
----------------------------------------------------------------------------------

Console:1 Console:2htm

不可重複讀和幻讀的區別

不可重複讀和幻讀的類似之處在於結果上都是在一個事務中屢次查詢結果不一致,其不一樣在於形成其結果的緣由不一樣,不可重複讀由於update、delete操做而幻讀由於insert操做。

能夠從鎖機制實現隔離來看這兩個的區別:可重複讀中,該sql第一次讀取到數據後,就將這些數據加鎖,其它事務沒法修改這些數據,就能夠實現可重複讀了。但這種方法卻沒法鎖住insert的數據,因此當事務A先前讀取了數據,或者修改了所有數據,事務B仍是能夠insert數據提交,這時事務A就會發現莫名其妙多了一條以前沒有的數據,這就是幻讀,不能經過行鎖來避免。

數據庫經過加鎖來實現上訴數據庫事務。Mysql的InnoDB數據庫引擎經過樂觀鎖、悲觀鎖實現事務隔離,實現數據庫的併發控制。

悲觀鎖

百度百科中這樣介紹悲觀鎖,悲觀鎖在數據庫理論中根據鎖的互斥性把鎖分爲共享鎖和排它鎖:

正如其名,它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。

上訴悲觀鎖,咱們能夠看出有點像Java中的Synchronized、ReentranLock的感受,是同步阻塞的。在兩個事務同時進行鎖操做時,後一個事務會被阻塞直至獲取鎖或者超時。

下面使用Mysql的Update操做來看悲觀鎖機制,事務A進行更新操做,可是不提交:

// 設置Mysql不用自動提交
mysql> set autocommit=0;
Query OK, 0 rows affected
// Mysql Update操做會形成加鎖
mysql> update isolation set name = '-1' where id=1;
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0
// 這裏不Commit

事務B同時也進行相同的更新操做,結果會操做該線程阻塞:

mysql> update isolation set name = '-1' where id=1
// 會一直阻塞在這裏直至獲取鎖或者超時

樂觀鎖

百度百科中這樣介紹樂觀鎖:

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖機制採起了更加寬鬆的加鎖機制。悲觀鎖大多數狀況下依靠數據庫的鎖機制實現,以保證操做最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷每每沒法承受。而樂觀鎖機制在必定程度上解決了這個問題。樂觀鎖,大可能是基於數據版本( Version )記錄機制實現。何謂數據版本?即爲數據增長一個版本標識,在基於數據庫表的版本解決方案中,通常是經過爲數據庫表增長一個 「version」 字段來實現。讀取出數據時,將此版本號一同讀出,以後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,若是提交的數據版本號大於數據庫表當前版本號,則予以更新,不然認爲是過時數據。

上訴樂觀鎖,咱們能夠看出來有點像Java中的CAS操做,是同步非阻塞的。

Reference

https://tech.meituan.com/innodb-lock.html
http://chenzhou123520.iteye.com/blog/1860954
https://blog.csdn.net/sadfishsc/article/details/51027734

相關文章
相關標籤/搜索