在一次数据库连接问题中,我们坚持认为数据库连接数过万是阿里云rds的问题,但后来阿里云提供了当时的数据库连接情况,让我们动摇了自己的想法。

上面这5个帐户产生了10030个数据库连接,当看前4个帐户(产生了9511个连接)的名称时,我们打了一个寒颤 —— 这些都是运行 linux 上的 asp.net core 站点。。。这不是巧合,其中必有蹊跷。随后,我们观察了主备库切换后的 rds 中数据库连接情况。有一个运行在 linux 上的 asp.net core 站点,用了3台服务器,却产生了1528个数据库连接。
<b>select * from sys.sysprocesses </b>
<b>where loginame='xxx'</b>
重启其中1台服务器上的站点,连接数立马从1528降到了391。什么情况?数据库连接池发飙了?继续观察,当前数据库中大量的连接都是由运行在 linux 上的 asp.net core 站点产生的,而且会随着时间的推移保持增长。
数据库连接泄漏了,这还是第1次遇到!可我们在 aps.net core 应用中所有的数据库操作都用的是entity framework core,不存在没有及时关闭数据库连接的情况,唯一可以怀疑的对象是在 system.data.sqlclient 中实现的 ado.net 数据库连接池。
数据库连接池究竟出什么状况了?我们在数据库连接字符串中没有另外设置连接池,用的是默认设置(min_pool_size = 0; 与 max_pool_size = 100;)。而且更奇怪的是 max_pool_size 的限制没起作用,不然只会报下面的错误,不会连接数一直增长。
<b>timeout expired. the timeout period elapsed prior to obtaining a connection from the pool. this may have occurred because all pooled connections were in use and max pool size was reached.</b>
我们想来想去,唯一能想得通的解释是 .net core 的数据库连接池发生了这样的状况 —— 连接池中已经创建的连接无法被重用,不仅如此,而且它们直接被 sqlclient 给无视了,都没有被计算在 pool size 中,所以根本触发不了 max_pool_size 的限制,造成连接无限制,任由 sqlclient 建。更要命的是,这些被无视的连接却一直在保持着与数据库的连接。于是,连接泄露成了命中注定。
在有了这个唯一想得通的猜测后,我们今天开始在测试环境中进行验证。部署一个 asp.net core 站点,创建一个专用数据库连接帐户,然后用下面的 sql 语句查看数据库连接是否被重用,同时在测试服务器用 tcpdump 进行抓包,并且分别用阿里云 rds 与我们自己搭建的 sql server 服务器进行测试。
<b>select * from sys.sysprocesses where loginame='测试专用帐户'</b>
如果连接池正常工作,第1次访问,新建所需的数据库连接;第2次访问同样的页面,应该重用已有的数据库连接,不会创建新的数据库连接。开始测试时,不管连接阿里云 rds 还是我们自己的 sql server,连接池都工作正常,连接能被重用。后来分析了一下,虽然生产环境中连接数一直在增长,但增长速度不是很快,可能问题的发生需要一定的时间间隔,或许连接闲置超过一定时间之后才不会被重用。
于是,我们间隔了10分钟左右进行访问测试,问题重现了!比如其中的一次测试,同一个页面第1次访问,产生了5个连接;过10分钟左右再访问,会新建3个连接变成8个连接;再过10分钟左右访问,连接增长到11个。这种连接不能被重用的情况通过 tcp 抓包也可以看出来。如果在很短的时间内访问,连接数保持不变(连接被重用)。
这个问题不仅在阿里云 rds (sql server 2008 r2)可以重现,而且在我们自己搭建的 sql server 2014 也能重现,问题的真相随之水落石出:数据库连接数过万问题不是阿里云 rds 的问题,而是 .net core 中 system.data.sqlclient 的连接池在 linux 上的实现问题,<b>我们错怪了阿里云,轻信了微软</b>。这是我们使用阿里云以来对阿里云最大的一次误会,这是我们 .net core 迁移过程中遇到的最大的一个坑。为什么最近才出现这个问题?是因为我们最近将更多站点迁移到了 asp.net core ,而且将之前一些跑在 windows 上的 asp.net core 站点切换到了 linux 。
如何解决这个问题?我们会察看一下 system.data.sqlclient 的实现代码,看能否找到实现层面的线索。阿里云会进一步验证这个问题,如果确认是微软实现上的问题,会与微软沟通解决。我们在 windows 上进行对比测试发现,在 windows 上连接池中闲置的数据库连接过段时间会被自动关闭,与上面 linux 同样的测试场景,间隔10分钟后查看,数据库连接全消失了。
<b>来源:博客园</b>