本文目錄一覽:
- 1、mysql的事務四個特性以及事務的四個隔離級別
- 2、MySQL的默認事務隔離級別是?
- 3、怎麼查看資料庫隔離級別
- 4、如何查看mysql資料庫隔離級別
- 5、MySQL 事務的默認隔離級別是什麼?可以解決幻讀問題么?
mysql的事務四個特性以及事務的四個隔離級別
分別是原子性、一致性、隔離性、持久性。
原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾,因此事務的操作如果成功就必須要完全應用到資料庫,如果操作失敗則不能對資料庫有任何影響。
一致性是指事務必須使資料庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。舉例來說,假設用戶A和用戶B兩者的錢加起來一共是1000,那麼不管A和B之間如何轉賬、轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是1000,這就是事務的一致性。
隔離性是當多個用戶並發訪問資料庫時,比如同時操作同一張表時,資料庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。關於事務的隔離性資料庫提供了多種隔離級別,稍後會介紹到。
持久性是指一個事務一旦被提交了,那麼對資料庫中的數據的改變就是永久性的,即便是在資料庫系統遇到故障的情況下也不會丟失提交事務的操作。例如我們在使用JDBC操作資料庫時,在提交事務方法後,提示用戶事務操作完成,當我們程序執行完成直到看到提示後,就可以認定事務已經正確提交,即使這時候資料庫出現了問題,也必須要將我們的事務完全執行完成。否則的話就會造成我們雖然看到提示事務處理完畢,但是資料庫因為故障而沒有執行事務的重大錯誤。這是不允許的。
在資料庫操作中,在並發的情況下可能出現如下問題:
正是為了解決以上情況,資料庫提供了幾種隔離級別。
資料庫事務的隔離級別有4個,由低到高依次為Read uncommitted(未授權讀取、讀未提交)、Read committed(授權讀取、讀提交)、Repeatable read(可重複讀取)、Serializable(序列化),這四個級別可以逐個解決臟讀、不可重複讀、幻象讀這幾類問題。
雖然資料庫的隔離級別可以解決大多數問題,但是靈活度較差,為此又提出了悲觀鎖和樂觀鎖的概念。
悲觀鎖,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度。因此,在整個數據處理過程中,將數據處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制。也只有資料庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統的數據訪問層中實現了加鎖機制,也無法保證外部系統不會修改數據。
商品t_items表中有一個欄位status,status為1代表商品未被下單,status為2代表商品已經被下單(此時該商品無法再次下單),那麼我們對某個商品下單時必須確保該商品status為1。假設商品的id為1。
如果不採用鎖,那麼操作方法如下:
但是上面這種場景在高並發訪問的情況下很可能會出現問題。例如當第一步操作中,查詢出來的商品status為1。但是當我們執行第三步Update操作的時候,有可能出現其他人先一步對商品下單把t_items中的status修改為2了,但是我們並不知道數據已經被修改了,這樣就可能造成同一個商品被下單2次,使得數據不一致。所以說這種方式是不安全的。
在上面的場景中,商品信息從查詢出來到修改,中間有一個處理訂單的過程,使用悲觀鎖的原理就是,當我們在查詢出t_items信息後就把當前的數據鎖定,直到我們修改完畢後再解鎖。那麼在這個過程中,因為t_items被鎖定了,就不會出現有第三者來對其進行修改了。需要注意的是,要使用悲觀鎖,我們必須關閉mysql資料庫的自動提交屬性,因為MySQL默認使用autocommit模式,也就是說,當你執行一個更新操作後,MySQL會立刻將結果進行提交。我們可以使用命令設置MySQL為非autocommit模式: set autocommit=0;
設置完autocommit後,我們就可以執行我們的正常業務了。具體如下:
上面的begin/commit為事務的開始和結束,因為在前一步我們關閉了mysql的autocommit,所以需要手動控制事務的提交。
上面的第一步我們執行了一次查詢操作: select status from t_items where id=1 for update; 與普通查詢不一樣的是,我們使用了 select…for update 的方式,這樣就通過資料庫實現了悲觀鎖。此時在t_items表中,id為1的那條數據就被我們鎖定了,其它的事務必須等本次事務提交之後才能執行。這樣我們可以保證當前的數據不會被其它事務修改。需要注意的是,在事務中,只有 SELECT … FOR UPDATE 或 LOCK IN SHARE MODE 操作同一個數據時才會等待其它事務結束後才執行,一般 SELECT … 則不受此影響。拿上面的實例來說,當我執行 select status from t_items where id=1 for update; 後。我在另外的事務中如果再次執行 select status from t_items where id=1 for update; 則第二個事務會一直等待第一個事務的提交,此時第二個查詢處於阻塞的狀態,但是如果我是在第二個事務中執行 select status from t_items where id=1; 則能正常查詢出數據,不會受第一個事務的影響。
使用 select…for update 會把數據給鎖住,不過我們需要注意一些鎖的級別,MySQL InnoDB默認Row-Level Lock,所以只有「明確」地指定主鍵或者索引,MySQL 才會執行Row lock (只鎖住被選取的數據) ,否則MySQL 將會執行Table Lock (將整個數據表單給鎖住)。舉例如下:
1、 select * from t_items where id=1 for update;
這條語句明確指定主鍵(id=1),並且有此數據(id=1的數據存在),則採用row lock。只鎖定當前這條數據。
2、 select * from t_items where id=3 for update;
這條語句明確指定主鍵,但是卻查無此數據,此時不會產生lock(沒有元數據,又去lock誰呢?)。
3、 select * from t_items where name=’手機’ for update;
這條語句沒有指定數據的主鍵,那麼此時產生table lock,即在當前事務提交前整張數據表的所有欄位將無法被查詢。
4、 select * from t_items where id0 for update; 或者 select * from t_items where id1 for update; (註:在SQL中表示不等於)
上述兩條語句的主鍵都不明確,也會產生table lock。
5、 select * from t_items where status=1 for update; (假設為status欄位添加了索引)
這條語句明確指定了索引,並且有此數據,則產生row lock。
6、 select * from t_items where status=3 for update; (假設為status欄位添加了索引)
這條語句明確指定索引,但是根據索引查無此數據,也就不會產生lock。
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為數據一般情況下不會造成衝突,所以只會在數據進行提交更新的時候,才會正式對數據的衝突與否進行檢測,如果發現衝突了,則返回用戶錯誤的信息,讓用戶決定如何去做。實現樂觀鎖一般來說有以下2種方式:
MySQL的默認事務隔離級別是?
mysql的4種事務隔離級別,如下所示:
1、未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據。
2、提交讀(Read Committed):只能讀取到已經提交的數據。Oracle等多數資料庫默認都是該級別 (不重複讀)。
3、可重複讀(Repeated Read):可重複讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重複讀,但是還存在幻象讀,但是innoDB解決了幻讀。
4、串列讀(Serializable):完全串列化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞。
相關簡介
MySQL是一個關係型資料庫管理系統,由瑞典MySQL AB 公司開發,屬於 Oracle 旗下產品。MySQL 是最流行的關係型資料庫管理系統之一,在 WEB 應用方面,MySQL是最好的 RDBMS (Relational Database Management System,關係資料庫管理系統) 應用軟體之一。
MySQL是一種關係型資料庫管理系統,關係資料庫將數據保存在不同的表中,而不是將所有數據放在一個大倉庫內,這樣就增加了速度並提高了靈活性。
MySQL所使用的 SQL 語言是用於訪問資料庫的最常用標準化語言。MySQL 軟體採用了雙授權政策,分為社區版和商業版,由於其體積小、速度快、總體擁有成本低,尤其是開放源碼這一特點,一般中小型網站的開發都選擇 MySQL 作為網站資料庫。
怎麼查看資料庫隔離級別
修改方法
有兩種方法可以對配置了 systemd 的程序進行資源隔離:1. 命令行修改:通過執行 systemctl set-property 命令實現,形式為 systemctl set-property name parameter=value;修改默認即時生效。2. 手工修改文件:直接編輯程序的 systemd unit file 文件,完成之後需手工執行 systemctl daemon-reload 更新配置,並重啟服務 systemctl restart name.service。
systemd unit file 里支持的資源隔離配置項,如常見的:
CPUQuota=value
該參數表示服務可以獲取的最大 CPU 時間,value 為百分數形式,高於 100% 表示可使用 1 核以上的 CPU。與 cgroup cpu 控制器 cpu.cfs_quota_us 配置項對應。
MemoryLimit=value
該參數表示服務可以使用的最大內存量,value 可以使用 K, M, G, T 等後綴表示值的大小。與 cgroup memory 控制器 memory.limit_in_bytes 配置項對應。
事務的4種隔離級別
READ UNCOMMITTED 未提交讀,可以讀取未提交的數據。
READ COMMITTED 已提交讀,對於鎖定讀(select with for update 或者 for share)、update 和 delete 語句,InnoDB 僅鎖定索引記錄,而不鎖定它們之間的間隙,因此允許在鎖定的記錄旁邊自由插入新記錄。
Gap locking 僅用於外鍵約束檢查和重複鍵檢查。
REPEATABLE READ 可重複讀,事務中的一致性讀取讀取的是事務第一次讀取所建立的快照。
SERIALIZABLE 序列化在了解了 4 種隔離級別的需求後,在採用鎖控制隔離級別的基礎上,我們需要了解加鎖的對象(數據本身間隙),以及了解整個數據範圍的全集組成。
數據範圍全集組成
SQL 語句根據條件判斷不需要掃描的數據範圍(不加鎖);
SQL 語句根據條件掃描到的可能需要加鎖的數據範圍;
以單個數據範圍為例,數據範圍全集包含:(數據範圍不一定是連續的值,也可能是間隔的值組成)
如何查看mysql資料庫隔離級別
術式之後皆為邏輯,一切皆為需求和實現。希望此文能從需求、現狀和解決方式的角度幫大家理解隔離級別。
隔離級別的產生
在串型執行的條件下,數據修改的順序是固定的、可預期的結果,但是並發執行的情況下,數據的修改是不可預期的,也不固定,為了實現數據修改在並發執行的情況下得到一個固定、可預期的結果,由此產生了隔離級別。
所以隔離級別的作用是用來平衡資料庫並發訪問與數據一致性的方法。
事務的4種隔離級別
READ UNCOMMITTED 未提交讀,可以讀取未提交的數據。READ COMMITTED 已提交讀,對於鎖定讀(select with for update 或者 for share)、update 和 delete 語句, InnoDB 僅鎖定索引記錄,而不鎖定它們之間的間隙,因此允許在鎖定的記錄旁邊自由插入新記錄。 Gap locking 僅用於外鍵約束檢查和重複鍵檢查。REPEATABLE READ 可重複讀,事務中的一致性讀取讀取的是事務第一次讀取所建立的快照。SERIALIZABLE 序列化
在了解了 4 種隔離級別的需求後,在採用鎖控制隔離級別的基礎上,我們需要了解加鎖的對象(數據本身間隙),以及了解整個數據範圍的全集組成。
數據範圍全集組成
SQL 語句根據條件判斷不需要掃描的數據範圍(不加鎖);
SQL 語句根據條件掃描到的可能需要加鎖的數據範圍;
以單個數據範圍為例,數據範圍全集包含:(數據範圍不一定是連續的值,也可能是間隔的值組成)
1. 數據已經填充了整個數據範圍:(被完全填充的數據範圍,不存在數據間隙)
整形,對值具有唯一約束條件的數據範圍 1~5 ,
已有數據1、2、3、4、5,此時數據範圍已被完全填充;
整形,對值具有唯一約束條件的數據範圍 1 和 5 ,
已有數據1、5,此時數據範圍已被完全填充;
2. 數據填充了部分數據範圍:(未被完全填充的數據範圍,是存在數據間隙)
整形的數據範圍 1~5 ,
已有數據 1、2、3、4、5,但是因為沒有唯一約束,
所以數據範圍可以繼續被 1~5 的數據重複填充;
整形,具有唯一約束條件的數據範圍 1~5 ,
已有數據 2,5,此時數據範圍未被完全填充,還可以填充 1、3、4 ;
3. 數據範圍內沒有任何數據(存在間隙)
如下:
整形的數據範圍 1~5 ,數據範圍內當前沒有任何數據。
在了解了數據全集的組成後,我們再來看看事務並發時,會帶來的問題。
無控制的並發所帶來的問題
並發事務如果不加以控制的話會帶來一些問題,主要包括以下幾種情況。
1. 範圍內已有數據更改導致的:
更新丟失:當多個事務選擇了同一行,然後基於最初選定的值更新該行時,
由於每個事物不知道其他事務的存在,最後的更新就會覆蓋其他事務所做的更新;
臟讀: 一個事務正在對一條記錄做修改,這個事務完成並提交前,這條記錄就處於不一致狀態。
這時,另外一個事務也來讀取同一條記錄,如果不加控制,
第二個事務讀取了這些「臟」數據,並據此做了進一步的處理,就會產生提交的數據依賴關係。
這種現象就叫「臟讀」。
2. 範圍內數據量發生了變化導致:
不可重複讀:一個事務在讀取某些數據後的某個時間,再次讀取以前讀過的數據,
卻發現其讀出的數據已經發生了改變,或者某些記錄已經被刪除了。
這種現象就叫「不可重複讀」。
幻讀:一個事務按相同的查詢條件重新讀取以前檢索過的數據,
卻發現其他事務插入了滿足其查詢條件的新數據,這種現象稱為「幻讀」。
可以簡單的認為滿足條件的數據量變化了。
因為無控制的並發會帶來一系列的問題,這些問題會導致無法滿足我們所需要的結果。因此我們需要控制並發,以實現我們所期望的結果(隔離級別)。
MySQL 隔離級別的實現
InnoDB 通過加鎖的策略來支持這些隔離級別。
行鎖包含:
Record Locks
索引記錄鎖,索引記錄鎖始終鎖定索引記錄,即使表中未定義索引,
這種情況下,InnoDB 創建一個隱藏的聚簇索引,並使用該索引進行記錄鎖定。
Gap Locks
間隙鎖是索引記錄之間的間隙上的鎖,或者對第一條記錄之前或者最後一條記錄之後的鎖。
間隙鎖是性能和並發之間權衡的一部分。
對於無間隙的數據範圍不需要間隙鎖,因為沒有間隙。
Next-Key Locks
索引記錄上的記錄鎖和索引記錄之前的 gap lock 的組合。
假設索引包含 10、11、13 和 20。
可能的next-key locks包括以下間隔,其中圓括弧表示不包含間隔端點,方括弧表示包含端點:
(負無窮大, 10] (10, 11] (11, 13] (13, 20] (20, 正無窮大) 對於最後一個間隔,next-key將會鎖定索引中最大值的上方,
左右滑動進行查看
“上確界”偽記錄的值高於索引中任何實際值。
上確界不是一個真正的索引記錄,因此,實際上,這個 next-key 只鎖定最大索引值之後的間隙。
基於此,當獲取的數據範圍中,數據已填充了所有的數據範圍,那麼此時是不存在間隙的,也就不需要 gap lock。
對於數據範圍內存在間隙的,需要根據隔離級別確認是否對間隙加鎖。
默認的 REPEATABLE READ 隔離級別,為了保證可重複讀,除了對數據本身加鎖以外,還需要對數據間隙加鎖。
READ COMMITTED 已提交讀,不匹配行的記錄鎖在 MySQL 評估了 where 條件後釋放。
對於 update 語句,InnoDB 執行 “semi-consistent” 讀取,這樣它會將最新提交的版本返回到 MySQL,
以便 MySQL 可以確定該行是否與 update 的 where 條件相匹配。
總結延展:
唯一索引存在唯一約束,所以變更後的數據若違反了唯一約束的原則,則會失敗。
當 where 條件使用二級索引篩選數據時,會對二級索引命中的條目和對應的聚簇索引都加鎖;所以其他事務變更命中加鎖的聚簇索引時,都會等待鎖。
行鎖的增加是一行一行增加的,所以可能導致並發情況下死鎖的發生。
例如,
在 session A 對符合條件的某聚簇索引加鎖時,可能 session B 已持有該聚簇索引的 Record Locks,而 session B 正在等待 session A 已持有的某聚簇索引的 Record Locks。
session A 和 session B 是通過兩個不相干的二級索引定位到的聚簇索引。
session A 通過索引 idA,session B通過索引 idB 。
當 where 條件獲取的數據無間隙時,無論隔離級別為 rc 或 rr,都不會存在間隙鎖。
比如通過唯一索引獲取到了已完全填充的數據範圍,此時不需要間隙鎖。
間隙鎖的目的在於阻止數據插入間隙,所以無論是通過 insert 或 update 變更導致的間隙內數據的存在,都會被阻止。
rc 隔離級別模式下,查詢和索引掃描將禁用 gap locking,此時 gap locking 僅用於外鍵約束檢查和重複鍵檢查(主要是唯一性檢查)。
rr 模式下,為了防止幻讀,會加上 Gap Locks。
事務中,SQL 開始則加鎖,事務結束才釋放鎖。
就鎖類型而言,應該有優化鎖,鎖升級等,例如rr模式未使用索引查詢的情況下,是否可以直接升級為表鎖。
就鎖的應用場景而言,在回放場景中,如果確定事務可並發,則可以考慮不加鎖,加快回放速度。
鎖只是並發控制的一種粒度,只是一個很小的部分:
從不同場景下是否需要控制並發,(已知無交集且有序的數據的變更,MySQL 的 MTS 相同前置事務的多事務並發回放)
並發控制的粒度,(鎖是一種邏輯粒度,可能還存在物理層和其他邏輯粒度或方式)
相同粒度下的優化,(鎖本身存在優化,如IX、IS類型的優化鎖)
粒度載入的安全性能(如獲取行鎖前,先獲取頁鎖,頁鎖在執行獲取行鎖操作後即釋放,無論是否獲取成功)等多個層次去思考並發這玩意。
MySQL 事務的默認隔離級別是什麼?可以解決幻讀問題么?
我們設想一個場景,這個場景中我們需要插入多條相關聯的數據到資料庫,不幸的是,這個過程可能會遇到下面這些問題:
上面的任何一個問題都可能會導致數據的不一致性。為了保證數據的一致性,系統必須能夠處理這些問題。事務就是我們抽象出來簡化這些問題的首選機制。事務的概念起源於資料庫,目前,已經成為一個比較廣泛的概念。
何為事務? 一言蔽之, 事務是邏輯上的一組操作,要麼都執行,要麼都不執行。
事務最經典也經常被拿出來說例子就是轉賬了。假如小明要給小紅轉賬 1000 元,這個轉賬會涉及到兩個關鍵操作,這兩個操作必須都成功或者都失敗。
事務會把這兩個操作就可以看成邏輯上的一個整體,這個整體包含的操作要麼都成功,要麼都要失敗。這樣就不會出現小明餘額減少而小紅的餘額卻並沒有增加的情況。
大多數情況下,我們在談論事務的時候,如果沒有特指 分散式事務 ,往往指的就是 資料庫事務 。
資料庫事務在我們日常開發中接觸的最多了。如果你的項目屬於單體架構的話,你接觸到的往往就是資料庫事務了。
那資料庫事務有什麼作用呢?
簡單來說,資料庫事務可以保證多個對資料庫的操作(也就是 SQL 語句)構成一個邏輯上的整體。構成這個邏輯上的整體的這些資料庫操作遵循: 要麼全部執行成功,要麼全部不執行 。
另外,關係型資料庫(例如: MySQL 、 SQL Server 、 Oracle 等)事務都有 ACID 特性:
ACID
這裡要額外補充一點: 只有保證了事務的持久性、原子性、隔離性之後,一致性才能得到保障。也就是說 A、I、D 是手段,C 是目的!
在典型的應用程序中,多個事務並發運行,經常會操作相同的數據來完成各自的任務(多個用戶對同一數據進行操作)。並發雖然是必須的,但可能會導致以下的問題。
不可重複讀和幻讀區別 :不可重複讀的重點是修改比如多次讀取一條記錄發現其中某些列的值被修改,幻讀的重點在於新增或者刪除比如多次查詢同一條查詢語句(DQL)時,記錄發現記錄增多或減少了。
SQL 標準定義了四個隔離級別:
隔離級別臟讀不可重複讀幻讀 READ-UNCOMMITTED READ-COMMITTED REPEATABLE-READ SERIALIZABLE
MySQL 的隔離級別基於鎖和 MVCC 機制共同實現的。
SERIALIZABLE 隔離級別,是通過鎖來實現的。除了 SERIALIZABLE 隔離級別,其他的隔離級別都是基於 MVCC 實現。
不過, SERIALIZABLE 之外的其他隔離級別可能也需要用到鎖機制,就比如 REPEATABLE-READ 在當前讀情況下需要使用加鎖讀來保證不會出現幻讀。
MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀) 。我們可以通過 SELECT @@tx_isolation; 命令來查看,MySQL 8.0 該命令改為 SELECT @@transaction_isolation;
從上面對 SQL 標準定義了四個隔離級別的介紹可以看出,標準的 SQL 隔離級別定義里,REPEATABLE-READ(可重複讀)是不可以防止幻讀的。
但是!InnoDB 實現的 REPEATABLE-READ 隔離級別其實是可以解決幻讀問題發生的,主要有下面兩種情況:
因為隔離級別越低,事務請求的鎖越少,所以大部分資料庫系統的隔離級別都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存儲引擎默認使用 REPEATABLE-READ 並不會有任何性能損失。
InnoDB 存儲引擎在分散式事務的情況下一般會用到 SERIALIZABLE 隔離級別。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/243276.html