MySQL事務的介紹

1、概念:

a.數據庫事務: 數據庫事務通常指對數據庫進行讀或寫的一個操作序列。

它的存在包含有以下兩個目的:

爲數據庫操作提供了一個從失敗中恢復到正常狀態的方法,同時提供了數據庫即使在異常狀態下仍能保持一致性的方法。

當多個應用程序在併發訪問數據庫時,可以在這些應用程序之間提供一個隔離方法,以防止彼此的操作互相干擾。

b.系統中的事務: 處理一系列業務處理的執行邏輯單元,該單元裏的一系列類操作要不全部成功要不全部失敗

2、爲什麼使用事務

可以保證數據的一致性和完整性(避免異常和錯誤等導致的數據信息異常)

3、事務的特性ACID

原子性(Atomicity):

事務包含的操作全部成功或者全部失敗

一致性(Consistency):

數據庫從一個一致性狀態變到另一個一致性狀態

(一系列操作後,所有的操作和更新全部提交成功,數據庫只包含全部成功後的數據就是數據的一致性)

(由於系統異常或數據庫系統出現故障導致只有部分數據更新成功,但是這不是我們需要的最終數據,這就是數據的不一致)

隔離性(Isolation):

事務互相隔離互不干擾

(事務內部操作的數據對其它事務是隔離的,在一個事務執行完之前不會被其他事務影響和操作)

持久性(Durability):

事務提交後數據應該被永久的保存下來,出現宕機等故障後可以恢復數據

4、數據庫的隔離級別

此處使用https://blog.csdn.net/qq_33290787/article/details/51924963(寫的很好,直接copy下來了)

數據庫事務的隔離級別有4種,由低到高分別爲

Readuncommitted 、
Read committed 、
Repeatable read 、
Serializable
(在事務的併發操作中可能會出現髒讀,不可重複讀,幻讀)

Read uncommitted

(讀未提交,顧名思義,就是一個事務可以讀取另一個未提交事務的數據。)

事例:

老闆要給程序員發工資,程序員的工資是3.6萬/月。
但是發工資時老闆不小心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,
但是事務還沒有提交,就在這時,程序員去查看自己這個月的工資,發現比往常多了3千元,以爲漲工資了非常高興。
但是老闆及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。

分析:

實際程序員這個月的工資還是3.6萬,但是程序員看到的是3.9萬。
他看到的是老闆還沒提交事務時的數據。這就是髒讀。

那怎麼解決髒讀呢?

Readcommitted!讀提交,能解決髒讀問題。

Readcommitted

讀提交,顧名思義,就是一個事務要等另一個事務提交後才能讀取數據。

事例:

程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程序員事務開啓),收費系統事先檢測到他的卡里有3.6萬,
就在這個時候!!程序員的妻子要把錢全部轉出充當家用,並提交。
當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。
程序員就會很鬱悶,明明卡里是有錢的…

分析:

這就是讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交後才能讀取數據,可以解決髒讀問題。
但在這個事例中,出現了一個事務範圍內兩個相同的查詢卻返回了不同數據,這就是不可重複讀。

那怎麼解決可能的不可重複讀問題?

Repeatable read !

Repeatableread

重複讀,就是在開始讀取數據(事務開啓)時,不再允許修改操作

事例:

程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(事務開啓,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡里有3.6萬。
這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。

分析:

重複讀可以解決不可重複讀問題。寫到這裏,應該明白的一點就是,不可重複讀對應的是修改,即UPDATE操作。
但是可能還會有幻讀問題。因爲幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。

什麼時候會出現幻讀?

事例:

程序員某一天去消費,花了2千元,然後他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啓),看到確實是花了2千元,
就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。
當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。

那怎麼解決幻讀問題?

Serializable!

Serializable序列化

Serializable 是最高的事務隔離級別,在該級別下,
事務串行化順序執行,可以避免髒讀、不可重複讀與幻讀。
但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。

值得一提的是:大多數數據庫默認的事務隔離級別是Read committed,比如Sql Server , Oracle。Mysql的默認隔離級別是Repeatable read。

MySQL 的 InnoDB 存儲引擎的鎖類型:

  • 表鎖:鎖粒度大,衝突概率高,併發低,不會出現死鎖,加鎖快,開銷小
  • 行鎖:鎖粒度小,衝突概率低,併發高,會出現死鎖,加鎖慢,開銷大,MyISAM 存儲引擎用不了行鎖

行鎖還可以分爲排它鎖和共享鎖。

  • 排它鎖(Exclusive Locks,簡稱爲 X 鎖或寫鎖):事務 A 對某行數據加寫鎖,只允許事務 A 對數據行進行讀和寫,其他事務無法對數據進行讀或寫操作(也就是不能再對數據加讀鎖或寫鎖),除非事務 A 釋放寫鎖
  • 共享鎖(Share Locks,簡稱爲 S 鎖或讀鎖):事務 A 對數據行加讀鎖,其他事務也只能對數據行加讀鎖,不能加寫鎖,事務 A 釋放讀鎖後,其他事務才能加讀鎖或寫鎖

對於 Update、Delete、Insert 語句,InnoDB 會自動給涉及的數據加排他鎖,行級鎖並不是直接鎖記錄,而是鎖索引,如果一條 SQL 語句用到了主鍵索引,MySQL 會鎖住主鍵索引。
如果一條語句操作了非主鍵索引,MySQL 會先鎖住非主鍵索引,再鎖定主鍵索引。對於普通的 Select 語句,InnoDB 不會加任何鎖,但我們可顯式的給數據加鎖。

SELECT * FROM table_t WHERE 1=1 LOCK IN SHARE MODE; #加共享鎖,WHERE 1=1 什麼也沒做,只是爲了符合sql規範
SELECT * FROM table_t WHERE 1=1 FOR UPDATE;  #加排他鎖

MVCC(Multiversion Concurrency Control),即多版本併發控制技術,結合行鎖實現數據的讀不加鎖,讀寫不衝突,在讀多寫少的場景大幅提升性能,MVCC 主要是爲了解決 Repeatable-Read 事務隔離級別下事務併發產生的幻讀問題。

1. 每行數據都存在一個隱藏的版本字段,每次數據更新時都更新該版本
2. 修改時給數據行加排它鎖,Copy 修改前的數據到 undo log(未做日誌),然後隨意修改數據,同時建立 redo log(重做日誌),各個事務之間無干擾
3. 保存數據時比較版本號,如果相同則提交(commit),則覆蓋原記錄;不同則回滾(rollback),根據 undo log(未做日誌)回滾數據到修改前狀態,如果在提交的過程,系統崩潰了,重啓後通過 redo log(重做日誌)來恢復數據

活鎖和死鎖的預防與診斷

活鎖:

事務 1 封鎖了數據 A,事務 2 又請求封鎖 A,於是事務 2 等待,
事務 3 也請求封鎖 A,當事務 1 釋放了 A 上的封鎖之後系統首先批准了事務 3 的請求,
事務 2 仍然等待,相同的情況出現在事務 4,事務 5…… 事務 2 有可能永遠等待,
這就是活鎖。

避免活鎖:

系統會採用先來先服務的策略,
當多個事務都要對統一數據上鎖,
系統按照請求的先後次序讓事務排隊等待,
一旦數據上的鎖被釋放,
系統首先讓排隊中的第一歌事務獲得鎖,
以此類推。

死鎖:

事務 1 對數據 A 上鎖,事務 2 對數據 B 上鎖,
而事務 1 請求 B 鎖佔用 A 鎖,事務 2 請求 A 鎖佔用 B 鎖,
形成死鎖。

避免死鎖:

事務可以一次性對所有數據上鎖,處理完畢再釋放,
但是併發度很低,而且很難確定哪些數據需要上鎖,
另外一種方式是事務可以按順序給數據依次上鎖,所有事務都按照相同順序上鎖,
這樣避免死鎖更爲合理,不過一旦產生死鎖,
可以通過兩種方式處理死鎖。

超時法:

如果一個事務的等待時間超過了規定的時限,就認爲發生了死鎖。
  • 優點:實現簡單
  • 缺點:有可能誤判死鎖,如果超時等待時間過長,會延遲死鎖的處理

等待圖法:

用事務等待圖動態反映所有事務的等待情況。
事務等待圖是一個有向圖,系統週期性地(如每隔數秒)生成事務等待圖,檢測事務。
如果發現圖中存在迴路,則出現了死鎖。
  • 優點:實時性高,檢測準確率高
  • 缺點:遇到行鎖和表鎖,內部鎖和外部鎖衝突的情況,並不能很好的識別出來
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章