MySQL 连接池优化配置

当初使用NestJS搭建中台系统时,并没有做太多的优化设置,毕竟面向的是B端用户,就没几个访问量。直到某次使用了批量创建脚本,出现了程序重启的情况,具体报错ConnectionAcquireTimeoutError [SequelizeConnectionAcquireTimeoutError]: Operation timeout,从而发现了Sequelize的默认配置并不能满足并发场景。

MySQL连接池

在使用MySQL数据库时,为了提高系统性能和响应速度,我们通常会使用连接池来管理数据库连接。连接池是一种数据库连接的缓存机制,它可以在系统启动时创建一定数量的数据库连接,并将这些连接保存在一个连接池中。当系统需要访问数据库时,可以从连接池中获取一个可用的连接,用完之后再将连接释放回连接池。

连接池的大小对系统的性能和稳定性有着重要的影响。如果连接池的大小设置得过小,可能导致系统并发访问量大时无法获取到可用的数据库连接,从而引起连接超时或者连接请求被拒绝的错误。相反,如果连接池的大小设置得过大,会占用过多的系统资源,导致系统性能下降。

我们可以通过以下代码查看MySQL服务的最大连接数。

SHOW VARIABLES LIKE 'max_connections';

返回结果一般都有5K连接数量,在并发量高的情况下,可能需要根据实际的硬件配置做相应的调整。

MySQL客户端

虽然服务器的最大连接数很大,但是客户端一般都不能直接设置这么大,因为一般情况下会造成资源浪费。网上关于 MySQL 连接分析的文章很多,我们这里只放结论。

我们一般使用由 PostgreSQL 提供的计算公式设置连接池大小,这个公式公认也可以广泛地应用于大多数数据库产品。

连接数 = ((核心数 * 2) + 有效磁盘数)

注意,其中的核心数不应包含超线程(hyper thread),即使打开了 hyperthreading 也是。如果活跃数据全部被缓存了,那么有效磁盘数是 0,随着缓存命中率的下降,有效磁盘数逐渐趋近于实际的磁盘数。需要注意的是,这个公式针对 SSD 的效果尚未有分析。

按这个公式,你的 4 核数据库服务器的连接池大小应该为((4 * 2) + 1) = 9。取个整就算是是 10 吧。是不是觉得太小了?跑个性能测试试一下,我们保证它能轻松搞定3000用户以6000TPS的速率并发执行简单查询的场景。如果连接池大小超过10,你会看到响应时长开始增加,TPS开始下降。

公理:你需要以等待代替连接

如果你有 1w 并发,设置一个连接数 1w 的连接池基本等于失了智。1k 仍然很恐怖,即是 100 也太多了。

你可以设置一个 10 来个连接的连接池,然后让剩下的业务线程都在队列里等待。连接池中的连接数量应该等于你的数据库能够有效同时进行的查询任务数(通常不会高于2*CPU核心数,即满足上述公式)。