MySQL連接超時

在負載較重的MySQL服務器上,有時你偶爾會看到一些連接超時的錯誤,諸如: Can’t connect to MySQL server on ‘mydb’(110)。如果當時你有多個連接請求,你會發現其它連接卻沒問題。這類問題開始時很不顯眼,且長時間來看幾乎可以忽略不計(註:次數不多),類似於百萬分之一的發生率,但是在服務器負載不斷加重時,可能出現的頻率將有所上升。

如果你對連接進行計時你會發現,連接一般都接近3-9秒。這個時長有時也很詭異,多年前我就曾遇到過一次,當時數據庫請求連接被重置,SYN包一直被丟棄。3秒就有一個SYN包被拋棄,9秒有兩被拋棄。如果你也碰到瞭類似情況,則有可能是你的網絡存在問題或你的數據庫服務器請求偵聽隊列溢出,你可以通過運行netstat -s命令進行檢查,可能會返回類似於:偵聽隊列的Socket發生瞭38409次溢出,38409個SYN包被丟棄,這就意味著偵聽Socket的內核緩存溢出瞭,SYN包將不得不被丟棄 — MySQL並不是在需要時就盡快接受連接。

如果發生瞭這種情況,有2個調優的地方你可以考慮。
1. Linux內核:net.ipv4.tcp_max_syn_backlog,這參數是用來設置所有Socket內核緩存大小的。我的系統缺省值為2048,其它版本可能不盡相同,如果你的連接並發較大則你可能需要將此值提高到8192左右。具體匹配情況我會在下面介紹。

2.
MySQL參數:back_log,缺省值為50。你可能需將此值設置為1000或更高。同時,你可能提高net.core.somaxconn內核參數值,這個參數是用來設置偵聽隊列的最大深度。我本人內核中此參數設置的是128,這在很多情況下會偏低。

現在我們來深入研究下這個問題。首先看看MySQL是如何接受連接的。有個主線程將接受所有請求偵聽Socket的連接。當有個新連接來到時,主線程將為新連接創建一個新的Socket,同時創建一個新的子線程或從緩存中取一個子線程來處理這個連接。站在MySQL網絡通訊基多線程這點來看,多核是有利的,但對於主線程來說,多核並沒什麼用。通常主線程接受連接會很快,但是,如果主線程因為互斥等待或為啟動新線程而等待,則偵聽隊列可能會溢出。我們來看看,如果一個數據庫一般每秒能接受1000個連接,這個值很高,但你可能會碰到更高的。因為連接請求的隨機到達特性,有時你可能會看到3000個連接並發。這種情況下,缺省的back_log(50)僅能支撐17毫秒,主線程在某些地方處理稍有停滯超過17毫秒,則有些SYN包將被丟棄。

我建議將tcp_max_syn_backlog和back_log的值調整到足夠支撐2秒的連接請求(註:連接請求2秒未超時)。比方說,正常情況下每秒有100個連接,假設峰值為3倍正常情況,則為300連接/秒,這意味著前面提到的參數至少需設到600。(註:300連接/秒 * 2秒 = 600連接/秒)

將參數設到支持2秒以上請求的意義不大,因為客戶端在3秒內未收到應答後將發出新的連接請求。

另外,如果你每秒創建瞭1000個MySQL連接,你可能有點過瞭,畢竟創建和銷毀1000個連接需使用大量的資源。考慮下使用長連接或連接池吧,至少是那種大部分連接是由應用創建的情況。

 

本文出自“IT牛奶”

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。