自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL在并發(fā)場景下的問題及解決思路

數(shù)據(jù)庫 MySQL
為了滿足這一目標(biāo)大多數(shù)數(shù)據(jù)庫通過鎖和事務(wù)機(jī)制來實(shí)現(xiàn),MySQL數(shù)據(jù)庫也不例外。盡管如此我們?nèi)匀粫跇I(yè)務(wù)開發(fā)過程中遇到各種各樣的疑難問題,本文將以案例的方式演示常見的并發(fā)問題并分析解決思路。

1、背景

對于數(shù)據(jù)庫系統(tǒng)來說在多用戶并發(fā)條件下提高并發(fā)性的同時又要保證數(shù)據(jù)的一致性一直是數(shù)據(jù)庫系統(tǒng)追求的目標(biāo),既要滿足大量并發(fā)訪問的需求又必須保證在此條件下數(shù)據(jù)的安全,為了滿足這一目標(biāo)大多數(shù)數(shù)據(jù)庫通過鎖和事務(wù)機(jī)制來實(shí)現(xiàn),MySQL數(shù)據(jù)庫也不例外。盡管如此我們?nèi)匀粫跇I(yè)務(wù)開發(fā)過程中遇到各種各樣的疑難問題,本文將以案例的方式演示常見的并發(fā)問題并分析解決思路。

2、表鎖導(dǎo)致的慢查詢的問題

首先我們看一個簡單案例,根據(jù)ID查詢一條用戶信息: 

  1. mysql> select * from user where id=6

這個表的記錄總數(shù)為3條,但卻執(zhí)行了13秒。

出現(xiàn)這種問題我們首先想到的是看看當(dāng)前MySQL進(jìn)程狀態(tài):

從進(jìn)程上可以看出select語句是在等待一個表鎖,那么這個表鎖又是什么查詢產(chǎn)生的呢?這個結(jié)果中并沒有顯示直接的關(guān)聯(lián)關(guān)系,但我們可以推測多半是那條update語句產(chǎn)生的(因?yàn)檫M(jìn)程中沒有其他可疑的SQL),為了印證我們的猜測,先檢查一下user表結(jié)構(gòu):

果然user表使用了MyISAM存儲引擎,MyISAM在執(zhí)行操作前會產(chǎn)生表鎖,操作完成再自動解鎖。如果操作是寫操作,則表鎖類型為寫鎖,如果操作是讀操作則表鎖類型為讀鎖。正如和你理解的一樣寫鎖將阻塞其他操作(包括讀和寫),這使得所有操作變?yōu)榇?;而讀鎖情況下讀-讀操作可以并行,但讀-寫操作仍然是串行。以下示例演示了顯式指定了表鎖(讀鎖),讀-讀并行,讀-寫串行的情況。

顯式開啟/關(guān)閉表鎖,使用lock table user read/write; unlock tables;

session1:

session2:

可以看到會話1啟用表鎖(讀鎖)執(zhí)行讀操作,這時會話2可以并行執(zhí)行讀操作,但寫操作被阻塞。接著看:

session1:

session2:

當(dāng)session1執(zhí)行解鎖后,seesion2則立刻開始執(zhí)行寫操作,即讀-寫串行。

總結(jié):

到此我們把問題的原因基本分析清楚,總結(jié)一下——MyISAM存儲引擎執(zhí)行操作時會產(chǎn)生表鎖,將影響其他用戶對該表的操作,如果表鎖是寫鎖,則會導(dǎo)致其他用戶操作串行,如果是讀鎖則其他用戶的讀操作可以并行。所以有時我們遇到某個簡單的查詢花了很長時間,看看是不是這種情況。

解決辦法:

1)盡量不用MyISAM存儲引擎,在MySQL8.0版本中已經(jīng)去掉了所有的MyISAM存儲引擎的表,推薦使用InnoDB存儲引擎。

2)如果一定要用MyISAM存儲引擎,減少寫操作的時間;

3、線上修改表結(jié)構(gòu)有哪些風(fēng)險?

如果有一天業(yè)務(wù)系統(tǒng)需要增大一個字段長度,能否在線上直接修改呢?在回答這個問題前,我們先來看一個案例:

以上語句嘗試修改user表的name字段長度,語句被阻塞。按照慣例,我們檢查一下當(dāng)前進(jìn)程:

從進(jìn)程可以看出alter語句在等待一個元數(shù)據(jù)鎖,而這個元數(shù)據(jù)鎖很可能是上面這條select語句引起的,事實(shí)正是如此。在執(zhí)行DML(select、update、delete、insert)操作時,會對表增加一個元數(shù)據(jù)鎖,這個元數(shù)據(jù)鎖是為了保證在查詢期間表結(jié)構(gòu)不會被修改,因此上面的alter語句會被阻塞。那么如果執(zhí)行順序相反,先執(zhí)行alter語句,再執(zhí)行DML語句呢?DML語句會被阻塞嗎?例如我正在線上環(huán)境修改表結(jié)構(gòu),線上的DML語句會被阻塞嗎?答案是:不確定。

在MySQL5.6開始提供了online ddl功能,允許一些DDL語句和DML語句并發(fā),在當(dāng)前5.7版本對online ddl又有了增強(qiáng),這使得大部分DDL操作可以在線進(jìn)行。詳見:https://dev.mysql.com/doc/refman/5.7/en/innodb-create-index-overview.html

所以對于特定場景執(zhí)行DDL過程中,DML是否會被阻塞需要視場景而定。

總結(jié):通過這個例子我們對元數(shù)據(jù)鎖和online ddl有了一個基本的認(rèn)識,如果我們在業(yè)務(wù)開發(fā)過程中有在線修改表結(jié)構(gòu)的需求,可以參考以下方案:

1. 盡量在業(yè)務(wù)量小的時間段進(jìn)行;

2. 查看官方文檔,確認(rèn)要做的表修改可以和DML并發(fā),不會阻塞線上業(yè)務(wù);

3. 推薦使用percona公司的pt-online-schema-change工具,該工具被官方的online ddl更為強(qiáng)大,它的基本原理是:通過insert… select…語句進(jìn)行一次全量拷貝,通過觸發(fā)器記錄表結(jié)構(gòu)變更過程中產(chǎn)生的增量,從而達(dá)到表結(jié)構(gòu)變更的目的。

例如要對A表進(jìn)行變更,主要步驟為: 

  1. 創(chuàng)建目的表結(jié)構(gòu)的空表,A_new;  
  2. 在A表上創(chuàng)建觸發(fā)器,包括增、刪、改觸發(fā)器;  
  3. 通過insert…select…limit N 語句分片拷貝數(shù)據(jù)到目的表  
  4. Copy完成后,將A_new表rename到A表。 

4、一個死鎖問題的分析

在線上環(huán)境下死鎖的問題偶有發(fā)生,死鎖是因?yàn)閮蓚€或多個事務(wù)相互等待對方釋放鎖,導(dǎo)致事務(wù)永遠(yuǎn)無法終止的情況。為了分析問題,我們下面將模擬一個簡單死鎖的情況,然后從中總結(jié)出一些分析思路。

演示環(huán)境:MySQL5.7.20 事務(wù)隔離級別:RR

表user: 

  1. CREATE TABLE `USER` (  
  2. `ID` INT(11) NOT NULL AUTO_INCREMENT,  
  3. `NAME` VARCHAR(300) DEFAULT NULL,  
  4. `AGE` INT(11) DEFAULT NULL,  
  5. PRIMARY KEY (`ID`)  
  6. ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8 

下面演示事務(wù)1、事務(wù)2工作的情況:

這是一個簡單的死鎖場景,事務(wù)1、事務(wù)2彼此等待對方釋放鎖,InnoDB存儲引擎檢測到死鎖發(fā)生,讓事務(wù)2回滾,這使得事務(wù)1不再等待事務(wù)B的鎖,從而能夠繼續(xù)執(zhí)行。那么InnoDB存儲引擎是如何檢測到死鎖的呢?為了弄明白這個問題,我們先檢查此時InnoDB的狀態(tài): 

  1. show engine innodb statusG 

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

LATEST DETECTED DEADLOCK

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

  1. 2018-01-14 12:17:13 0x70000f1cc000  
  2. *** (1) TRANSACTION:  
  3. TRANSACTION 5120, ACTIVE 17 sec starting index read  
  4. mysql tables in use 1, locked 1  
  5. LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)  
  6. MySQL thread id 10, OS thread handle 123145556967424, query id 2764 localhost root updating  
  7. update user set name='haha' where id=4 

*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 

  1. RECORD LOCKS space id 94 page no 3 n bits 80 index PRIMARY of table `test`.`user` trx id 5120 lock_mode X locks rec but not gap waiting  
  2. Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0  
  3. 0: len 4; hex 80000004; asc ;;  
  4. 1: len 6; hex 0000000013fa; asc ;;  
  5. 2: len 7; hex 520000060129a6; asc R ) ;;  
  6. 3: len 4; hex 68616861; asc haha;;  
  7. 4: len 4; hex 80000015; asc ;; 

*** (2) TRANSACTION: 

  1. TRANSACTION 5121, ACTIVE 12 sec starting index read  
  2. mysql tables in use 1, locked 1  
  3. 3 lock struct(s), heap size 1136, 2 row lock(s)  
  4. MySQL thread id 11, OS thread handle 123145555853312, query id 2765 localhost root updating  
  5. update user set name='hehe' where id=3 

*** (2) HOLDS THE LOCK(S): 

  1. RECORD LOCKS space id 94 page no 3 n bits 80 index PRIMARY of table `test`.`user` trx id 5121 lock_mode X locks rec but not gap  
  2. Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0  
  3. 0: len 4; hex 80000004; asc ;;  
  4. 1: len 6; hex 0000000013fa; asc ;;  
  5. 2: len 7; hex 520000060129a6; asc R ) ;;  
  6. 3: len 4; hex 68616861; asc haha;;  
  7. 4: len 4; hex 80000015; asc ;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 

  1. RECORD LOCKS space id 94 page no 3 n bits 80 index PRIMARY of table `test`.`user` trx id 5121 lock_mode X locks rec but not gap waiting  
  2. Record lock, heap no 7 PHYSICAL RECORD: n_fields 5; compact format; info bits 0  
  3. 0: len 4; hex 80000003; asc ;;  
  4. 1: len 6; hex 0000000013fe; asc ;;  
  5. 2: len 7; hex 5500000156012f; asc U V /;;  
  6. 3: len 4; hex 68656865; asc hehe;;  
  7. 4: len 4; hex 80000014; asc ;; 

*** WE ROLL BACK TRANSACTION (2)

InnoDB狀態(tài)有很多指標(biāo),這里我們截取死鎖相關(guān)的信息,可以看出InnoDB可以輸出最近出現(xiàn)的死鎖信息,其實(shí)很多死鎖監(jiān)控工具也是基于此功能開發(fā)的。

在死鎖信息中,顯示了兩個事務(wù)等待鎖的相關(guān)信息(藍(lán)色代表事務(wù)1、綠色代表事務(wù)2),重點(diǎn)關(guān)注:WAITING FOR THIS LOCK TO BE GRANTED和HOLDS THE LOCK(S)。

WAITING FOR THIS LOCK TO BE GRANTED表示當(dāng)前事務(wù)正在等待的鎖信息,從輸出結(jié)果看出事務(wù)1正在等待heap no為5的行鎖,事務(wù)2正在等待 heap no為7的行鎖;

HOLDS THE LOCK(S):表示當(dāng)前事務(wù)持有的鎖信息,從輸出結(jié)果看出事務(wù)2持有heap no為5行鎖。

從輸出結(jié)果看出,***InnoDB回滾了事務(wù)2。

那么InnoDB是如何檢查出死鎖的呢?

我們想到最簡單方法是假如一個事務(wù)正在等待一個鎖,如果等待時間超過了設(shè)定的閾值,那么該事務(wù)操作失敗,這就避免了多個事務(wù)彼此長等待的情況。參數(shù)innodb_lock_wait_timeout正是用來設(shè)置這個鎖等待時間的。

如果按照這個方法,解決死鎖是需要時間的(即等待超過innodb_lock_wait_timeout設(shè)定的閾值),這種方法稍顯被動而且影響系統(tǒng)性能,InnoDB存儲引擎提供一個更好的算法來解決死鎖問題,wait-for graph算法。簡單的說,當(dāng)出現(xiàn)多個事務(wù)開始彼此等待時,啟用wait-for graph算法,該算法判定為死鎖后立即回滾其中一個事務(wù),死鎖被解除。該方法的好處是:檢查更為主動,等待時間短。

下面是wait-for graph算法的基本原理:

為了便于理解,我們把死鎖看做4輛車彼此阻塞的場景:

 

4輛車看做4個事務(wù),彼此等待對方的鎖,造成死鎖。wait-for graph算法原理是把事務(wù)作為節(jié)點(diǎn),事務(wù)之間的鎖等待關(guān)系,用有向邊表示,例如事務(wù)A等待事務(wù)B的鎖,就從節(jié)點(diǎn)A畫一條有向邊到節(jié)點(diǎn)B,這樣如果A、B、C、D構(gòu)成的有向圖,形成了環(huán),則判斷為死鎖。這就是wait-for graph算法的基本原理。

總結(jié):

1. 如果我們業(yè)務(wù)開發(fā)中出現(xiàn)死鎖如何檢查出?剛才已經(jīng)介紹了通過監(jiān)控InnoDB狀態(tài)可以得出,你可以做一個小工具把死鎖的記錄收集起來,便于事后查看。

2. 如果出現(xiàn)死鎖,業(yè)務(wù)系統(tǒng)應(yīng)該如何應(yīng)對?從上文我們可以看到當(dāng)InnoDB檢查出死鎖后,對客戶端報(bào)出一個Deadlock found when trying to get lock; try restarting transaction信息,并且回滾該事務(wù),應(yīng)用端需要針對該信息,做事務(wù)重啟的工作,并保存現(xiàn)場日志事后做進(jìn)一步分析,避免下次死鎖的產(chǎn)生。

5、鎖等待問題的分析

在業(yè)務(wù)開發(fā)中死鎖的出現(xiàn)概率較小,但鎖等待出現(xiàn)的概率較大,鎖等待是因?yàn)橐粋€事務(wù)長時間占用鎖資源,而其他事務(wù)一直等待前個事務(wù)釋放鎖。

從上述可知事務(wù)1長時間持有id=3的行鎖,事務(wù)2產(chǎn)生鎖等待,等待時間超過innodb_lock_wait_timeout后操作中斷,但事務(wù)并沒有回滾。如果我們業(yè)務(wù)開發(fā)中遇到鎖等待,不僅會影響性能,還會給你的業(yè)務(wù)流程提出挑戰(zhàn),因?yàn)槟愕臉I(yè)務(wù)端需要對鎖等待的情況做適應(yīng)的邏輯處理,是重試操作還是回滾事務(wù)。

在MySQL元數(shù)據(jù)表中有對事務(wù)、鎖等待的信息進(jìn)行收集,例如information_schema數(shù)據(jù)庫下的INNODB_LOCKS、INNODB_TRX、INNODB_LOCK_WAITS,你可以通過這些表觀察你的業(yè)務(wù)系統(tǒng)鎖等待的情況。你也可以用一下語句方便的查詢事務(wù)和鎖等待的關(guān)聯(lián)關(guān)系: 

  1. SELECT R.TRX_ID WAITING_TRX_ID,  
  2.  R.TRX_MYSQL_THREAD_ID WAITING_THREAD,  
  3.  R.TRX_QUERY WATING_QUERY,  
  4.  B.TRX_ID BLOCKING_TRX_ID,  
  5.  B.TRX_MYSQL_THREAD_ID BLOCKING_THREAD,  
  6.  B.TRX_QUERY BLOCKING_QUERY  
  7. FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS W  
  8. INNER JOIN INFORMATION_SCHEMA.INNODB_TRX B ON B.TRX_ID = W.BLOCKING_TRX_ID  
  9. INNER JOIN INFORMATION_SCHEMA.INNODB_TRX R ON R.TRX_ID = W.REQUESTING_TRX_ID; 

結(jié)果: 

  1. waiting_trx_id: 5132  
  2. waiting_thread: 11  
  3. wating_query: update user set name='hehe' where id=3  
  4. blocking_trx_id: 5133  
  5. blocking_thread: 10  
  6. blocking_query: NULL 

總結(jié):

1. 請對你的業(yè)務(wù)系統(tǒng)做鎖等待的監(jiān)控,這有助于你了解當(dāng)前數(shù)據(jù)庫鎖情況,以及為你優(yōu)化業(yè)務(wù)程序提供幫助;

2. 業(yè)務(wù)系統(tǒng)中應(yīng)該對鎖等待超時的情況做合適的邏輯判斷。

6、小結(jié)

本文通過幾個簡單的示例介紹了我們常用的幾種MySQL并發(fā)問題,并嘗試得出針對這些問題我們排查的思路。文中涉及事務(wù)、表鎖、元數(shù)據(jù)鎖、行鎖,但引起并發(fā)問題的遠(yuǎn)遠(yuǎn)不止這些,例如還有事務(wù)隔離級別、GAP鎖等。真實(shí)的并發(fā)問題可能多而復(fù)雜,但排查思路和方法卻是可以復(fù)用,在本文中我們使用了show processlist;show engine innodb status;以及查詢元數(shù)據(jù)表的方法來排查發(fā)現(xiàn)問題,如果問題涉及到了復(fù)制,還需要借助master/slave監(jiān)控來協(xié)助。 

責(zé)任編輯:龐桂玉 來源: 今日頭條
相關(guān)推薦

2018-05-04 15:15:37

數(shù)據(jù)庫MySQL并發(fā)場景

2013-07-04 10:55:20

2022-02-02 21:50:25

底層偽共享CPU

2022-05-11 11:25:49

模型方案

2018-07-27 10:56:10

2010-08-19 09:29:26

hoverIE6

2016-11-09 21:09:54

mysqlmysql優(yōu)化

2021-12-01 10:13:48

場景分布式并發(fā)

2023-07-18 09:24:04

MySQL線程

2025-01-03 09:56:09

2022-05-27 09:25:49

數(shù)據(jù)并發(fā)

2023-06-25 08:05:09

MySQL事務(wù)并發(fā)

2024-01-15 08:57:13

MySQL高并發(fā)

2021-05-18 08:21:38

React HooksReact前端

2023-05-28 13:13:54

高并發(fā)場景JUC

2022-11-16 21:55:51

Redis數(shù)據(jù)庫

2018-05-29 11:44:22

數(shù)據(jù)庫MySQL死鎖

2025-02-26 03:00:00

2025-02-28 00:03:22

高并發(fā)TPS系統(tǒng)

2021-05-14 14:52:59

高并發(fā)TPSQPS
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號