如何主动清空.Net数据库连接池?

运维 数据库运维
一般我们的项目中会使用1到2个数据库连接配置,同程艺龙的数据库连接配置被收拢到统一的配置中心,由DBA统一维护,业务方通过某个配置字符串拿到的是开箱即用的Connection对象。

[[417053]]

本文转载自微信公众号「精益码农」,作者小码甲。转载本文请联系精益码农公众号。

一般我们的项目中会使用1到2个数据库连接配置,同程艺龙的数据库连接配置被收拢到统一的配置中心,由DBA统一维护,业务方通过某个配置字符串拿到的是开箱即用的Connection对象。

DBA能在对业务方无侵入的情况下,给业务方切换备份数据库,之后DBA要求旧连接池必须立即被清空。

那么问题来了: 能不能立即清空.NET连接池?注意我用得是清空,而不是释放连接。

如果有同学不知道DBA做这个要求的目的,那我啰嗦一下:

应用程序不再使用旧连接时,理论上你的连接池要被完全清空,因为单纯的释放连接,只会让连接池中的Connection处于Sleep状态,依旧维持了短时间的物理连接,这个短时间其实是不必要的占用,影响了旧连接数据库的吞吐量。

连接池知识背景

回答这个问题之前, 我们还是先研究一下.NET数据库连接池。

1. .NET数据库连接池的背景

数据库连接是一个耗时的行为,大多数应用程序只使用1到几种数据库连接,为了最小化打开连接的成本,ado.net使用了一种称为连接池的优化技术。

2. .NET 数据库连接池的表现

数据库连接池减少了必须打开新连接的次数,池程序维护了数据库物理连接。

通过为每个特定的连接配置保持一组活动的连接对象来管理连接。

每当应用程序尝试Open连接,池程序就会在池中找到可用的连接,如果有则返回给调用者;

应用程序Close连接对象时,池程序将连接对象返回到池中(Sleep), 这个连接可以在下一次Open调用中重用。

看黑板,下面是这次的重点:

3. .NET是如何形成数据库连接池的?

只有相同的连接配置才能被池化,.NET为不同的配置维护了不同的连接池。

  • 相同的配置限制为:
  • 进程相同、
  • 连接字符串相同、
  • 连接字符串关键key顺序相同。

(连接字符串提供的关键字顺序不同也将被分到不同的池)。

连接池中的可用连接的数量由连接字符串Max Pool Size决定。

在一个应用程序中,有如下代码:

  1. using (SqlConnection connection = new SqlConnection(   
  2.   "Integrated Security=SSPI;Initial Catalog=Northwind"))   
  3.     {   
  4.         connection.Open(); 
  5.         // Pool A is created.   
  6.     }   
  7.    
  8. using (SqlConnection connection = new SqlConnection(   
  9.   "Integrated Security=SSPI;Initial Catalog=pubs"))   
  10.     {   
  11.         connection.Open(); 
  12.         // Pool B is created because the connection strings differ.   
  13.     }   
  14.    
  15. using (SqlConnection connection = new SqlConnection(   
  16.   "Integrated Security=SSPI;Initial Catalog=Northwind"))   
  17.     {   
  18.         connection.Open(); 
  19.         // The connection string matches pool A.   
  20.     }   

上面创建了三个Connection对象,但是只形成了两个数据库连接池。

还是以上代码,如果有两个相同的应用程序,理论上就形成了四个数据库连接池。

4. 连接池中的连接什么时候被移除?

连接池中的连接空闲4-8 分钟,池程序会移除这个连接。

应用程序下线,连接池直接被清空。

如何主动清空.NET连接池

有了以上知识背景,我们再来回顾一下DBA的要求,切换数据库连接配置的时候,清空原连接池。

.NET提供了 ClearAllPools、ClearPool静态方法用于清空连接池。

  • ClearAllPools: 清空与这个DBProvider相关的所有连接池
  • ClearPool(DBConnection conn) 清空与这个连接对象相关的连接池

很明显,我们这次要使用ClearPool(DBConnection conn) 方法。

光说不练不验证,不是我的风格。

天锤压测/queryapi 产生一个包含大量连接对象的连接池;

适当的时候,调用/clearpoolapi清空连接池。

  1. public class MySqlController : Controller 
  2.    { 
  3.        // GET: MySql 
  4.        [Route("query")] 
  5.        public string Index() 
  6.        { 
  7.            var s = "User ID=teinfra_neo_netreplay;Password=123456;DataBase=teinfra_neo_netreplay;Server=10.100.41.196;Port=3980;Min Pool Size=1;Max Pool Size=28;CharSet=utf8;"
  8.            using (var conn = new MySqlConnection(s)) 
  9.            { 
  10.                var comm = conn.CreateCommand(); 
  11.                comm.CommandText = "select count(*) from usertest;"
  12.                conn.Open(); 
  13.                var ret = comm.ExecuteScalar(); 
  14.  
  15.                comm.CommandText = "select count(*) from information_schema.PROCESSLIST WHERE HOST like  '10.22.12.245%';"
  16.                var len = comm.ExecuteScalar(); 
  17.                return $"查询结果:{ret} ,顺便查一下当前连接池的连接对象个数: {len}"
  18.            }; 
  19.        } 
  20.  
  21.        [Route("clearpool")] 
  22.        public string Switch() 
  23.        { 
  24.            var s = "User ID=teinfra_neo_netreplay;Password=123456;DataBase=teinfra_neo_netreplay;Server=10.100.41.196;Port=3980;Min Pool Size=1;Max Pool Size=28;CharSet=utf8;"
  25.            using (var conn = new MySqlConnection(s)) 
  26.            { 
  27.                conn.Open(); 
  28.                MySqlConnection.ClearPool(conn); 
  29.            }; 
  30.  
  31.            using (var conn = new MySqlConnection(s)) 
  32.            { 
  33.                conn.Open(); 
  34.                var comm = conn.CreateCommand(); 
  35.                comm.CommandText = "select count(*) from information_schema.PROCESSLIST WHERE HOST like  '10.22.12.245%';"
  36.                var len = comm.ExecuteScalar(); 
  37.                return $"之前已经清空连接池, 此次查询连接池有 {v1}  个连接对象"
  38.            } 
  39.  
  40.        } 
  41.    } 

1.压测产生大量连接对象

2. mysql数据库对比

mysql的连接数查询命令: (host是web服务器IP):

  1. select * from information_schema.PROCESSLIST WHERE HOST like '10.22.12.245%'

3. 调用/clearpoolapi,清空连接池

bingo,清空连接池的理论得到验证。

旁白

这是我在同程艺龙最近爬的比较深的坑位, 在本次实践中我们了解到:

  • NET 数据库连接池属编程语言范畴,连接池维护了物理连接
  • NET数据库连接池的定义方式:(同一进程、同一连接字符串、同一连接字符串关键key顺序一致) 被划到一个池
  • DB客户端查询当前连接数的方式

根据这个思路改造祖传代码,.NET数据获取组件SDK 已经满足了DBA的要求。

希望本文设计考量、理论+论证的行文思路对读者有所帮助, 距离上次发文一月有余,再次感谢5000+读者不离不弃。

引用链接 

[1] sql连接池(ado.net): https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

 

责任编辑:武晓燕 来源: 精益码农
相关推荐

2009-06-24 07:53:47

Hibernate数据

2009-07-29 09:33:14

ASP.NET数据库连

2009-06-26 14:41:48

ADO.NET

2010-03-18 15:09:15

python数据库连接

2011-07-29 15:11:42

WeblogicOracle数据库连接

2009-06-16 09:25:31

JBoss配置

2017-06-22 14:13:07

PythonMySQLpymysqlpool

2019-11-27 10:31:51

数据库连接池内存

2009-11-12 08:59:18

ADO.NET数据库连

2009-06-15 13:46:00

netbeans设置数据库连接池

2020-04-30 14:38:51

数据库连接池线程

2018-10-10 14:27:34

数据库连接池MySQL

2024-01-10 08:17:50

HikariCP数据库Spring

2019-12-30 15:30:13

连接池请求PHP

2010-03-18 14:55:17

Python数据库连接

2011-05-19 09:53:33

数据库连接池

2018-01-03 14:32:32

2009-07-17 13:32:49

JDBC数据库

2010-01-05 10:11:23

ADO.NET连接池

2009-08-10 17:34:42

C#数据库连接池
点赞
收藏

51CTO技术栈公众号