MySQL++:MySQL 鎖表原因 及 核心問題(性能優化)


MySQL 鎖概述:

  鎖是計算機協調多個進程或線程並發訪問某一資源的機制。

在數據庫中,除傳統的計算資源(如CPU、RAM、I/O等)的爭用以外,數據也是一種供許多用戶共享的資源。

如何保證數據並發訪問的一致性、有效性是所有數據庫必須解決的一個問題,鎖沖突也是影響數據庫並發訪問性能的一個重要因素。

從這個角度來說,鎖對數據庫而言顯得尤其重要,也更加復雜。

本章我們着重討論MySQL鎖機制的特點,常見的鎖問題,以及解決MySQL鎖問題的一些方法或建議:↓

- - - 

相對其他數據庫而言,MySQL的鎖機制比較簡單,其最顯著的特點是不同的存儲引擎支持不同的鎖機制。

比如:

  MyISAM:和 MEMORY存儲引擎采用的是表級鎖(table-level locking);

  BDB:存儲引擎采用的是頁面鎖(page-level locking),但也支持表級鎖;

  InnoDB:存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是采用行級鎖。

MySQL 這 3 種鎖的特性可大致歸納如下:↓ ↓ ↓

開銷、加鎖速度、死鎖、粒度、並發性能

l         表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,並發度最低。
l         行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,並發度也最高。
l         頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,並發度一般。

從上述特點可見,很難籠統地說哪種鎖更好,只能就具體應用的特點來說哪種鎖更合適!

僅從鎖的角度來說:表級鎖更適合於以查詢為主,只有少量按索引條件更新數據的應用,如Web應用;

而行級鎖則更適合於有大量按索引條件並發更新少量不同數據,同時又有並發查詢的應用,如一些在線事務處理(OLTP)系統。

一切取決於應用程序,應用程序的不同部分可能需要不同的鎖類型。

為了確定是否想要使用行級鎖定的存儲引擎,應看看應用程序做什么並且混合使用什么樣的選擇和更新語句。

例如,大多數Web應用程序執行許多選擇,而很少進行刪除,只對關鍵字的值進行更新,並且只插入少量具體的表。

基本MySQL MyISAM 設置已經調節得很好。

在 MySQL 中對於使用表級鎖定的存儲引擎,表鎖定時不會死鎖的。

InnoDB:

  對於普通SELECT語句,InnoDB不會加任何鎖;

  InnoDB 行鎖是通過給索引項加鎖實現的:這就意味着只有通過索引條件檢索數據時,InnoDB才使用行鎖,否則使用表鎖。

  在mysql中,如果查詢條件帶有主鍵,會鎖行數據,如果沒有,會鎖表。

  當where條件中的字段沒有加索引時,會鎖住整張表。

  在有索引的情況下,更新不同的行,innodb 默認的行鎖是不會阻塞的。

  where 后面的索引失效時,行鎖變表鎖,其他事物操作會有阻塞。

注意:在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖沖突,從而影響並發性能。

  行鎖建議:↓

    盡可能讓所有數據檢索都通過索引來完成,避免無索引行鎖升級為表鎖

    合理設計索引,盡量縮小索引的范圍

    盡可能較少檢索條件,避免間隙鎖

    盡量控制事務大小,減少鎖定資源量和時間長度

    盡可能低級別事務隔離

    varchar 不加單引號會引發行鎖。

導致鎖表原因:↓

1、鎖表發生在insert update 、delete 中

2、鎖表的原理是 數據庫使用獨占式封鎖機制,當執行上面的語句時,對表進行鎖住,直到發生commite 或者 回滾 或者退出數據庫用戶

  第一、 A程序執行了對 tableA 的 insert ,並還未 commite時,B程序也對tableA 進行insert 則此時會發生資源正忙的異常 就是鎖表

  第二、鎖表常發生於並發而不是並行(並行時,一個線程操作數據庫時,另一個線程是不能操作數據庫的,cpu 和i/o 分配原則)

3、減少鎖表的概率:

  減少insert 、update 、delete 語句執行 到 commite 之間的時間。

  具體點批量執行改為單個執行、優化sql自身的非執行速度 如果異常對事物進行回滾

小栗子:↓ ↓ ↓

1):使用 update  

  假設kid 是表table 的 一個索引字段 且值不唯一:

  1):如果kid 有多個值為12的記錄那么:

    update table set name=’feie’ where kid=12;   #會鎖表

  2):如果kid有唯一的值為1的記錄那么:

    update table set name=’feie’ where kid=1;   #不會鎖

總結:用索引字段做為條件進行修改時, 是否表鎖的取決於這個索引字段能否確定記錄唯一,當索引值對應記錄不唯一,會進行鎖表,相反則行鎖。

2):使用 delete

  如果有兩個delete : kid1 與 kid2 是索引字段

  1):語句1 delete from table where kid1=1 and kid2=2;

  2):語句2 delete from table where kid1=1 and kid2=3;

      # 這樣的兩個delete 是不會鎖表的

  1):語句1 delete from table where kid1=1 and kid2=2;

  2):語句2 delete from table where kid1=1 ;

      # 這樣的兩個delete 會鎖表

總結:同一個表,如果進行刪除操作時,盡量讓刪除條件統一,否則會相互影響造成鎖表

 

相關指令:↓

1):顯示那些線程正在運行(查詢到相對應的進程===然后 kill  id)

   show processlist :只列出前100條

   show full processlist:查看全部進程

2):顯示那些表是打開的

  show open tables  、 show open tables from database;

  這條命令能夠查看當前有那些表是打開的。

  In_use 列表示有多少線程正在使用某張表,Name_locked 表示表名是否被鎖,這一般發生在Drop或Rename命令操作這張表時。

  所以這條命令不能幫助解答我們常見的問題:當前某張表是否有死鎖,誰擁有表上的這個鎖等。

3):查看正在鎖的事務

  SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 

4):查看等待鎖的事務

  SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 

5):查看服務器狀態

  show status like '%lock%';

6):查看超時時間:

  show variables like '%timeout%';

7):查詢表級鎖爭用情況

  可以通過檢查table_locks_waited和table_locks_immediate狀態變量來分析系統上的表鎖定爭奪:

  show status like 'table%';

  show global status like "table_locks%";

  如果Table_locks_waited的值比較高,則說明存在着較嚴重的表級鎖爭用情況。

8):獲取 InnoDB 行鎖爭用情況

  可以通過檢查InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪情況:

  show status like 'innodb_row_lock%';

  如果發現鎖爭用比較嚴重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高

 

補充:

1、show processlist; 進程狀態(status)解釋

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已經處理完了所有待處理的插入操作,正在等待新的請求。

大部分狀態對應很快的操作,只要有一個線程保持同一個狀態好幾秒鍾,那么可能是有問題發生了,需要檢查一下。

還有其他的狀態沒在上面中列出來,不過它們大部分只是在查看服務器是否有存在錯誤是才用得着。

 

 行為不規范-BUG滿屏飛 @Coding++


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM