這段時間處理了兩個比較有意思的mysql問題,乙個死鎖的,乙個優化的,陡然發現其實自己對mysql的理解還不深入,很多執行機制也是知其然但不知其所以然,後續還需要好好惡補一下底層知識。
假設有如下表結構:
mysql> show create table tt \g;
*************************** 1. row *************************** table: tt
create table: create table `tt` (
`id` int(11) not null default '0',
`fileid` int(11) default null,
primary key (`id`),
unique key `fileid` (`fileid`)
) engine=innodb default charset=utf8
1 row in set (0.00 sec)
啟動三個shell,連線mysql,然後begin
開啟乙個事務,各個shell分別執行對應的更新語句,
shell 1:
shell 1> update tt set id = 2 where fileid = 1;
shell 2:
shell 2> update tt set id = 3 where fileid = 1;
shell 3:
shell 3> update tt set id = 4 where fileid = 1;
假設shell 1先執行,這時候2和3會block,然後shell 1 commit提交,我們發現shell 2執行成功,但是3出現死鎖錯誤,通過show engine innodb status
我們得到如下死鎖資訊:
------------------------
latest detected deadlock
------------------------
2015-01-23 14:24:16 10ceed000
*** (1) transaction:
transaction 24897, active 3 sec starting index read
mysql tables in use1, locked1lockwait2lockstruct(s), heapsize360, 1rowlock(s)
mysql threadid8, os thread handle 0x10cea5000, queryid138127.0.0.1 root updating
update tt setid = 4where fileid = 1
*** (1) waiting for this lockto be granted:
record locks spaceid495 page no4n bits 72index`fileid`oftable`test`.`tt` trx id24897 lock_mode x locks rec but not gap waiting
recordlock, heapno2physicalrecord: n_fields 2; compact format; info bits 32
0: len4; hex 80000001; asc ;;
1: len 4; hex 80000002; asc ;;
*** (2) transaction:
transaction 24896, active 8 sec updating or deleting
mysql tables in use1, locked14lockstruct(s), heapsize1184, 3rowlock(s), undolog entries 2
mysql threadid7, os thread handle 0x10ceed000, queryid136127.0.0.1 root updating
update tt setid = 3where fileid = 1
*** (2) holds the lock(s):
record locks spaceid495 page no4n bits 72index`fileid`oftable`test`.`tt` trx id24896 lock_mode x locks rec but not gap
recordlock, heapno2physicalrecord: n_fields 2; compact format; info bits 32
0: len4; hex 80000001; asc ;;
1: len 4; hex 80000002; asc ;;
*** (2) waiting for this lockto be granted:
record locks spaceid495 page no4n bits 72index`fileid`oftable`test`.`tt` trx id24896lockmode s waiting
recordlock, heapno2physicalrecord: n_fields 2; compact format; info bits 32
0: len4; hex 80000001; asc ;;
1: len 4; hex 80000002; asc ;;
*** we roll back transaction (1)
------------
剛開始碰到這個死鎖問題,真心覺得很奇怪,每個事務一條語句,通過乙個唯一索引去更新同一條記錄,正常來說完全不可能發生死鎖,但確確實實發生了。筆者百思不得其解,幸好有google,然後搜到了這篇,乙個最不可思議的mysql死鎖分析,雖然觸發情況不一樣,但是死鎖原理都應該類似的,後續如果有精力,筆者將好好深入研究一下。
順帶再說一下,mysql 加鎖處理分析這篇文章也是幹活滿滿,這兩篇加起來深入理解了,對mysql的deadlock就會有乙個很全面的認識了。
我們需要在一張表裡面刪除某種型別的資料,大概的表結構類似這樣:
createtablet (
idint,
tp enum ("t1", "t2"),
primary key(id)
) engine=innodb;
假設我們需要刪除型別為t2的資料,語句可能是這樣delete from t where tp = "t2"
,這樣沒啥問題,但我們這張表有5億資料,好吧,真的是5億,所以以後別再跟我說mysql表儲存百萬級別資料就要分表了,百萬太小case了。
這事情我交給了乙個小盆友去幫我搞定,他最開始寫出了如下的語句delete from t where tp = "t2" limit 1000
,使用limit來限制一次刪除的個數,可以了,不過這有個很嚴重的問題,就是越往後,隨著t2型別的減少,我們幾乎都是全表遍歷來刪除,所以總的應該是o(n*n)的開銷。
於是我讓他考慮主鍵,每次操作的時候,記錄當前最大的主鍵,這樣下次就可以從這個主鍵之後開始刪除了,首先select id from t where id > last_max_select_id and tp = "t2" limit 1000
,然後delete from t where id in (ids)
,雖然這次優化採用了兩條語句,但是通過主鍵,我們只需要遍歷一次表就可以了,總的來說,效能要快的。
但是,實際測試的時候,我們卻發現,select這條語句耗時將近30s,太慢了。雖然我們使用了主鍵,但是mysql仍然需要不停的讀取資料判斷條件,加之t2型別的資料在表裡面比較少量,所以為了limit 1000這個條件,mysql需要持續的進行io讀取操作,結果自然是太慢了。
想清楚了這個,其實就好優化了,我們只需要讓條件判斷在應用層做,mysql只查詢資料返回,語句就是select id, tp from t where id > last_max_select_id limit 1000
,得到結果集之後,自行判斷需要刪除的id,然後delete。看似我們需要額外處理邏輯,並且網路開銷也增大了,但mysql只是簡單的io讀取,非常快,總的來說,效能提公升很顯著。當然筆者後續還需要更深入的分析。
mysql併發死鎖優化教程
1.使用一般使用innodb引擎進行事務回滾 2.如果需要修改表的結構,可以在 量少的情況下進行操作,因為dml 增刪改查 操作都會產生元資料鎖,會卡死。3.解決死鎖 在命令列輸入show engine innodb statusg 可以顯示最近死鎖的資訊,然後加以解決 其次可以設定鎖等待時間,這個...
mysql 死鎖語句 MySQL死鎖
死鎖產生 行鎖的具體實現演算法有三種 record lock gap lock以及next key lock。record lock是專門對索引項加鎖 gap lock是對索引項之間的間隙加鎖 next key lock則是前面兩種的組合,對索引項及其之間的間隙加鎖。只在可重複讀或以上隔離級別下的特...
mysql事務死鎖 MySQL事務 死鎖
一 概念 多個事務在同一資源上互相占用形成迴路。這就是死鎖 基本命令 檢視是否自動提交事務 show variables like autocommit 設定事務是否自動提交 set autocommit 0 set autocommit 1 二 例子 create table user id bi...