在使用 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/