客戶端C API
在C API中調用mysql_options()來設置mysql_init() 所創建的連接對象的屬性,使用這三個選項可以設置連接超時和讀寫超時,單位都是秒。讀寫超時達到後C API的查詢發送和結果獲取函數會返回超時錯誤。
MYSQL_OPT_CONNECT_TIMEOUT
MYSQL_OPT_READ_TIMEOUT
MYSQL_OPT_WRITE_TIMEOUT
也可以使用配置文件來設置連接超時和交互超時:
connect-timeout=seconds
interactive-timeout=seconds
當客戶端API在向mysql server發起連接connect-timeout秒後沒有收到mysql server的相應那麼認爲連接失敗。
interactive-timeout是用於這個客戶端連接的交互超時。交互超時用於在交互界面程序(比如mysql這個連接客戶端程序)中設置server的會話超時時間,來賦值給wait_timeout會話變量,它通常比較大,因爲使用這個交互界面的通常是人類在手動操作,而不是程序執行和發送指令。mysql server默認使用會話變量@@wait_timeout 作爲會話的超時,除非在mysql_real_connect中使用CLIENT_INTERACTIVE標誌來標識這個連接是用於交互操作,此時如果客戶端有interactive-timeout就使用它來作爲本會話的會話超時,否則使用服務器端的interactive-timeout作爲會話超時設置給wait_timeout。
MySQL Server內部
從下圖中可以看出,服務器端關於超時的變量有很多,這裏去掉了我們的TDSQL特有的非標準的MySQL/MariaDB的幾個超時值。其中的connect_timeout, net_read_timeout, net_write_timeout,slave_net_timeout, interactive_timeout和wait_timeout與網絡IO有關。
其中,connect_timeout 被用於在用戶登錄期間,也就是建立數據庫連接期間,作爲mysql server端的網絡讀寫超時。這與文檔上面的說法並不相同,但是代碼中確實是這樣子的。
net_read_timeout 和net_write_timeout是數據庫會話創建好之後mysql server端使用的讀寫超時。如果讀取或者寫入操作在等待了達到超時後服務器認爲客戶端連接斷開,執行錯誤處理。
而slave_net_timeout是slave的io線程使用客戶端C API連接master時候,調用mysql_options()來設置MYSQL_OPT_CONNECT_TIMEOUT,MYSQL_OPT_READ_TIMEOUT,MYSQL_OPT_WRITE_TIMEOUT這三個超時選項使用的值。IO線程連接master使用的都是標準的客戶端C API代碼和通信協議。
最後,wait_timeout是mysqld server的默認的會話超時,如果一個數據庫連接(會話)在這麼長時間之後沒有任何讀寫動作,那麼這個連接被關閉。interactive_timeout是默認的交互式連接的會話超時,會設置給wait_timeout,如果客戶端有自定義的值,那麼那個值會被優先使用來設置給wait_timeout。
所有這些超時可以分爲連接超時,讀寫超時和會話超時三類,下面就講一下這些超時機制是如何實現的。
超時的實現方法
在Linux的connect(), recv, send, read(), write()等系統調用中,並不可以簡單地阻塞等待一段指定時間後再返回錯誤,而是要麼把文件句柄設置爲非阻塞的(使用fcntl()和O_NONBLOCK標誌,或者對於recv/send()可以每次調用使用MSG_DONTWAIT)並且立刻返回,要麼一直阻塞等待。所以,要實現超時還是要一點小技巧的。另外,MySQL中網絡IO的代碼無論是客戶端C API的網絡IO功能還是服務器內部使用的網絡通信功能,都是同一份代碼實現,因此下文不需要區分客戶端和服務器端。
連接超時
相關函數:vio_socket_connect(),vio_io_wait()
首先,如果有連接超時時間的話,就設置socket fd爲非阻塞的,然後調用標準的socket函數connect()來發起連接,這個函數會立即返回-1並且設置errno爲 EINPROGRESS或者EALREADY。然後,調用vio_io_wait()來使用poll()來阻塞等待這個連接可以寫入,使用連接超時值作爲poll()的超時參數。這樣,在等待超時後就認爲連接失敗,否則連接就成功了。這裏並沒有使用網絡協議自己的連接超時,因爲那樣的話無法在不影響其他進程的情況下隨時靈活更改這個超時時間。
讀寫超時
相關函數:vio_read(), vio_write(), vio_socket_io_wait()
首先,調用標準的recv()/send()系統調用來讀取或者寫入,如果有讀寫超時時間的話,就使用MSG_DONTWAIT作爲最後一個flags參數,這樣如果沒有數據可以讀取或者無法寫入(比如網絡擁塞)這個函數會立刻返回SOCKET_EAGAIN 或者SOCKET_EWOULDBLOCK,於是,調用vio_socket_io_wait()來阻塞等待這個socket fd可以讀或者寫,這個函數主要是調用了poll(),並且用讀寫超時值作爲poll()的等待超時時間。
會話超時
這裏的會話(Session)其實就是數據庫連接,在mysql內部對應於THD類。會話超時是Mysql server端纔有的機制,在客戶端沒有。實現方法是:Mysql server內部線程定期檢查所有THD會話對象的狀態,將會話超時的THD對象銷燬。