mysql 行鎖小技巧 索引對行鎖的影響 批量update的時候容易出現死鎖
錯誤:Deadlock found when trying to get
lock; try restarting transaction
做專案時由於業務邏輯的需要,必須對資料表的一行或多行加入行鎖,舉個最簡單的例子,圖書借閱系統。假設 id=1 的這本書庫存為 1 ,但是有 2 個人同時來借這本書,此處的邏輯為
- Select restnum from book where id =1 ;
- -- 如果 restnum 大於 0 ,執行 update
- Update book set restnum=restnum-1 where id=1 ;
問題就來了,當 2 個人同時來借的時候,有可能第一個人執行 select 語句的時候,第二個人插了進來,在第一個人沒來得及更新 book 表的時候,第二個人查到資料了,其實是髒資料,因為第一個人會把 restnum 值減 1 ,因此第二個人本來應該是查到 id=1 的書 restnum 為 0 了,因此不會執行 update ,而會告訴它 id=1 的書沒有庫存 了,可是
- Begin;
- Select restnum from book where id =1 for update ;
- -- 給 id=1 的行加上排它鎖且 id 有索引
- Update book set restnum=restnum-1 where id=1 ;
- Commit;
這樣,第二個人執行到 select 語句的時候就會處於等待狀態直到第一個人執行 commit 。從而保證了第二個人不會讀到第一個人修改前的資料。
那這樣是不是萬無一失了呢,答案是否定的。看下面的例子。
跟我一步一步來,先建立表
- CREATE TABLE `book` (
- `id` int(11) NOT NULL auto_increment,
- `num` int(11) default NULL,
- `name` varchar(0) default NULL,
- PRIMARY KEY (`id`),
- KEY `asd` (`num`)
- ) ENGINE=InnoDB DEFAULT CHARSET=gbk
其中 num 欄位加了索引
然後插入資料,執行,
- insert into book(num) values(11),(11),(11),(11),(11);
- insert into book(num) values(22),(22),(22),(22),(22);
然後開啟 2 個 mysql 控制檯視窗,其實就是建立 2 個 session 做併發操作
********************************************************************
在第一個 session 裡執行:
begin;
select * from book where num=11 for update;
出現結果:
+----+-----+------+
| id | num | name |
+----+-----+------+
| 11 | 11 | NULL |
| 12 | 11 | NULL |
| 13 | 11 | NULL |
| 14 | 11 | NULL |
| 15 | 11 | NULL |
+----+-----+------+
5 rows in set
然後在第二個 session 裡執行:
begin;
select * from book where num=22 for update;
出現結果:
+----+-----+------+
| id | num | name |
+----+-----+------+
| 16 | 22 | NULL |
| 17 | 22 | NULL |
| 18 | 22 | NULL |
| 19 | 22 | NULL |
| 20 | 22 | NULL |
+----+-----+------+
5 rows in set
好了,到這裡什麼問題都沒有,是吧,可是接下來問題就來了,大家請看:
回到第一個 session ,執行:
update book set name='abc' where num=11;
********************************************************************************************
問題來了, session 竟然處於等待狀態 ,可是 num=11 的行不是被第一個 session 自己鎖住的麼,為什麼不能更新呢?好了,打這裡大家也許有自己的答案,先別急,再請看一下操作。
把 2 個 session 都關閉,然後執行:
- delete from book where num=11 limit 3;
- delete from book where num=22 limit 3;
其實就是把 num=11 和 22 的記錄各刪去 3 行,
然後重複 “***********************” 之間的操作
竟然發現,執行 update book set name='abc' where num=11; 後,有結果出現了,說明沒有被鎖住,
這是為什麼呢,難道 2 行資料和 5 行資料,對 MySQL 來說,會產生鎖行和鎖表兩種情況嗎 。經過跟網友討論和翻閱資料,仔細分析後發現:
在以上實驗資料作為測試資料的情況下,由於 num 欄位重複率太高,只有 2 個值,分別是 11 和 12. 而資料量相對於這兩個值來說卻是比較大的,是 10 條, 5 倍的關係。
那麼 mysql 在解釋 sql 的時候,會忽略索引,因為它的優化器發現:即使使用了索引,還是要做全表掃描,故而放棄了索引,也就沒有使用行鎖,卻使用了表鎖。 簡單的講,就是 MYSQL 無視了你的索引,它覺得與其行鎖,還不如直接表鎖,畢竟它覺得表鎖所花的代價比行鎖來的小。以上問題即便你使用了 force index 強制索引,結果還是一樣,永遠都是表鎖。
所以 mysql 的行鎖用起來並不是那麼隨心所欲的,必須要考慮索引。再看下面的例子。
- select id from items where id in (select id from items where id <6) for update;
- --id欄位加了索引
- select id from items where id in (1,2,3,4,5) for update;
大部分會認為結果一樣沒什麼區別,其實差別大了,區別就是第一條 sql 語句會產生表鎖,而第二個 sql 語句是行鎖,為什麼呢?因為第一個 sql 語句用了子查詢外圍查詢故而沒使用索引,導致表鎖。
好了,回到借書的例子,由於 id 是唯一的,所以沒什麼問題,但是如果有些表出現了索引有重複值,並且 mysql 會強制使用表鎖的情況,那怎麼辦呢?一般來說只有重新設計表結構和用新的 SQL 語句實現業務邏輯,但是其實上面借書的例子還有一種辦法。請看下面程式碼:
- Set sql_mode=
- 'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
- Begin;
- Select restnum from book where id =1 ; -- 取消排它鎖 , 設定 restnum 為 unsigned
- Update book set restnum=restnum-1 where id=1 ;
- If(update 執行成功 ) commit;
- Else rollback;
上面是個小技巧,通過把資料庫模式臨時設定為嚴格模式,當 restnum 被更新為 -1 的時候,由於 restnum 是 unsigned 型別的,因此 update 會執行失敗,無論第二個session 做了什麼資料庫操作,都會被回滾,從而確保了資料的正確性,這個目的只是為了防止併發的時候極小概率出現的 2 個 session 的 sql 語句巢狀執行導致資料髒讀。當然最好的辦法還是修改表結構和 sql 語句,讓 MYSQL 通過索引來加行鎖, MySQL 測試版本為 5.0.75-log 和 5.1.36-community
參考連結:
http://blog.csdn.net/aesop_wubo/article/details/8286215
http://blog.csdn.net/fatshaw/article/details/52064668
相關推薦
mysql 行鎖小技巧 索引對行鎖的影響 批量update的時候容易出現死鎖
錯誤:Deadlock found when trying to get lock; try restarting transaction 做專案時由於業務邏輯的需要,必須對資料表的一行或多行加入行鎖,舉個最簡單的例子,圖書借閱系統。假設 id=1 的這本書庫存為 1
小技巧textbox的行數
tchar subst void rom 功能 bstr dex 不知道 getline 沒什麽技術含量,但如果不知道則實現起來很麻煩。 c#中textbox.lines只記錄回車的數量,並不是真正的總行數,如何得到呢,請使用: int 總行數 = this.te
Vim 小技巧-刪除行末空格
在編寫程式碼時,我們時常一不留神就在行末多打了幾個空格。卻又看不出來,即使用 vim-airline 之類的外掛能知道有行末空格(trailing spaces),刪除起來也特別麻煩。本文就介紹一個我就常用的 vim 快捷鍵:刪除行末空格。 功能實現 Vim 並沒有內建的支援,所以我們有正則表示式替換來實現這
用Eclipse 統計程式碼行數小技巧
今天公司SQA問我目前專案程式碼行數有多少,我當時就是想,以前好像寫過類似的統計工具但是一時又找不到 公司網路又不能下載,所以想想eclipse是不是又類似功能,找了下沒有,但突然一想有一個轉彎方法:統計工程裡面的\n個數 1. 按 CTRL+H 開啟查詢對話方塊 選擇fi
告訴你10個MySQL資料庫的小技巧!
無論是運維、開發、測試,還是架構師,資料庫技術是一個必備加薪神器,那麼,一直說學習資料庫、學MySQL,到底是要學習它的哪些東西呢? 1、如何快速掌握MySQL? 培養興趣 興趣是最好的老師,不論學習什麼知識,興趣都可以極大地提高學習效率。當然學習MySQL 5.6也不例外。 夯
mysql的儲存引擎innodb、myisam對插入影響和索引對插入的影響
前言 一直好奇mysql的儲存引擎innodb和myisam對插入影響和索引對插入的影響。 這次我就來做個測試,以下測試供大家參考。 drop table userinfo; CREATE TAB
資料庫新增索引對效能的影響以及使用場景
1.新增索引後查詢速度會變快 mysql中索引是儲存引擎層面用於快速查詢找到記錄的一種資料結構,索引對效能的影響非常重要,特別是表中資料量很大的時候,正確的索引會極大的提高查詢效率。簡單理解索引,就相當於一本磚頭厚書的目錄部分,通過目錄可以快速查詢到想要找的內容具體所在
mysql操作表時出現死鎖解決方式
情景:有時頻繁地某個表時,發現不能進行增刪改操作時,出現操作超時死鎖的情況 --顯示所有程序 show processlist kill id --顯示是否有死鎖 show open tables where In_use > 0; --檢視死鎖 se
面試題:談談對程序的理解?談談你對執行緒的理解?2.程序死鎖的原因?如何解決程序死鎖?
2.談談對程序的理解? 答:首先程序是指在系統中正在執行的一個應用程式;程式一旦執行就是程序,或者更專業化來說:程序是指程式執行時的一個例項,即它是程式已經執行到課中程度的資料結構的彙集。從核心的觀點看,程序的目的就是擔當分配系統資源(CPU時間、記憶體等)的基本單位,程序
關於兩個update語句互相死鎖的顯現,加深我們對鎖的瞭解
前段時間在msdn的論壇上看到鄒老大對一個問題的回覆,覺得對鎖更瞭解了,先二話不說“拿來”記錄學習下。 原帖地址:http://social.msdn.microsoft.com/Forums/zh-CN/6559504d-c546-45a6-89e2-eeb75041b3
發現操作系統的數據庫出現死鎖如何處理
ack cte 進行 username null amp 現在 res bstr where q.address = s.sql_addressand q.hash_value = s.sql_hash_valueand s.paddr = p.addrand exists
[經驗總結]呼叫WinSock的closesocket函數出現死鎖的解決辦法
這兩天除錯一個網路應用程式,出現一個很詭異的問題:程式在關閉連線時失去響應。用Process Explorer工具檢視該程式的各個執行緒,發現一個工作執行緒的呼叫棧類似這樣: stopProc ==> closesocket ==> EnterCri
PL/SQL 出現死鎖解決辦法
在PL/SQL中操作資料表時,長時間沒反應,並且編輯某個表中資料時,出現“record is locked by another user”等情況,即出現了死鎖。 下面,簡述解決辦法: step1.P
Shell腳本編程小技巧(1)-如何解決腳本中多行重定向結束符不用對齊到行首
shell 多行重定向 1、what?問題需求是什麽? 首先需求從何而來呢,主要是編寫shell腳本,用cat 進行多行輸入重定向的時候,結束符必須要對齊行首,格式不好看。 2、how?怎麽解決這個問題? 首先百度,google搜索了下,結果就是說的多的就是shell多行重定向沒有解決實際問題,可能是
總結自己使用shell命令行經常使用到的8個小技巧
技巧 span get ash lan host tab localhost 沒有 原創blog,轉載請註明出處 Shell是命令解釋器 [[email protected]/* */ ~]# cat /etc/shells 查看本系統共支持哪些shel
Sublime 小技巧:文本自動換行顯示?
toggle 文本 也有 word-wrap 實現 mman 喜歡 gin undefined Sublime Text tip for wrap line 題記:雖然現在寫代碼時,一般各種語言的規範都會說寫一行代碼不要超過好多好多字,如PEP8是79個字符,
微信小程序---小技巧 換行省略符
logs pla isp pac nowrap 換行 lamp psi 程序 單行省略: overflow:hidden; text-overflow:ellipsis; white-space:nowrap; 多行省略: text-overflow:ellips
mysql中InnoDB存儲引擎的行鎖和表鎖
nbsp 大於 依然 自帶 打折 一個 系統 指定 任務 Mysql的InnoDB存儲引擎支持事務,默認是行鎖。因為這個特性,所以數據庫支持高並發,但是如果InnoDB更新數據的時候不是行鎖,而是表鎖的話,那麽其並發性會大打折扣,而且也可能導致你的程序出錯。 而導致行鎖變為
將mysql某個數據庫中表的行數從大到小排序
mysql 表的行數 排序 隨著公司的業務越來越大,工作中需要對某一個數據庫的表進行分表,為了做的更細致一點,在該數據庫中,將所有表,按行數從到小排序:實現方式:mysql> use information_schema;Reading table information for compl
How Javascript works (Javascript工作原理) (二) 引擎,運行時,如何在 V8 引擎中書寫最優代碼的 5 條小技巧
tco master 一次 指定 ava 技術分享 將不 創建 跳轉 個人總結: 一個Javascript引擎由一個標準解釋程序,或者即時編譯器來實現。 解釋器(Interpreter): 解釋一行,執行一行。 編譯器(Compiler): 全部編譯成機器碼,統一執行。(減