EFCore连接池的坑 差点晚年不保
长话短说
上个月公司上线了一个物联网数据科学项目,我主要负责前端接受物联网事件,并提供 参数下载。
webapp 部署在Azure云上,参数使用Azure SQL Server存储。 最近从灰度测试转向全量部署之后,日志时常收到:
SQL Session超限报错。
19/12/18 20:41:18 [Error].[Microsoft.EntityFrameworkCore.Query].[][0HLS3MS83SC3K:00000004].[http://localhost/api/v1/soc-prediction-model/all].[].[GetModeParameters]
An exception occurred while iterating over the results of a query for context type 'Gridsum.SaicEnergyTracker.CarModelContext'.
Microsoft.Data.SqlClient.SqlException (0x80131904): Resource ID : 2. The session limit for the database is 300 and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
Changed database context to 'saic-carmodel'.
Changed language setting to us_english.
at Microsoft.Data.ProviderBase.DbConnectionPool.CheckPoolBlockingPeriod(Exception e)
at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at Microsoft.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
排查
我在Azure上使用的是 SQL Server Basic Edition(好歹也是付费版),全量发布至今,日均SQL访问次数约为10000,查询了Azure SQL的使用限制文档:
一句话: 付费级别和计算资源大小决定了 Azure SQL最大会话数和请求数。要缓解,要么升级硬件资源,要么优化查询利用率。
查看使用EFCore访问SQL Server的过程, 也是官方默认用法:
- 在依赖注入框架 注册一个自定义的 DbContext类型
- 在 Controller 构造函数中获取 DbContext实例
这意味着每次请求都会创建一个DbContext实例, 可以想象到
主: 频繁创建和销毁DbContext 实例,影响App Service自身性能。
次: 在急缺CPU资源下,连接会话来不及释放,会话数不断累积,最终某时刻会超过Azure的会话限制数。
EFCore2.0 为DbContext引入新的注册方式, 能够透明的注册一个 DbContext实例池:
services.AddDbContextPool<CarModelContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SQL")));
- 一如既往支持lambda方式注册连接字符串
- 默认的DbContext Pool实例数量为 128
- 每次使用完DbContext不会释放对象,而是重置并回收到DBContextPool
Web程序中通过重用池中DbContext实例可增加高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点, 这也是EFCore2.0 其中一个性能亮点。
这么重要的使用方式竟然不在 Microsoft doc 默认Demo中推荐使用,真是一个坑。

修改代码重新部署之后,历经几天测试,暂时未出现最开始的SqlException异常。
验证
回过头随机验证SQL Server 有连接的会话数:
SELECT DEC.session_id, DEC.protocol_type, DEC.auth_scheme,
DES.login_name, DES.login_time
FROM sys.dm_exec_sessions AS DES
JOIN sys.dm_exec_connections AS DEC
ON DEC.session_id = DES.session_id;

总结
① 提示EFCore2.0 新推出的DbContextPool 特性, 提高App Service 性能。
② 尝试使用SQL Server 内置脚本验证当前有效连接的 会话数
--------------------------------------20191222 更新------------------------
查阅资料, DbContext 确实是EFCore连接会话的概念,EFCore2.0推出的DbContextPool 通过减少DBContext频繁实例化、销毁的成本来提高程序性能,
概念上类似 Ado.Net原生的连接池的概念(Ado.NET连接池是更重更底层的资源, 效果会更好)。
至于SQL查询吞吐量,DbContextPool 并没有很直接的证据能证明对SQL吞吐量有改善,目前的证据显示DbContextPool 因为app 性能提升,前端处理请求的吞吐量有20% 提升。
鉴于没有实际证据证明对SQL查询吞吐量有改善,本文不再将 数据库底层的改善直接归功于使用DbContextPool, 因此本文删除了上面的关联关系,但是DbContextPool 还是应该作为默认实践。
+ https://github.com/aspnet/EntityFrameworkCore/issues/9426
+ https://github.com/aspnet/EntityFrameworkCore/issues/10125
+ https://stackoverflow.com/questions/48443567/adddbcontext-or-adddbcontextpool
+ https://www.mssqltips.com/sqlservertip/5507/understanding-and-using-sysdmexecsessions-in-sql-server/
EFCore连接池的坑 差点晚年不保的更多相关文章
- 阿里Druid连接池的坑。。
Druid的坑 当查询数据库的Clob转换为Oracle Clob类型的时候. java.lang.ClassCastException: com.alibaba.druid.proxy.jdbc.C ...
- 记录关于使用ADO.NET 连接池连接Oracle时Session信息不更新的坑
最近的一个项目中,由于界面查询的数据量比较大,关联的表比较多,有些数据查出来需要临时保存起来供后面的查询使用,于是想到了用oracle的临时表来实现这个需求.大家都知道,oracle的临时表有两种:事 ...
- weblogic连接池过小导致TPS呈周期性跳坑现象
利用晚上时间跑个12小时稳定性,第二天发现TPS曲线图成了这个样子. 排查步骤: 1.观察TPS图发现,几乎每两个小时TPS掉一次坑,是周期性的,而且TPS有掉到0的现象.LR上也有失败的交易,猜想是 ...
- DB数据源之SpringBoot+MyBatis踏坑过程(四)没有使用连接池的后果
DB数据源之SpringBoot+MyBatis踏坑过程(四)没有使用连接池的后果 liuyuhang原创,未经允许禁止转载 系列目录连接 DB数据源之SpringBoot+Mybatis踏坑过程实 ...
- DB数据源之SpringBoot+MyBatis踏坑过程(五)手动使用Hikari连接池
DB数据源之SpringBoot+MyBatis踏坑过程(五)手动使用Hikari连接池 liuyuhang原创,未经允许禁止转载 系列目录连接 DB数据源之SpringBoot+Mybatis踏坑 ...
- DB数据源之SpringBoot+MyBatis踏坑过程(七)手动使用Tomcat连接池
DB数据源之SpringBoot+MyBatis踏坑过程(七)手动使用Tomcat连接池 liuyuhang原创,未经允许禁止转载 系列目录连接 DB数据源之SpringBoot+Mybatis踏坑 ...
- Druid连接池默认配置和坑
一.公司默认配置 ds_0: !!com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: ...
- 关于PHP连接池扩展php-cp遇到的那些坑
php-cp是国内大神写的php第三方扩展,具体就不用多说了,细读https://github.com/swoole/php-cp,下面来说说今天安装方法. 环境:CentOS7.2.1511 由于本 ...
- java连数据库和数据库连接池踩坑日记(一)-------oracle连接的一些问题
最近接触oracle有点多,同时也在配置数据库连接池,坑也就踩多了,记录下. 事情还没有结束,没时间记录问题,很多事情都忘了,过了国庆再写的话可能就真的全忘了吧……而且不单单是数据库问题,还有一些数据 ...
随机推荐
- jdbc-mysql测试例子和源码详解
目录 简介 什么是JDBC 几个重要的类 使用中的注意事项 使用例子 需求 工程环境 主要步骤 创建表 创建项目 引入依赖 编写jdbc.prperties 获得Connection对象 使用Conn ...
- [UWP]用Win2D和CompositionAPI实现文字的发光效果,并制作动画
1. 成果 献祭了周末的晚上,成功召唤出了上面的番茄钟.正当我在感慨"不愧是Shadow大人,这难道就是传说中的五彩斑斓的黑?" "那才不是什么阴影效果,那是发光效果.& ...
- MySQL数据每日备份
1.window下通过命令方式 @echo offset "Ymd=%date:~,4%-%date:~5,2%-%date:~8,2%%time:~0,2%%time:~3,2%" ...
- 在UEFI+GPT下使用rEFind实现Win10 + Kali2.0 双引导
转载自:在UEFI+GPT下使用rEFind实现Win10 + Kali2.0 双引导 https://www.linuxidc.com/Linux/2016-07/133717.htm
- 自学python day 10 函数的动态参数、命名空间、作用域
作业提升: s为字符串 s.isalnum() 所有字符都是字母或者数字 s.isalpha() 所有字符都是字母 s.isdigit() 所有字符否是数字 2. for i in range(1,1 ...
- android clipChildren 的使用与遇到的困难
案例 在一次我写画板模块的时候,布局比较普通,但是需要子元素溢出父元素.其中一小块布局如下所示: 红色部分需要溢出,这个时候我想到了clipChildren. clipChildren 就是说我可以不 ...
- Virtualbox修改虚拟机分配内存的大小
起因:因为虚拟机刚开始分配的内存太小,导致太卡, 解决方法:修改虚拟机分配内存的大小 方法一:必须在关闭ubuntu的前提下进行,否则无法修改 点击设置 系统选项 主板中的内存大小 之后开启即可 方法 ...
- String s = "a";与String s = new String("a")的区别
String s1 = "a" 时,首先会在字符串常量池中查找有无 “a” 这个对象. 若没找到,就创建一个 "a" 对象, 然后,以 s1 为它的引用.若在字 ...
- k8s 上部署 Redis 三主三从 集群
目录 介绍 为什么要使用Redis? 什么是Redis群集? 在Kubernetes中部署Redis集群 从 GitHub 上下载: 创建pv 创建statefulset 创建service 初始化 ...
- Docker harbor 安装和基础操作
目录 简介 离线安装 配置文件 安装 查看 访问测试 及 简单操作 创建一个用户 创建一个测试仓库 创建测试仓库 测试上传和下载镜像 上传 下载镜像测试 简介 Docker容器应用的开发和运行离不开可 ...