探秘mysql中的timeout

1.timeout變量知多少

打開mysql,用show variables like ‘%timeout%’命令一看,不看不知道,一看嚇一跳,結果如下面所示,這么多timeout相關變量,一下就嚇尿了。。原來對mysql的了解原來是如此的不夠,好了,這么些timeout究竟各自是什么意思,花了一下午去學習,做了幾個小實驗,總算明白了一二,如有錯誤,請不吝賜教啊。

mysql>?show?variables?like?'%timeout%';  +-----------------------------+----------+  |?Variable_name???????????????|?Value????|  +-----------------------------+----------+  |?connect_timeout?????????????|?10???????|  |?delayed_insert_timeout??????|?300??????|  |?innodb_flush_log_at_timeout?|?1????????|  |?innodb_lock_wait_timeout????|?50???????|  |?innodb_rollback_on_timeout??|?OFF??????|  |?interactive_timeout?????????|?28800????|  |?lock_wait_timeout???????????|?31536000?|  |?net_read_timeout????????????|?30???????|  |?net_write_ti、eout???????????|?60???????|  |?rpl_stop_slave_timeout??????|?31536000?|  |?slave_net_timeout???????????|?3600?????|  |?wait_timeout????????????????|?28800????|  +-----------------------------+----------+

2.分析

下面從timeout里面找些比較常用的出來逐個分析下。

2.1 connect_timeout

connect_timeout指的是連接過程中握手的超時時間,在5.0.52以后默認為10秒,之前版本默認是5秒。官方文檔是這樣說的:

connect_timeout:?The?number?of?seconds?that?the?mysqld?server?waits?for?a?connect?packet?before?responding?with?Bad?handshake.?The?default?value?is?10?seconds?as?of?MySQL?5.0.52?and?5?seconds?before?that

mysql的基本原理應該是有個監聽線程循環接收請求,當有請求來時,創建線程(或者從線程池中取)來處理這個請求。由于mysql連接采用TCP協議,那么之前勢必是需要進行TCP三次握手的。TCP三次握手成功之后,客戶端會進入阻塞,等待服務端的消息。服務端這個時候會創建一個線程(或者從線程池中取一個線程)來處理請求,主要驗證部分包括host和用戶名密碼驗證。host驗證我們比較熟悉,因為在用grant命令授權用戶的時候是有指定host的。用戶名密碼認證則是服務端先生成一個隨機數發送給客戶端,客戶端用該隨機數和密碼進行多次循環加密后發送給服務端驗證。如果通過,整個連接握手過程完成。(具體握手過程后續找到資料再分析)

由此可見,整個連接握手可能會有各種可能出錯。所以這個connect_timeout值就是指這個超時時間了。可以簡單測試下,運行下面的telnet命令會發現客戶端會在10秒后超時返回。

telnet?localhost?3306

在超時之前mysql中該連接循環如下:

256?|?unauthenticated?user?|?localhost:60595?|?NULL?|?Connect?|?NULL?|?Reading?from?net?|?NULL

2.2 interactive_timeout & wait_timeout

還是先看官方文檔,從文檔上來看wait_timeout和interactive_timeout都是指不活躍的連接超時時間,連接線程啟動的時候wait_timeout會根據是交互模式還是非交互模式被設置為這兩個值中的一個。如果我們運行mysql -uroot -p命令登陸到mysql,wait_timeout就會被設置為interactive_timeout的值。如果我們在wait_timeout時間內沒有進行任何操作,那么再次操作的時候就會提示超時,這是mysql client會重新連接。

The?number?of?seconds?the?server?waits?for?activity?on?a?noninteractive?connection?before?closing?it.  On?thread?startup,?the?session?wait_timeout?value?is?initialized?from?the?global?wait_timeout?value?or?from?the?global?interactive_timeout?value,?depending?on?the?type?of?client?(as?defined?by?the?CLIENT_INTERACTIVE?connect?option?to?mysql_real_connect()).

測試如下:

mysql>?set?global?interactive_timeout=3;?##設置交互超時為3秒

重新進入mysql,這時候可以看到:

mysql>?show?variables?like?'%timeout%';?##wait_timeout已經被設置為3秒  +-----------------------------+----------+  |?Variable_name???????????????|?Value????|  +-----------------------------+----------+  |?connect_timeout?????????????|?10???????|  |?delayed_insert_timeout??????|?300??????|  |?innodb_flush_log_at_timeout?|?1????????|  |?innodb_lock_wait_timeout????|?50???????|  |?innodb_rollback_on_timeout??|?OFF??????|  |?interactive_timeout?????????|?3????????|  |?lock_wait_timeout???????????|?31536000?|  |?net_read_timeout????????????|?30???????|  |?net_write_timeout???????????|?3????????|  |?rpl_stop_slave_timeout??????|?31536000?|  |?slave_net_timeout???????????|?3600?????|  |?wait_timeout????????????????|?3????????|  +-----------------------------+----------+

可以看到wait_timeout被設置為了interactive_timeout的值,這樣,我們3秒后再執行其他命令,會提示如下:

mysql>?show?variables?like?'%timeout%';  ERROR?2006?(HY000):?MySQL?server?has?gone?away??##超時重連  No?connection.?Trying?to?reconnect...  Connection?id:????50  Current?database:?***?NONE?***    +-----------------------------+----------+  |?Variable_name???????????????|?Value????|  +-----------------------------+----------+  |?connect_timeout?????????????|?10???????|  |?delayed_insert_timeout??????|?300??????|  |?innodb_flush_log_at_timeout?|?1????????|  |?innodb_lock_wait_timeout????|?50???????|  |?innodb_rollback_on_timeout??|?OFF??????|  |?interactive_timeout?????????|?3????????|  |?lock_wait_timeout???????????|?31536000?|  |?net_read_timeout????????????|?30???????|  |?net_write_timeout???????????|?3????????|  |?rpl_stop_slave_timeout??????|?31536000?|  |?slave_net_timeout???????????|?3600?????|  |?wait_timeout????????????????|?3????????|  +-----------------------------+----------+

2.3 innodb_lock_wait_timeout & innodb_rollback_on_timeout

還是先祭出官方文檔,從文檔中看,這個值是針對innodb引擎的,是innodb中行鎖的等待超時時間,默認為50秒。如果超時,則當前語句會回滾。如果設置了innodb_rollback_on_timeout,則會回滾整個事務,否則,只回滾事務等待行鎖的這個語句。

The?length?of?time?in?seconds?an?InnoDB?transaction?waits?for?a?row?lock?before?giving?up.?The?default?value?is?50?seconds.?A?transaction?that?tries?to?access?a?row?that?is?locked?by?another?InnoDB?transaction?waits?at?most?this?many?seconds?for?write?access?to?the?row?before?issuing?the?following?error:    ERROR?1205?(HY000):?Lock?wait?timeout?exceeded;?try?restarting?transaction

同樣來測試下(先創建一個innodb引擎的表test,只有一列,列名為a):

mysql>?CREATE?TABLE?`test`?(?`a`?int?primary?key)?engine=innodb;

首先插入三條測試數據

mysql>?select?*?from?test;  +---+  |?a?|  +---+  |?1?|  |?2?|  |?3?|

當前innodb_rollback_on_timeout=OFF,設置innodb_lock_wait_timeout=1,我們開啟兩個事務

##事務1?加行鎖  mysql>?begin;  Query?OK,?0?rows?affected?(0.00?sec)    mysql>?select?*?from?test?where?a=2?for?update;  +---+  |?a?|  +---+  |?2?|  +---+  1?row?in?set?(0.01?sec)
##事務2,請求行鎖  mysql>?begin;  Query?OK,?0?rows?affected?(0.00?sec)    mysql>?delete?from?test?where?a=1;  Query?OK,?1?row?affected?(0.00?sec)    mysql>?delete?from?test?where?a=2;?##請求行鎖超時  ERROR?1205?(HY000):?Lock?wait?timeout?exceeded;?try?restarting?transaction  mysql>?select?*?from?test;  +---+  |?a?|  +---+  |?2?|  |?3?|  +---+  2?rows?in?set?(0.00?sec)    mysql>?begin;?##  這里我們直接開啟另外的事務(或者直接commit當前事務),則原來的事務只會回滾第二條語句,最終結果就是test表中只剩下2和3.如果這里我們顯示的rollback,則會回滾整個事務,保持1,2,3不變。

那么如果innodb_rollback_on_timeout=ON,同樣事務2會超時,但是這個時候如果我們begin開啟新的事務,那么會回滾請求鎖超時的整個事務,而不是像前面那樣只回滾了超時的那條語句。

2.4 lock_wait_timeout

文檔中描述如下,簡單說來lock_wait_timeout是元數據鎖等待超時,任意鎖元數據的語句都會用到這個超時參數,默認為一年。元數據鎖可以參加mysql metadata lock,為了保證事務可串行化,不管是myisam還是innodb引擎的表,只要是先在一個session里面開啟一個事務,就會獲取操作表的元數據鎖,這時候如果另一個session要對表的元數據進行修改,則會阻塞直到超時。

This?variable?specifies?the?timeout?in?seconds?for?attempts?to?acquire?metadata?locks.?The?permissible?values?range?from?1?to?31536000?(1?year).?The?default?is?31536000.    This?timeout?applies?to?all?statements?that?use?metadata?locks.?These?include?DML?and?DDL?operations?on?tables,?views,?stored?procedures,?and?stored?functions,?as?well?as?LOCK?TABLES,?FLUSH?TABLES?WITH?READ?LOCK,?and?HANDLER?statements

測試例子:
我們用一個myisam引擎的表myisam_test來測試。其中有一條記錄(1,1),現在我們先開啟一個session,然后執行一個select語句。另外打開一個session,然后執行表的元數據操作,如循環表,會發現操作阻塞直到lock_wait_timeout秒后提示超時。

##第一個session,獲取metadata?lock  mysql>?show?create?table?myisam_test;  -----------------------------------------------------------+  |?Table???????|?Create?Table|  +-----------------------------------------------------------  |?myisam_test?|?CREATE?TABLE?`myisam_test`?(  ??`i`?int(11)?NOT?NULL,  ??`j`?int(11)?DEFAULT?NULL,  ??PRIMARY?KEY?(`i`)  )?ENGINE=MyISAM?DEFAULT?CHARSET=latin1    mysql>?start?transaction;  Query?OK,?0?rows?affected?(0.00?sec)    mysql>?select?*?from?myisam_test;  +---+------+  |?i?|?j????|  +---+------+  |?2?|????1?|  +---+------+  1?row?in?set?(0.00?sec)    ##另一個session,刪除表提示超時  mysql>?drop?table?myisam_test;  ERROR?1205?(HY000):?Lock?wait?timeout?exceeded;?try?restarting?transaction

其中更改表結構的元數據操作指令有如下這些:

DROP?TABLE?t;  ALTER?TABLE?t?...;  DROP?TABLE?nt;  ALTER?TABLE?nt?...;  LOCK?TABLE?t?...?WRITE;

當然,多說一句,對于myisam表的加鎖以及并發插入等,這篇博客myisam表鎖非常詳細,有興趣的可以看看。

2.5 net_read_timeout & net_write_timeout

文檔中描述如下,就是說這兩個參數在網絡條件不好的情況下起作用。比如我在客戶端用load data in循環的方式導入很大的一個文件到數據庫中,然后中途用iptables禁用掉mysql的3306端口,這個時候服務器端該連接狀態是reading from net,在等待net_read_timeout后關閉該連接。同理,在程序里面循環一個很大的表時,在查詢過程中同樣禁用掉端口,制造網絡不通的情況,這樣該連接狀態是writing to net,然后在net_write_timeout后關閉該連接。slave_net_timeout類似。

The?number?of?seconds?to?wait?for?more?data?from?a?connection?before?aborting?the?read.?When?the?server?is?reading?from?the?client,?net_read_timeout?is?the?timeout?value?controlling?when?to?abort.?When?the?server?is?writing?to?the?client,?net_write_timeout?is?the?timeout?value?controlling?when?to?abort

測試:
我創建一個120M的數據文件data.txt。然后登陸到mysql。

mysql?-uroot?-h?127.0.0.1?-P?3306?--local-infile=1

導入過程設置iptables禁用3306端口。

iptables?-A?INPUT?-p?tcp?--dport?3306?-j?DROP  iptables?-A?OUTPUT?-p?tcp?--sport?3306?-j?DROP

可以看到連接狀態為reading from net,然后經過net_read_timeout秒后關閉。

3.總結

經過幾個實驗可以發現,connect_timeout在握手認證階段(authenticate)起作用,interactive_timeout 和wait_timeout在連接空閑階段(sleep)起作用,而net_read_timeout和net_write_timeout則是在連接繁忙階段(query)或者網絡出現問題時起作用。

【相關推薦】

1.?循環

2.?循環

3.?循環

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享