連接 mysql 時遇到 Too many connections 的例外錯誤

在使用 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));


原始碼可以參考
https://github.com/mysql-net/MySqlConnector/blob/master/src/MySqlConnector/MySqlConnectionStringBuilder.cs#L1132

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 的連線量

參考資料
https://github.com/mysql-net/MySqlConnector/