在使用 MySqlConnector 套件連接 mysql 的時候,偶發性的出現 MySqlConnector.MySqlException (0x80004005): Too many connections 錯誤,這裡將展示怎麼重現這個問題
測試工具與套件
c# 7.0
RoslynPad
MySqlConnector
重現方式
Step1 查詢現在的最大連線數
SHOW GLOBAL VARIABLES LIKE 'max_connections';
Step2 設定最大連線數量為99
SET GLOBAL max_connections = 99;
Step3 建立 100條連線不關閉
#r "nuget: MySqlConnector, 2.3.5"
using MySqlConnector;
using System.Data;
for (var i = 1; i <= 101; i++)
{
try
{
var db = GetDbConnection(@"Server=127.0.0.1;Database=db1;Uid=root;Pwd=123;Port=3306;MaximumPoolSize=100");
//show it
Thread.Sleep(20);
}
catch (Exception ex)
{
Console.WriteLine($"{i}, {ex}");
break;
}
}
"done".Dump();
可以發現在建立第100條連線的時候,就會馬上出現 MySqlConnector.MySqlException (0x80004005): Too many connections 的錯誤
max_connections 這個值是什麼?
這個值指的並不是單一 db 的數量,而是指所有 db 的連線數量
假設你設定的 max_connections = 500
db1 最多會有 100條連線
db2 最多會有 300條連線
db3 最多會有 300條連線
那麼就有可能會觸發到 MySqlConnector.MySqlException (0x80004005): Too many connections 的錯誤
因為在高峰的時間,三個 db 總量有可能會到 700 條連線,但 max_connections 卻只有設定 500 條
這有可能造成的結果就是
db1: 95 (在建立第 96 連線就有例外錯誤產生)
db2: 300(全部佔好佔滿)
db3: 105(在建立第 106 連線就有例外錯誤產生)
原因與過程
在 MySqlConnector 這套件裡有實作 connection pool 的功能,所以在參數上可以設定最大數量的上限,但這僅限於套件端,並不是指 mysql server 端的最大連線數量。
MySqlConnector 上的參數設定是這樣的,若不給值的話,預設是 100條 connection pool
AddOption(MaximumPoolSize = new(
keys: new[] { "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" },
defaultValue: 100u));
connection pool 怎麼重覆使用連線,也可以參考 GetSessionAsync
https://github.com/mysql-net/MySqlConnector/blob/master/src/MySqlConnector/Core/ConnectionPool.cs#L21
以上面的例子來說在套件上,在程式連接端若設定 100條連線,但是在 mysql 的最大連線數量上只設 99條,就可以觸發這個問題
MySqlConnector在接收指令要建立第 100條連線的時候,認為 pool 是還可以再放一個,然而向 mysql 請求的時候,這個連線並沒有辦法被建立
解決方式
(1) 建立完的連線用完就要釋放掉,最簡單的就用 using 的方式把建立連線的部份包起來
using (var db = GetDbConnection())
{
var sql = "Insert into xxx";
db.QueryAsync(sql);
}
(2) 取決於程式碼在尖峰時間調整使用連線數,而設定 mysql 最大的連線量,但要注意的是這個數量指的是 mysql server 的連線量,也就是所有 db 的連線量總合,而不是單一 db 的連線量