1. 程式人生 > 資料庫 >MySQLInnoDB鎖與事務理解20201210

MySQLInnoDB鎖與事務理解20201210

MySQLInnoDB鎖與事務理解

概述

事務(Transaction)是併發控制的基本單位。所謂的事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。

事務把所有的命令作為一個整體一起向系統提交或撤銷操作請求,即這一組資料庫命令要麼都執行,要麼都不執行,因此事務是一個不可分割的工作邏輯單元。

在資料庫系統上執行併發操作時,事務是作為最小的控制單元來使用的,特別適用於多使用者同時操作的資料庫系統。例如,航空公司的訂票系統、銀行、保險公司以及證券交易系統等。
  事務具有 4 個特性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和永續性(Durability),這 4 個特性通常簡稱為 ACID。

一 關於事務的定義有幾點需要解釋下:

1.資料庫事務可以包含一個或多個數據庫操作,但這些操作構成一個邏輯上的整體。

2.構成邏輯整體的這些資料庫操作,要麼全部執行成功,要麼全部不執行。

3.構成事務的所有操作,要麼全都對資料庫產生影響,要麼全都不產生影響,即不管事務是否執行成功,資料庫總能保持一致性狀態。

4.以上即使在資料庫出現故障以及併發事務存在的情況下依然成立。

 

二 事務的四大特性

2.1原子性(Atomicity)

原子性(Atomicity):事務中的所有操作作為一個整體像原子一樣不可分割,要麼全部成功,要麼全部失敗。

2.2一致性(Consistency)

 事務的執行結果必須使資料庫從一個一致性狀態到另一個一致性狀態。一致性是基礎,也是最終目的,其他三個特性(原子性、隔離性和永續性)都是為了保證一致性的。

以轉賬為例子,A向B轉賬,假設轉賬之前這兩個使用者的錢加起來總共是2000,那麼A向B轉賬之後,不管這兩個賬戶怎麼轉,A使用者的錢和B使用者的錢加起來的總額還是2000,這個就是事務的一致性。

事務開始前和結束後,資料庫的完整性約束沒有被破壞。比如A向8轉賬,不可能A扣了錢,B卻沒收到。

 

2.3隔離性(Isolation)

 事務的隔離性是多個使用者併發訪問資料庫時,資料庫為每一個使用者開啟的事務,不能被其他事務的操作資料所幹擾,多個併發事務之間要相互隔離。

比如多個使用者同時往一個賬戶轉賬,最後賬戶的結果應該和他們按先後次序轉賬的結果一樣。

2.4永續性(Durability)

永續性(Durability):事務一旦提交,其對資料庫的更新就是持久的。任何事務或系統故障都不會導致資料丟失。

2.5 小結

原子性是事務隔離的基礎,隔離性和永續性是手段,最終目的是為了保持資料的一致性。

三 如何確保事務的ACID特性

在事務的ACID特性中,C即一致性是事務的根本追求,而對資料一致性的破壞主要來自兩個方面

1.事務的併發執行

2.事務故障或系統故障

資料庫系統是通過併發控制技術和日誌恢復技術來避免這種情況發生的。

併發控制技術保證了事務的隔離性,使資料庫的一致性狀態不會因為併發執行的操作被破壞。
   日誌恢復技術保證了事務的原子性,使一致性狀態不會因事務或系統故障被破壞。同時使已提交的對資料庫的修改不會因系統崩潰而丟失,保證了事務的永續性。

四 MySQL中使用事務

Mysql資料庫中使用事務有四種方式,自動提交,顯性提交,手動提交,隱式提交。MySQL預設採用自動提交(AUTOCOMMIT)模式。也就是說,如果不是顯式地開始一個事務,則每個查詢都被當作一個事務執行提交操作。

4.1 自動與手動提交

MySQL預設採用自動提交(AUTOCOMMIT)模式。也就是說,如果不是顯式地開始一個事務,則每個查詢都被當作一個事務執行提交操作。在當前連線中,可以通過設定AUTOCOMMIT變數來啟用或者禁用自動提交模式:

show variables like '%autocom%';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| autocommit    | ON    |

+---------------+-------+

1 row in set (0.00 sec)

 

1或者ON表示啟用,0或者OFF表示禁用。當 AUTOCOMMIT=0時,所有的查詢都是在一個事務中,直到顯式地執行COMMIT提交或者ROLLBACK回滾,該事務結束,同時又開始了另一個新事務。

set session AUTOCOMMIT=0;

delete from syjm5;

沒有提交

select count(*) from syjm5;

50000

 

set session AUTOCOMMIT=0;

delete from syjm5;

commit;

 

select count(*) from syjm5;

0

4.2 顯式提交

事務的語句

 開始事物:BEGIN

 提交事物:COMMIT

 回滾事務:ROLLBACK

  結束事務:END 

事務的儲存點

     SAVE TRANSACTION 儲存點名稱 --自定義儲存點的名稱和位置

     ROLLBACK TRANSACTION 儲存點名稱 --回滾到自定義的儲存點

begin tran1 --開始執行事務

 

update bb setmoneys=moneys-@momeys where ID=@fromID -執行的第一個操作,轉賬出錢,減去轉出的金額

update bb setmoneys=moneys+@momeys where ID=@toID --執行第二個操作,接受轉賬的金額,增加轉來的金額

 

if@@error<>0 --判斷如果兩條語句有任何一條出現錯誤

begin

rollback –開始執行事務的回滾,恢復的轉賬開始之前狀態

return 0

end

go

 

else   --如何兩條都執行成功

begin

commit --執行這個事務的操作

return 1

end

go

4.3 隱式提交

又名自動提交,即無需顯示執行commit語句,session中的操作被自動提交到資料庫的過程。

隱式提交的方式:

1、正常執行完DDL語句。包括create,alter,drop,truncate,rename。

2、正常執行完DCL語句。包括grant,revoke。

3、正常退出資料庫管理軟體,沒有明確發出commit或者rollback。

隱式提交的注意事項:

1、執行DDL語句時,前面的DML操作也會被提交到資料庫中,因為是在一個session裡,那執行ddl語句的時候前面的dml語句肯定也會“不可倖免”的被提交到庫中。

2、即使DDL語句執行失敗,前面的DML操作也會被提交到資料庫中這就有點兒讓人奇怪了,DDL都執行失敗了,怎麼還會提交呢?這就需要探究一下隱式提交的本質了(下文有敘述)。

3、在前面1和2的基礎上,為了避免隱式提交或者回滾,儘量保證一條或者幾條DML操作完成後有顯示的提交或者回滾,防止後續執行的DCL或者DDL自動提交前期的DML操作。

 

五 併發與隔離級別

資料庫是一個共享資源,可以供多個使用者使用,眾多使用者同時使用同一個資料庫,稱為高併發資料庫。在同一時刻併發執行的事務數可達數百上千個。例如飛機訂票資料庫系統、銀行資料庫系統等都是高併發資料庫系統。

 

5.1 高併發帶來問題

高併發會出現丟失更新,髒讀,不可重複讀,幻讀等問題,是通過隔離級別解決。但是隔離級別越高,資料庫的併發效能越差,效能越低。

5.2 丟失更新Lost Update

更新丟失(Lost Update )A和B同時寫

1.事務A將數值改為1並提交﹔⒉.事務B將數值改為2並提交。

這時資料的值為2,事務A所做的更新將會丟失。

解決辦法︰對行加鎖,只允許併發一個更新事務。

5.3 髒讀

事務A讀取了事務B更新的資料,然後B回滾操作,那麼A讀取到的資料是髒資料。

例如︰

1.風哥的學員張同學的原工資10000,財務人員將張同學的工資改為20000。(但未提交事務)

2.張同學這時去查自己的工資,發現自己的工資變為了20000,這小子夠開心了,準備提前拿1萬出去玩玩。

3.而財務發現操作有誤,回滾了事務,張同學的工資又變為了10000 ,像這樣,張同學之前查到的工資數20000是一個髒資料。

5.4 不可重複讀

事務A多次讀取同一資料,事務B在事務A多次讀取的過程中,對資料作了更新並提交,導致事務A多次讀取同一資料時,結果不一致。

A先讀,B再改,A再讀

例如:

1.在事務A中,張同學讀取了自己的工資為10000,操作並沒有完成。

⒉在事務B中,這時財務人員修改了張同學的工資為20000,並提交了事務。

3.在事務A中,張同學再次讀取自己的工資時,工資變為了20000.

解決辦法∶如果只有在修改事務完全提交之後才可以讀取資料,則可以避免該問題。

5.5 幻讀

B事務讀取了兩次資料,在這兩次的讀取過程中A事務添加了資料,B事務的這兩次讀取出來的集合不一樣。

系統管理員A將資料庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。

A改後還未提交,B改其他的,A再查

例如︰

1.A把所有的“10000”改為“20000"

2.B把所有的“5000”改為“10000"3.A再查詢10000,卻發現還有一批。

小結︰不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。

5.4 事務隔離級別

 

 

Read Uncommitted(讀未提交)

最低的隔離級別,什麼都不需要做,一個事務可以讀到另一個事務未提交的結果;所有的併發事務問題都會發生,解決了更新丟失。

Read Committed(讀已提交)

只有在事務提交後,其更新結果才會被其他事務看見。解決了更新丟失、髒讀。大多數資料庫的預設隔離級別為: Read Commited(讀已提交) ,如Oracle , DB2 , Sql Server。

Repeated Read(重複讀)

在一個事務中,對於同一份資料的讀取結果總是相同的,無論是否有其他事務對這份資料進行操作,以及這個事務是否提交。解決了更新丟失、髒讀、不可重複讀。

少數資料庫預設的隔離級別為Repeatable Read(可重複讀),

如MySQL InnoDB儲存弓|擎,因為考慮到資料安全,請使用Read Commited(讀已提交)。

Serialization(序列化)

事務序列化執行,隔離級別最高,犧牲了系統的併發性。可以解決併發事務的所有問題。

show variables like tx_isolation;

+---------------+----------------+

| Variable_name | Value          |

+---------------+----------------+

| tx_isolation  | READ-COMMITTED |

+---------------+----------------+

 

 

 

 

六 MySQL MVCC(多版本併發控制)

6.1 功能介紹

MVCC(Multi Version Concurrency Control的簡稱),代表多版本併發控制。與MVCC相對的,是基於鎖的併發控制,Lock-Based Concurrency Control)。
    MVCC最大的優勢:讀不加鎖,讀寫不衝突。在讀多寫少的OLTP應用中,讀寫不衝突是非常重要的,極大的增加了系統的併發效能。

指的是一種提高併發的技術。最早的資料庫版本,只有讀讀之間可以併發;但讀寫/寫讀/寫寫都要阻塞。

引入多版本之後,只有寫寫之間相互阻塞,其他三種操作(讀讀/讀寫/寫讀)都可以並行,這樣大幅度提高了InnoDB的併發度。

在內部實現中, InnoDB是通過在undo log中實現的,通過undo log可以找回資料的歷史版本(前映象) , 找回的資料歷史版本可以提供給使用者讀,也可以在回滾的時候覆蓋資料頁上的資料。

6.2 多版本併發控制實現原理

每個事務啟動時,InnoDB會 為每個啟動的事務提供一個當前時刻的映象;

為了實現此功能, InnoDB會為每個表提供2個隱藏欄位:

一個用於儲存行的建立時間,

一個用於儲存行的失效時間(裡面儲存的是系統版本號: system version number )

在某一事務中,使用比當前事務相等或更舊的版本號資料,從而保證其所讀取的資料都是過去的資料。

6.3 MVCC實現

MVCC是通過在每行記錄後面儲存兩個隱藏的列來實現的。這兩個列,一個儲存了行的建立時間,一個儲存行的過期時間(或刪除時間)。當然儲存的並不是實際的時間值,而是系統版本號(system version number)。每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會作為事務的版本號,用來和查詢到的每行記錄的版本號進行比較。
  下面看一下在REPEATABLE READ隔離級別下,MVCC具體是如何操作的。

SELECT

InnoDB會根據以下兩個條件檢查每行記錄:

InnoDB只查詢版本早於當前事務版本的資料行(也就是,行的系統版本號小於或等於事務的系統版本號),這樣可以確保事務讀取的行,要麼是在事務開始前已經存在的,要麼是事務自身插入或者修改過的。

行的刪除版本要麼未定義,要麼大於當前事務版本號。這可以確保事務讀取到的行,在事務開始之前未被刪除。

只有符合上述兩個條件的記錄,才能返回作為查詢結果

INSERT

InnoDB為新插入的每一行儲存當前系統版本號作為行版本號。

DELETE

InnoDB為刪除的每一行儲存當前系統版本號作為行刪除標識。

UPDATE

InnoDB為插入一行新記錄,儲存當前系統版本號作為行版本號,同時儲存當前系統版本號到原來的行作為行刪除標識。儲存這兩個額外系統版本號,使大多數讀操作都可以不用加鎖。這樣設計使得讀資料操作很簡單,效能很好,並且也能保證只會讀取到符合標準的行,不足之處是每行記錄都需要額外的儲存空間,需要做更多的行檢查工作,以及一些額外的維護工作

6.4 MVCC優缺點

6.4.1 MVCC的優點

在讀取資料的時候, innodb幾乎不用獲得任何鎖,每個查詢都通過版本檢查,只獲得自己需要的資料版本,從而大大提高了系統的併發度。

6.4.2 MVCC的缺點

為了實現多版本, innodb必須對每行增加相應的欄位來儲存版本資訊,同時需要維護每一行的版本資訊,而且在檢索行的時候,需要進行版本的比較,因而降低了查詢的效率;

innodb還必須定期清理不再需要的行版本,及時回收空間,這也增加了一些開銷。

七 通過案例理解隔離級別

7.1 讀未提交Read Uncommited

 

mysql> set session tx_isolation='read-uncommitted';

Query OK, 0 rows affected, 1 warning (0.00 sec)

 

mysql>  show variables like '%tx_isolation%';

+---------------+------------------+

| Variable_name | Value            |

+---------------+------------------+

| tx_isolation  | READ-UNCOMMITTED |

+---------------+------------------+

1 row in set (0.00 sec)

 

視窗1

Begin

create table syjjjj(id int,name varchar(10),age int);

視窗2 讀出的資料是錯誤的

 

 

7.2 讀已提交 Read commited

mysql> set session tx_isolation='READ-COMMITTED';

Query OK, 0 rows affected, 1 warning (0.00 sec)

 

mysql>  show variables like '%tx_isolation%';

+---------------+----------------+

| Variable_name | Value          |

+---------------+----------------+

| tx_isolation  | READ-COMMITTED |

+---------------+----------------+

1 row in set (0.01 sec)

 

 

7.3可重複讀Repeatable Read

set session tx_isolation='Repeatable-Read';

Query OK, 0 rows affected, 1 warning (0.00 sec)

 

mysql> show variables like '%tx_isolation%';

+---------------+-----------------+

| Variable_name | Value           |

+---------------+-----------------+

| tx_isolation  | REPEATABLE-READ |

+---------------+-----------------+

1 row in set (0.00 sec)

隔離級別

表資料

A插入已經提交了,但是B永遠無法查詢

如果想查出資料,必須要B重新連線,有一個新事務以後,才能查出資料。

八 MySQL鎖管理

8.1 MySQL鎖介紹

MySQL鎖是資料庫管理系統協調多個程序或執行緒併發訪問某一資料的機制。

在資料庫中,除傳統的計算資源(CPU、RAM、I/O)的爭用以外,資料也是一種供許多使用者共享的資源。如何保證資料併發訪問的一致性、有效性是所在有資料庫必須解決的一個問題,鎖衝突也是影響資料庫併發訪問效能的一個重要因素。從這個角度來說,鎖對資料庫而言顯得尤其重要,也更加複雜。鎖有效解決一致性和有效性。

鎖衝突也是影響資料庫併發訪問效能的一個重要因素。鎖是Mysql在伺服器層和儲存引擎層的的併發控制。

加鎖是消耗資源的,鎖的各種操作,包括獲得鎖、檢測鎖是否是否已解除、釋放鎖等。

8.2 根據鎖粒度分為3類

不同儲存引擎的可以支援的鎖型別

Lock鎖根據粒度主要分為表鎖、頁鎖和行鎖。不同的儲存引擎擁有的鎖粒度都不同。

 

8.2.1 表鎖:table lock

表鎖:table lock,是MySQL各儲存引擎中最大顆粒度的鎖定機制,該鎖定機制最大的特點是實現邏輯非常簡單,帶來的系統負面影響最小。獲取鎖和釋放鎖的速度很快。由於表級鎖一次會將整個表鎖定,所以可以很好的避免困擾我們的死鎖問題。鎖定了整張表,開銷小,加鎖快﹔不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。

當然,鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的概率也會最高,致使並大度大打折扣。 使用表級鎖定的主要是MyISAM,MEMORY,CSV等一些非事務性儲存引擎。

表級鎖更適合於以查詢為主,併發使用者少,只有少量按索引條件更新資料的應用,如Web 應用。

MyISAM 在執行查詢語句(SELECT)前,會自動給涉及的表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT 等)前,會自動給涉及的表加寫鎖,這個過程並不需要使用者干預,因此,使用者一般不需要直接用 LOCK TABLE 命令給 MyISAM 表顯式加鎖。

使用者也可以使用lock 和UNLOCK 手動新增與解除表鎖

LOCK TABLE itpux_m5 read, itpux_m1 read;

select count(*) as 'sum' from itpux_m1;

select count(*) as 'sum' from itpux_m5;

UNLOCK TABLES;

 

 

8.2.2 行鎖:row lock

行鎖:row lock,鎖定了需要的行,開銷大,加鎖慢﹔會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。

是目前各大資料庫管理軟體所實現的鎖定顆粒度最小的。由於鎖定顆粒度很小,所以發生鎖定資源爭用的概率也最小,能夠給予應用程式儘可能大的併發處理能力而提高一些需要高併發應用系統的整體效能。

雖然能夠在併發處理能力上面有較大的優勢,但是行級鎖定也因此帶來了不少弊端。由於鎖定資源的顆粒度很小,所以每次獲取鎖和釋放鎖需要做的事情也更多,帶來的消耗自然也就更大了。此外,行級鎖定也最容易發生死鎖。
使用行級鎖定的主要是InnoDB儲存引擎。

行級鎖∶更適合於有大量按索引條件併發更新少量不同資料,同時又有併發查詢的應用,如一些線上事務處理(OLTP)系統。

注:鎖的粒度越小,開銷越大,但併發性越好。

A行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。

B最大程度的支援併發,同時也帶來了最大的鎖開銷。

C在 InnoDB 中,除單個 SQL 組成的事務外,鎖是逐步獲得的,這就決定了在 InnoDB 中發生死鎖是可能的。

D行級鎖只在儲存引擎層實現,而Mysql伺服器層沒有實現。 行級鎖更適合於有大量按索引條件併發更新少量不同資料,同時又有併發查詢的應用,如一些線上事務處理(OLTP)系統

 

8.2.3頁鎖:page lock

頁鎖:page lock,開銷和加鎖時間界於表鎖和行鎖之間; 獲取鎖定所需要的資源開銷,以及所能提供的併發處理能力也同樣是介於上面二者之間。另外,頁級鎖定和行級鎖定一樣,會發生死鎖。併發度在兩者之間。


 

8.2.4 鎖粒度總結

在資料庫實現資源鎖定的過程中,隨著鎖定資源顆粒度的減小,鎖定相同資料量的資料所需要消耗的記憶體數量是越來越多的,實現演算法也會越來越複雜。不過,隨著鎖定資源顆粒度的減小,應用程式的訪問請求遇到鎖等待的可能性也會隨之降低,系統整體併發度也隨之提升。

表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。

行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。

頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

從鎖的角度來說,表級鎖更適合於以查詢為主,只有少量按索引條件更新資料的應用,如Web應用;而行級鎖則更適合於有大量按索引條件併發更新少量不同資料,同時又有併發查詢的應用,如一些線上事務處理(OLTP)系統。

8.3 InnoDB鎖模式分類

8.3.1 鎖分類介紹

對資料的操作其實只有兩種,也就是讀和寫,而資料庫在實現鎖時,也會對這兩種操作使用不同的鎖;InnoDB 實現了標準的行級鎖,也就是共享鎖(Shared Lock)和排他鎖(Exclusive Lock)。

共享鎖(讀鎖),允許事務讀一行資料。

排他鎖(寫鎖),允許事務刪除或更新一行資料。

而它們的名字也暗示著各自的另外一個特性,共享鎖之間是相容的,而互斥鎖與其他任意鎖都不相容:

 

 

lnnodb 實現了兩種型別的行鎖:

·共享鎖(S)︰允許一個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖,允許其他事物再加S鎖,不允許其他事物再加X鎖。非阻塞。

排他鎖(X)∶允許獲得排他鎖的事務更新資料,阻止其他事務獲取相同資料集的共享讀鎖和排他寫鎖。也稱寫鎖,不允許其他事務再加S鎖或者X鎖,阻塞。

另外,為了允許行鎖和表鎖共存,事項多粒度鎖機制,innodb還有兩種內部使用的意向鎖,這兩種意向鎖都是表鎖:

·意向共享鎖(IS)︰事務打算給資料行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。

·意向排它鎖(IX)︰事務打算給資料行加行排它鎖,事務在給一個數據行加排它鎖前必須先取得該表的IX鎖。

8.3.2 鎖相容

8.4 手動加鎖

8.4.1 行鎖

事務可以通過以下語句顯式給記錄集加共享鎖或排它鎖。

共享鎖(S) : select * from table_name where ... lock in share

排它鎖(X) : select * from table_name where ... for update.

8.4.2 表鎖

mysql 的 表鎖 lock tables 感覺就像一個封閉的空間

 LOCK TABLE `table` [READ|WRITE]

解鎖

   UNLOCK TABLES;

 

 

//如 將 table1 設為read鎖, table2 設為write鎖, table3 設為read鎖

lock tables [table1] read,[table2] write,[table3] read;
----------------------------------------------------------------------


//執行到這裡時,進入封閉空間。
1. table1 僅允許[所有人]讀,[空間外]如需寫、更新要等待[空間退出],[空間內]如需寫、更新會引發mysql報錯。
2. table2 僅允許[空間內]讀寫更新,[空間外]如需寫、更新要等待[空間退出]。
3. table3 僅允許[所有人]讀,[空間外]如需寫、更新要等待[空間退出],[空間內]如需寫、更新會引發mysql報錯。
----------------------------------------------------------------------
//執行到這裡時,退出封閉空間,釋放所有表鎖

unlock tables

8.5 InnoDB鎖演算法

Record Lock、Gap Lock、Next-key Lock鎖

8.5.1 行鎖Record Lock

​ InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,後者是通過在資料塊中對相應資料行加鎖來實現的。單條索引上加鎖,record lock 永遠鎖的是索引,而非資料本身。

如果innodb表中沒有索引,那麼會自動建立一個隱藏的聚集索引,鎖住的就是這個聚集索引。所以說當一條sql沒有走任何索引時,那麼將會在每一條聚集索引後面加X鎖,這個類似於表鎖,但原理上和表鎖應該是完全不同的。

記錄鎖鎖定的是索引記錄,而不是行資料,也就是說鎖定的是key。

該鎖是加在索引上的(從上面的index PRIMARY of table 'itpux `. fg`就能看出來)記錄鎖可以有兩種型別∶

lock_mode X locks rec but not gap

lock_mode s locks rec but not gap

8.5.2 間隙鎖Gap Lock

間隙鎖,是在索引的間隙之間加上鎖,這是為什麼Repeatable Read隔離級別下能防止幻讀的主要原因。

Gap鎖(間隙鎖),鎖住的不是記錄,而是範圍,一般是針對非唯一索引而言的,確保索引記錄的間隙不變,間隙鎖是針對事務隔離級別為可重複讀或以上級別而已的。

eg:RECORD LOCKS space id 12 page no 5 n bits 23 index idx_fg of table 'itpux'. fg ' trx id 6686 lock_mode X locks gap before rec

間隙鎖的出現主要集中在同一個事務中先delete後insert的情況下,當去刪除一條記錄的時候,如果這個記錄存在,那麼這個時產生普通行鎖,鎖住這個記錄,然後刪除再釋放鎖。

如果這條記錄不存在,問題就來了,資料庫會掃描索引,發現這個記錄不存在,這個時候的delete語句獲取到的就是一個間隙鎖,然後資料庫會向左掃描掃到第一個比給定引數小的值,向右掃描掃描到第一個比給定引數大的值,然後以此為界,構建一個區間,鎖住整個區間內的資料,一個特別容易出現死鎖的間隙鎖誕生了。

 很顯然,在使用範圍條件檢索並鎖定記錄時,InnoDB這種加鎖機制會阻塞符合條件範圍內鍵值的併發插入,這往往會造成嚴重的鎖等待。因此,在實際開發中,尤其是併發插入比較多的應用,我們要儘量優化業務邏輯,儘量使用相等條件來訪問更新資料,避免使用範圍條件。

8.5.3 Next-Key Lock

前兩種的結合,對記錄及其前面的間隙加鎖。

預設情況下,InnoDB工作在可重複讀隔離級別下,並且會以Next-Key Lock的方式對資料行進行加鎖,這樣可以有效防止幻讀的發生。

Next-Key Lock是行鎖和間隙鎖的組合,當InnoDB掃描索引記錄的時候,會首先對索引記錄加上行鎖(Record Lock ),再對索引記錄兩邊的間隙加上間隙鎖(Gap Lock )。加上間隙鎖之後,其他事務就不能在這個間隙修改或者插入記錄。

eg : RECORD LOCKS space id 12page no 5 n bits 23 index idx_fg of table 'itpux '. 'fg'trx id6686 lock_mode x

8.5.4 Insert Intention Locks插入意向鎖

可以理解為特殊的Gap間隔鎖的一種,用以提升併發寫入的效能。

eg :RECORD LOCKS space id 12 page no 3 n bits 23 index PRIMARY of table 'itpux . `fg trx id6686 lock_mode X insert intention waiting

8.5.5 .AUTO-INC Locks自增鎖

屬於表級別的鎖,

涉及到引數innodb_auto_lockmode三個取值∶(在複製有時候細講)與複製有關。

insert—般分為兩種,

第一種:簡單的insert。插入之前知道插入的行數,autoincrement提前分配,提前就確定了,如:insert ,replace。

第二種:塊的insert。插入之前不知道多少插入的行數,如:insert select , load data ,replace , replace ,replace ... select

簡單insert不會生成auto-inc locking(自增鎖)。

當事務A,開始insert時,事務B被鎖住,當事務A insert結束時,事務B insert被解鎖並插入成功。這樣的目的是Mysql通過auto-inc locking來保障塊插入是連續的所以在此期間不能有新記錄插入。

如果列中沒有自增量,auto-inc locking鎖自然也不會存在。

工作中經常有使用MySQL自帶的autoincrement函式作為計數器,在實際使用中當併發比較小的時候還沒有問題,一旦併發增加就會出現很多問題,因為插入的記錄行數不能馬上確定的。

自增鎖在事物提交前就釋放,其他的鎖是在事物提交時才釋放

如果自增鎖鎖在提交後才釋放,那其他事物就沒法插入了,無法實現併發。

eg : TABLE LOCK table xx trx id 6686 lock mode AUTO-INC waiting

8.5.6 顯示鎖(explicit lock)

手工加鎖,在show engine innoDB status 中能夠看到,會在記憶體中產生物件,佔用記憶體.eg: select ... for update , select ... lock in share mode

8.5.7 .隱示鎖(implicit lock)

在索引中對記錄邏輯的加鎖,但是實際上不產生鎖物件,不佔用記憶體空間.

哪些語句會產生implicit lock呢?

insert into xx values(xx)

update xx set t=t+1 where id = 1;會對輔助索引加implicit lock

implicit lock產生衝突的時候,會自動轉換成explicit lock,這樣做的好處就是降低鎖的開銷。

比如∶我插入了一條記錄100,本身這個記錄加上implicit lock,如果這時候有人再去更新這條100的記錄,那麼就會自動轉換成explicit lock

8.5.8 metadata lock元資料鎖,資料字典鎖

這是MySQL Server層實現的鎖,跟引擎層無關,當你執行select的時候,如果這時候有ddi語句,那麼ddl會被阻塞,因為select語句擁有metadata lock,防止元資料被改掉。

8.6 其他名稱解釋

8.6.1 鎖遷移(鎖繼承)

比如我鎖住的記錄是一條已經被標記為刪除的記錄,但是還沒有被puger,然後這條被purge掉了,那麼上面的鎖自然而然就繼承給了下一條記錄,我們稱之為鎖遷移。

鎖升級︰執行一條全表更新的語句,那麼資料庫就會對所有記錄進行加鎖,那麼可能造成鎖開銷非常大,可能升級為頁鎖,或者表鎖。MySQL沒有鎖升級功能。

8.6.2 鎖分裂與鎖合併

InnoDB的實現加鎖,其實是在頁上面做的,沒有辦法直接對記錄加鎖。當insert的時候,會產生頁的分裂動作(行溢位),如果頁分裂了,鎖物件也會分裂。有頁分裂,也就有頁合併,鎖也會合作。

8.6.3 latch

latch一般稱為門( shuan)鎖(輕重級別的鋇),內公是I水oTT出I的論什過。若持續的時間長,則應用的效能會非常差。臨界資源用完釋放,不支援死鎖檢測,是應用程式中的鎖,不是資料庫的鎖。

在lnnodb儲存引擎中,latch又可以分為mutex(互斥量)和RW-Lock(讀寫鎖)。

8.6.4 .lock

lock物件是事務,用來鎖定的是資料庫中的物件,如表、行、頁。

一般lock的物件僅在事務commit或rollback後進行釋放(不同事務隔離級別釋放的時間可能不同)。

此外,lock正如在大多數資料庫中一樣,是有死鎖機制的。

當事務結束後,釋放,支援死鎖檢測,是資料庫中的鎖。

8.6.5死鎖

所謂死鎖<DeadLock>:是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。

此時系統處於死鎖狀態或系統產生了死鎖,這些在互相等待的程序稱為死鎖程序。表級鎖不會產生死鎖,所以解決死鎖主要還是針對於最常用的InnoDB。

如果沒有死鎖檢測,那麼就會互相卡死,一直hang死。

如果有死鎖檢測機制,那麼資料庫會自動根據代價來評估出哪些事務可以被回滾掉,用來打破這個僵局。

總結:

死鎖並沒有啥壞處,反而可以保護資料庫和應用。

那麼出現死鎖,而且非常頻繁,我們應該調整業務邏輯,讓其避免產生死鎖方為上策。

九 閱讀mysql鎖日誌

主要針對兩種情況,1.表裡面有索引。2.表裡面沒有索引

9.1 有索引檢視鎖日誌

create table syj111(

id int PRIMARY key auto_increment,

name VARCHAR(20),

age int (10),

key idx_name (name desc)

) engine=innodb DEFAULT charset=utf8;

 

insert into syj111 values (1,'jjj',27);

insert into syj111 values (2,'lhr',22);

insert into syj111 values (3,'fj',25);

insert into syj111 values (4,'db',23);

insert into syj111 values (5,'hn',23);

 

desc syj111;

+-------+-------------+------+-----+---------+----------------+

| Field | Type        | Null | Key | Default | Extra          |

+-------+-------------+------+-----+---------+----------------+

| id    | int(11)     | NO   | PRI | NULL    | auto_increment |

| name  | varchar(20) | YES  | MUL | NULL    |                |

| age   | int(10)     | YES  |     | NULL    |                |

 

set  tx_isolation='Repeatable-Read';

##開始事務未提交

begin;

select * from syj111 where id=1 for update;

+----+------+------+

| id | name | age  |

+----+------+------+

|  1 | jjj  |   27 |

+----+------+------+

1 row in set (0.00 sec)

 

show engine innodb status\G;

鎖詳細日誌沒有被開啟

TRANSACTIONS

------------

Trx id counter 12069

Purge done for trx's n:o < 12066 undo n:o < 0 state: running but idle

History list length 0

LIST OF TRANSACTIONS FOR EACH SESSION:

---TRANSACTION 421691023244000, not started

0 lock struct(s), heap size 1136, 0 row lock(s)

---TRANSACTION 421691023243088, not started

0 lock struct(s), heap size 1136, 0 row lock(s)

 

show variables like '%output_locks%';

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| innodb_status_output_locks | OFF   |

+----------------------------+-------+

1 row in set (0.00 sec)

set globalinnodb_status_output_locks=1;

 

TRANSACTIONS

------------

Trx id counter 12565  

---下一個事務號 12565

Purge done for trx's n:o < 0 undo n:o < 0 state: running but idle

--purge 執行緒是空的undo n:o < 0中未被purge的事務數為0Purge done for trx's n:o < 0解釋purge執行緒已經清理了<0事務。

 InnoDBdelete所做刪除只是標記為刪除的狀態,實際上並沒有刪除掉,因為MVCC機制的存在,要保留之前的版本為併發所使用。最終的刪除由purge執行緒來決定的什麼時候來真正刪除檔案的。

History list length 0

--10秒會刪除無用的undo頁,這裡的History list length就指上一個10秒之後的未刪除的事務數,一般100以內為正常。

LIST OF TRANSACTIONS FOR EACH SESSION:

--列出事務會話

---TRANSACTION 421931892361952, not started

0 lock struct(s), heap size 1136, 0 row lock(s)

---TRANSACTION 12563, ACTIVE 339 sec

--事務ID12563 已經被鎖住339

  1. lock struct(s), heap size 1136, 1 row lock(s)

--一條記錄被鎖住,記憶體1136位元組

MySQL thread id 2, OS thread handle 140455760918272, query id 55 localhost root starting

show engine innodb status

 

TABLE LOCK table `itpuxdb`.`syj111` trx id 12566 lock mode IX

--表鎖 lock mode IX 排他意向鎖,2個記錄鎖,查詢的不是主鍵,但是通過主鍵鎖定的。

 

RECORD LOCKS space id 75 page no 4 n bits 72 index idx_name of table `itpuxdb`.`syj111` trx id 12566 lock_mode X

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 3; hex 6a6a6a; asc jjj;; --二級索引被鎖住了

 1: len 4; hex 80000001; asc     ;; --一級索引被鎖住了

--記錄鎖 Next-Key Lock(記錄鎖和間隔鎖)75號表空間,4頁。

 

RECORD LOCKS space id 75 page no 3 n bits 72 index PRIMARY of table `itpuxdb`.`syj111` trx id 12566 lock_mode X locks rec but not gap

Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0

 0: len 4; hex 80000001; asc     ;;      --一級索引被鎖住了

 1: len 6; hex 000000002f15; asc     / ;;  --事務id 2f15十六進位制變成十進位制12053

 2: len 7; hex b50000003d0110; asc     =  ;; —佔用7位元組回滾指標,MVCC

 3: len 3; hex 6a6a6a; asc jjj;;          --第二個欄位jjj被鎖定了

 4: len 4; hex 8000001b; asc     ;;    --第三個段被鎖定

--記錄鎖 行鎖Record Lock,排它鎖。75號表空間,3頁。第二行

 

 

RECORD LOCKS space id 75 page no 4 n bits 72 index idx_name of table `itpuxdb`.`syj111` trx id 12566 lock_mode X locks gap before rec

Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 3; hex 6c6872; asc lhr;;

 1: len 4; hex 80000002; asc     ;;

--間隙鎖Gap Lock75號表空間,4頁。第3

說明主鍵索引是一級索引,其他索引是二級索引。最後都是通過主鍵索引鎖定的。

 

mysql> desc  syj111;

+-------+-------------+------+-----+---------+----------------+

| Field | Type        | Null | Key | Default | Extra          |

+-------+-------------+------+-----+---------+----------------+

| id    | int(11)     | NO   | PRI | NULL    | auto_increment |

| name  | varchar(20) | YES  | MUL | NULL    |                |

| age   | int(10)     | YES  |     | NULL    |                |

+-------+-------------+------+-----+---------+----------------+

  1. rows in set (0.00 sec)

 

9.2 表中沒有索引的日誌

create table syj2(

id int,

name VARCHAR(20),

age int (10)

) engine=innodb DEFAULT charset=utf8;

 

insert into syj2 values (1,'jjj',27);

insert into syj2 values (2,'lhr',22);

insert into syj2 values (3,'fj',25);

insert into syj2values (4,'db',23);

insert into syj2 values (5,'hn',23);

set  tx_isolation='Repeatable-Read';

show variables like '%tx_isolation%';

begin;

select * from syj2 where name=’jjj’ for update;

---TRANSACTION 12584, ACTIVE 17 sec

事務ID 活躍時間17秒。6個鎖,一個表鎖,5個行鎖。

2 lock struct(s), heap size 1136, 6 row lock(s)

MySQL thread id 4, OS thread handle 140455760918272, query id 111 localhost root starting

show engine innodb status

TABLE LOCK table `itpuxdb`.`syj2` trx id 12584 lock mode IX

--表鎖 lock mode IX 排他意向鎖,由於沒有建立索引

RECORD LOCKS space id 77 page no 3 n bits 72 index GEN_CLUST_INDEX of table `itpuxdb`.`syj2` trx id 12584 lock_mode X

--如果表中沒有PRIMARY KEY,而且也沒有合適的UNIQUE index,那麼InnoDB內部將生產一個名字叫GEN_CLUST_INDEX的隱藏clustered index,其值為行ID

 這種鎖資訊的出現,以為著出現了表鎖。在innodb中行級鎖是靠索引實現的,出現這種鎖資訊,基本可以判定欄位缺少索引。

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

 0: len 8; hex 73757072656d756d; asc supremum;;

 

Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0

 0: len 6; hex 000000037400; asc     t ;;

 1: len 6; hex 00000000311b; asc     1 ;;

 2: len 7; hex bb0000002b0110; asc     +  ;;

 3: len 4; hex 80000001; asc     ;;

 4: len 3; hex 6a6a6a; asc jjj;;

 5: len 4; hex 8000001b; asc     ;;

 

Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0

 0: len 6; hex 000000037401; asc     t ;;

 1: len 6; hex 00000000311c; asc     1 ;;

 2: len 7; hex bc0000002d0110; asc     -  ;;

 3: len 4; hex 80000002; asc     ;;

 4: len 3; hex 6c6872; asc lhr;;

 5: len 4; hex 80000016; asc     ;;

 

Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0

 0: len 6; hex 000000037402; asc     t ;;

 1: len 6; hex 000000003121; asc     1!;;

 2: len 7; hex bf000000810110; asc        ;;

 3: len 4; hex 80000003; asc     ;;

 4: len 2; hex 666a; asc fj;;

 5: len 4; hex 80000019; asc     ;;

 

Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0

 0: len 6; hex 000000037403; asc     t ;;

 1: len 6; hex 000000003122; asc     1";;

 2: len 7; hex c00000003b0110; asc     ;  ;;

 3: len 4; hex 80000005; asc     ;;

 4: len 2; hex 686e; asc hn;;

 5: len 4; hex 80000017; asc     ;;

 

Record lock, heap no 6 PHYSICAL RECORD: n_fields 6; compact format; info bits 0

 0: len 6; hex 000000037404; asc     t ;;

 1: len 6; hex 000000003127; asc     1';;

 2: len 7; hex a3000000220110; asc     "  ;;

 3: len 4; hex 80000004; asc     ;;

 4: len 2; hex 6462; asc db;;

 5: len 4; hex 80000017; asc     ;;

 

十 Mysql死鎖相關引數

innodb_print_all_deadlocks              ON  

###死鎖的相關日誌回輸出

 innodb_lock_wait_timeout                5 

 預設是50秒,MySQL獲取行鎖的時候,有鎖超過5秒就超時了。就報鎖等待超時。

innodb_deadlock_detect                  ON 

開啟死鎖檢測,資料庫自動回滾,如果沒有off就用超時來處理,就用這個引數處理innodb_lock_wait_timeout。

innodb_status_output_locks              OFF  

#預設關閉,建議開啟,有詳細的記錄

10.1 檢視鎖狀態

show engine innodb status\G

show variables like '%timeout%';

innodb_lock_wait_timeout    | 5       ##鎖等待超時5秒

show status like '%lock%';

 

show variables like '%autocommit%';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| autocommit    | ON    |

+---------------+-------+

Autocommit   ##自動提交

 

 十一 Mysql常見鎖及問題解決

11.1 模擬一個典型的鎖及處理過程

set global innodb_lock_wait_timeout=5000;

Query OK, 0 rows affected (0.00 sec)

 

show variables like 'innodb_lock_wait_timeout%';

+--------------------------+-------+

| Variable_name            | Value |

+--------------------------+-------+

| innodb_lock_wait_timeout | 5000  |

+--------------------------+-------+

 

select @@tx_isolation;

+----------------+

| @@tx_isolation |

+----------------+

| READ-COMMITTED |

+----------------+

1 row in set, 1 warning (0.00 sec)

 

CREATE table syjuser2 (

a int auto_increment PRIMARY key,

b int,

c int,

UNIQUE key idx_b(b)

) ENGINE innodb;

 

insert into syjuser2 value (null,12,21);

insert into syjuser2 value (null,13,31);

commit;

B視窗由於A視窗沒有提交 插入12被卡住,

 

---TRANSACTION 34848, ACTIVE 140 sec inserting

mysql tables in use 1, locked 1

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1

MySQL thread id 6, OS thread handle 140555715659520, query id 190 localhost root update

insert into syjuser2 value (null,12,22)

------- TRX HAS BEEN WAITING 140 SEC FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 153 page no 4 n bits 72 index idx_b of table `itpuxdb`.`syjuser2` trx id 34848 lock mode S waiting

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32

 0: len 4; hex 8000000c; asc     ;;

 1: len 4; hex 80000001; asc     ;;

 

------------------

TABLE LOCK table `itpuxdb`.`syjuser2` trx id 34848 lock mode IX

RECORD LOCKS space id 153 page no 4 n bits 72 index idx_b of table `itpuxdb`.`syjuser2` trx id 34848 lock mode S waiting

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32

 0: len 4; hex 8000000c; asc     ;;

 1: len 4; hex 80000001; asc     ;;

 

---TRANSACTION 34847, ACTIVE 165 sec

3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1

MySQL thread id 2, OS thread handle 140555718940416, query id 188 localhost root

TABLE LOCK table `itpuxdb`.`syjuser2` trx id 34847 lock mode IX

RECORD LOCKS space id 153 page no 4 n bits 72 index idx_b of table `itpuxdb`.`syjuser2` trx id 34847 lock_mode X locks rec but not gap

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32

 0: len 4; hex 8000000c; asc     ;;

 1: len 4; hex 80000001; asc     ;;

 

RECORD LOCKS space id 153 page no 3 n bits 72 index PRIMARY of table `itpuxdb`.`syjuser2` trx id 34847 lock_mode X locks rec but not gap

Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 32

 0: len 4; hex 80000001; asc     ;;

 1: len 6; hex 00000000881f; asc       ;;

 2: len 7; hex 3d0000002d2513; asc =   -% ;;

 3: len 4; hex 8000000c; asc     ;;

 4: len 4; hex 80000015; asc     ;;

 

解決辦法1,應用自己提交,鎖釋放

11.1.1 解決辦法2,應用長時間不能提交,鎖死,手工干預。

1 查詢是否有鎖表

show open tables where in_use>0;

+----------+----------+--------+-------------+

| Database | Table    | In_use | Name_locked |

+----------+----------+--------+-------------+

| itpuxdb  | syjuser2 |      2 |           0 |

+----------+----------+--------+-------------+

Name_locked >0 就是有鎖表。

查詢單個數據庫的情況

show open tables from itpuxdb;

 

2 查程序

show processlist;

+----+------+---------------------+---------+---------+------+----------+-----------------------------------------+

| Id | User | Host                | db      | Command | Time | State    | Info                                    |

+----+------+---------------------+---------+---------+------+----------+-----------------------------------------+

|  2 | root | localhost           | itpuxdb | Sleep   | 1268 |          | NULL                                    |

|  3 | root | 192.168.198.1:51170 | itpuxdb | Sleep   | 1462 |          | NULL                                    |

|  4 | root | 192.168.198.1:51195 | itpuxdb | Sleep   | 1458 |          | NULL                                    |

|  5 | root | 192.168.198.1:51198 | itpuxdb | Sleep   | 1458 |          | NULL                                    |

|  6 | root | localhost           | itpuxdb | Query   | 1243 | update   | insert into syjuser2 value (null,12,22) |

|  7 | root | localhost           | NULL    | Query   |    0 | starting | show processlist                        |

|  8 | root | localhost           | itpuxdb | Query   |  226 | update   | insert into syjuser2 value (null,12,26) |

+----+------+---------------------+---------+---------+------+----------+-----------------------------------------+

 

找到有鎖的程序ID,殺掉: kill  8;

kill  8;

 

kill  8;

Query OK, 0 rows affected (0.00 sec)

 

mysql> show processlist;

+----+------+---------------------+---------+---------+------+----------+-----------------------------------------+

| Id | User | Host                | db      | Command | Time | State    | Info                                    |

+----+------+---------------------+---------+---------+------+----------+-----------------------------------------+

|  2 | root | localhost           | itpuxdb | Sleep   | 1433 |          | NULL                                    |

|  3 | root | 192.168.198.1:51170 | itpuxdb | Sleep   | 1627 |          | NULL                                    |

|  4 | root | 192.168.198.1:51195 | itpuxdb | Sleep   | 1623 |          | NULL                                    |

|  5 | root | 192.168.198.1:51198 | itpuxdb | Sleep   | 1623 |          | NULL                                    |

|  6 | root | localhost           | itpuxdb | Query   | 1408 | update   | insert into syjuser2 value (null,12,22) |

|  7 | root | localhost           | NULL    | Query   |    0 | starting | show processlist                        |

+----+------+---------------------+---------+---------+------+----------+-----------------------------------------+

 

11.1.2 show processlist; 列解釋

各列的含義和用途:

id,一個標識,你要kill一個執行緒的時候很有用。

user,顯示當前使用者,如果不是root,這個命令就只顯示你許可權範圍內的sql語句。

host,顯示這個語句是從哪個ip、哪個埠上發出的。

db,顯示這個程序目前連線的是哪個資料庫。

command,顯示這個連線的狀態,一般就是休眠(sleep),查詢(query),連線(connect)。

time,顯示這個狀態持續的時間,單位是秒。

state,顯示使用當前連線的sql語句的狀態,很重要的列,可用來判斷mysql的執行狀態。

info,顯示這個sql語句,因為長度有限,所以長的sql語句就顯示不全,但是一個判斷問題語句的重要依據。

這個命令中最關鍵的就是state列,mysql列出的狀態主要有以下幾種:

   Checking table正在檢查資料表(這是自動的)。
 Closing tables 正在將表中修改的資料重新整理到磁碟中,同時正在關閉已經用完的表。這是一個很快的操作,如果不是這樣的話,就應該確認磁碟空間是否已經滿了或者磁碟是否正處於重負中。
 Connect Out 複製從伺服器正在連線主伺服器。
 Copying to tmp table on disk 由於臨時結果集大於tmp_table_size,正在將臨時表從記憶體儲存轉為磁碟儲存以此節省記憶體。
 Creating tmp table 正在建立臨時表以存放部分查詢結果。
 deleting from main table 伺服器正在執行多表刪除中的第一部分,剛刪除第一個表。
 deleting from reference tables 伺服器正在執行多表刪除中的第二部分,正在刪除其他表的記錄。
 Flushing tables 正在執行FLUSH TABLES,等待其他執行緒關閉資料表。

 Killed 傳送了一個kill請求給某執行緒,那麼這個執行緒將會檢查kill標誌位,同時會放棄下一個kill請求。MySQL會在每次的主迴圈中檢查kill標誌位,不過有些情況下該執行緒可能會過一小段才能死掉。如果該執行緒程被其他執行緒鎖住了,那麼kill請求會在鎖釋放時馬上生效。
 Locked 被其他查詢鎖住了。
 Sending data 正在處理Select查詢的記錄,同時正在把結果傳送給客戶端。
 Sorting for group 正在為GROUP BY做排序。
 Sorting for order 正在為ORDER BY做排序。
 Opening tables 這個過程應該會很快,除非受到其他因素的干擾。例如,在執Alter TABLE或LOCK TABLE語句行完以前,資料表無法被其他執行緒開啟。正嘗試開啟一個表。
 Removing duplicates 正在執行一個Select DISTINCT方式的查詢,但是MySQL無法在前一個階段優化掉那些重複的記錄。因此,MySQL需要再次去掉重複的記錄,然後再把結果傳送給客戶端。
 Reopen table 獲得了對一個表的鎖,但是必須在表結構修改之後才能獲得這個鎖。已經釋放鎖,關閉資料表,正嘗試重新開啟資料表。
 Repair by sorting 修復指令正在排序以建立索引。
 Repair with keycache 修復指令正在利用索引快取一個一個地建立新索引。它會比Repair by sorting慢些。
 Searching rows for update 正在講符合條件的記錄找出來以備更新。它必須在Update要修改相關的記錄之前就完成了。
 Sleeping 正在等待客戶端傳送新請求.
 System lock  正在等待取得一個外部的系統鎖。如果當前沒有執行多個mysqld伺服器同時請求同一個表,那麼可以通過增加--skip-external-locking引數來禁止外部系統鎖。
 Upgrading lock  Insert DELAYED正在嘗試取得一個鎖表以插入新記錄。
 Updating  正在搜尋匹配的記錄,並且修改它們。
 User Lock  正在等待GET_LOCK()。
 Waiting for tables  該執行緒得到通知,資料表結構已經被修改了,需要重新開啟資料表以取得新的結構。然後,為了能的重新開啟資料表,必須等到所有其他執行緒關閉這個表。以下幾種情況下會產生這個通知:FLUSH TABLES tbl_name, Alter TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
 waiting for handler insert  Insert DELAYED已經處理完了所有待處理的插入操作,正在等待新的請求。

11.1.3 檢視正在鎖的事務

正在鎖的事務(被鎖的)

select * from information_schema.INNODB_TRX;

select * from information_schema.INNODB_locks;

 

檢視等待鎖的事務,阻塞。(查詢源頭)

select * from information_schema.INNODB_lock_waits;

|檢視trx_ _mysql_ thread_ id,根據這個ID殺死鎖。

trx. Weight 事務鎖定的行數。

可以根據這個查詢資料庫有多少鎖等待,阻塞。

 

十二 如何在Mysql中避免死鎖

修改應用程式:大事務拆小,大表折多表。

修改表模式:刪除不必要的外來鍵,合理使用索引。

間隙鎖:使用隔離級別為讀提交( read committed)

DDL操作:不要在客戶端修改表結構,可能被鎖,最好用語句在服務端執行。

12.1 InnoDB 優化建議

從鎖機制的實現方面來說,InnoDB 的行級鎖帶來的效能損耗可能比表級鎖要高一點,但在併發方面的處理能力遠遠優於 MyISAM 的表級鎖。這也是大多數公司的 MySQL 都是使用 InnoDB 模式的原因。

但是,InnoDB 也有脆弱的一面,下面提出幾個優化建議供大家參考:

1 儘可能讓資料檢索通過索引完成,避免 InnoDB 因為無法通過索引加行鎖,而導致升級為表鎖的情況。換句話說就是,多用行鎖,少用表鎖。

2 加索引的時候儘量準確,避免造成不必要的鎖定影響其他查詢。

3 儘量減少給予範圍的資料檢索(間隙鎖),避免因為間隙鎖帶來的影響,鎖定了不該鎖定的記錄。

4 儘量控制事務的大小,減少鎖定的資源量和鎖定時間。

5 儘量使用較低級別的事務隔離,減少 MySQL 因為事務隔離帶來的成本。使用Read Committed(讀已提交)語句級別。