「MySQL高級篇」MySQL鎖機制 && 事務( 三 )


  • 場景:注冊問題吧 , 查詢某個主鍵id是否存在,第一次查詢不存在,即將插入新數據時【剛好另一個人插入了該主鍵id】,導致這邊注冊失敗

「MySQL高級篇」MySQL鎖機制 && 事務

文章插圖
  1. 幻讀在“當前讀”下才會出現 。
  2. 幻讀僅專指“新插入的行”【update的不算】
事務隔離級別為了解決上述提到的事務并發問題,數據庫提供一定的事務隔離機制來解決這個問題 。數據庫的事務隔離越嚴格,并發副作用越??,但竷鳊_拇垡簿馱醬螅?因為事務隔離實質上就是使用事務在一定程度上“串行化” 進行,這顯然與“并發” 是矛盾的 。
數據庫的隔離級別有4個 , 由低到高依次為Read uncommitted、Read committed、Repeatable read、Serializable,這四個級別可以逐個解決臟寫、臟讀、不可重復讀、幻讀這幾類問題 。
隔離級別丟失更新臟讀不可重復讀幻讀Read uncommitted×√√√Read committed××√√Repeatable read(默認)×××√Serializable(串行化)××××
備注 : √ 代表可能出現  ,  × 代表不會出現。
  1. 讀未提交:別人修改了某行數據,還未提交我們就能看到 。
  2. 讀已提交:別人修改了某行數據,得等到提交后我們才能看到 。-- 解決臟讀
  3. 可重復讀:別人修改了某行數據,我們也不去讀那一行數據,還是讀我們當前事務最初的那個未被修改的值 。-- 解決不可重復讀
  4. 串行化:對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖” 。當出現讀寫鎖沖突的時候 , 后訪問的事務必須等前一個事務執行完成,才能繼續執行 。
例子
「MySQL高級篇」MySQL鎖機制 && 事務

文章插圖
  1. 讀未提交:v1=v2=v3=2;B還未提交,A就可以看到了 。
  2. 讀已提交:v1=1,v2=v3=2;等到B提交后,A才能看到 。
  3. 可重復讀:v1=v2=1,v3=2;也就是說,所謂的可重復讀,是說在當前事務提交之前,只會讀取當前事務最初的值 , 而不去讀取其他的事務;
  4. 串行化:v1=1,v2=1,v3=2;事務A中查詢得到值1的時候,就會加了“讀鎖”,會阻塞其他事務對該行的寫操作(上文我們已經有提及到相關的讀鎖和寫鎖,忘記了的小伙伴可以翻閱上文看看)所以在事務B執行“將1改成2”的時候,會被鎖住 。直到事務A提交后 , 事務B才可以繼續執行 。
Mysql 的數據庫的默認隔離級別為 Repeatable read ,  查看方式:
show variables like 'tx_isolation';
「MySQL高級篇」MySQL鎖機制 && 事務

文章插圖
InnoDB 的行鎖模式InnoDB 實現了以下兩種類型的行鎖 。
  • 共享鎖(S):又稱為讀鎖,簡稱S鎖,共享鎖就是多個事務對于同一數據可以共享一把鎖 , 都能訪問到數據,但是只能讀不能修改 。
  • 排他鎖(X):又稱為寫鎖,簡稱X鎖,排他鎖就是不能與其他鎖并存,如一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對數據就行讀取和修改 。
對于UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加**排他鎖**(X);
對于普通SELECT語句,InnoDB不會加任何鎖;
可以通過以下語句顯示給記錄集加共享鎖或排他鎖。
共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE排他鎖(X) :SELECT * FROM table_name WHERE ... FOR UPDATE(悲觀鎖)即手動鎖定一行悲觀鎖和樂觀鎖悲觀鎖:事務必須排隊執行 。數據鎖住了,不允許并發 。(行級鎖:select后面添加for update) 樂觀鎖:支持并發,事務也不需要排隊,只不過需要一個版本號 。
「MySQL高級篇」MySQL鎖機制 && 事務

文章插圖
案例準備工作create table test_innodb_lock( id int(11), name varchar(16), sex varchar(1))engine = innodb default charset=utf8;insert into test_innodb_lock values(1,'100','1');insert into test_innodb_lock values(3,'3','1');insert into test_innodb_lock values(4,'400','0');insert into test_innodb_lock values(5,'500','1');insert into test_innodb_lock values(6,'600','0');insert into test_innodb_lock values(7,'700','0');insert into test_innodb_lock values(8,'800','1');insert into test_innodb_lock values(9,'900','1');insert into test_innodb_lock values(1,'200','0');create index idx_test_innodb_lock_id on test_innodb_lock(id);create index idx_test_innodb_lock_name on test_innodb_lock(name);

推薦閱讀