首先从ado.net的链接池开始了解,数据库操做一般是 new SqlConnection()、 Open()、 使用完后 Close(),整个过程至关耗时,特别是频繁建议套字接链接的过程。ado.net 驱动已经现实了链接池管理,Open() 等于申请链接,Close() 即归还资源。mysql
Open() 的时候有几种状况:git
一、有资源直接返回;github
二、无可用资源,且未超过池最大设置值时,建立资源并返回;redis
三、无可用资源,此时会等待设置秒数,若仍然未获取资源则报错;sql
链接的复用使性能成数倍提高,试想网站在某一时刻忽然爆增10万次,new 10万个SqlConnection对象显然会炸掉服务,建立,connect,disconnect,disponse,显然开销很大。数据库
虽然ado.net自带的链接池已经接近完美,但在某些场合还不够用,先来一个压力测试。apache
[HttpGet("vs_gen")] async public Task<object> vs_gen() { var select = Tag.Select; var count = await select.CountAsync(); var items = await select.Page(page, limit).ToListAsync(); return new { count, items }; } [HttpGet("vs_dapper")] async public Task<object> vs_dapper() { var conn = new SqlConnection("Data Source=.;Integrated Security=True;Initial Catalog=cms;Pooling=true;Max Pool Size=11"); conn.Open(); var count = await conn.ExecuteScalarAsync<int>("SELECT count(1) FROM[dbo].[tag] a"); //conn.Close(); //conn = new SqlConnection("Data Source=.;Integrated Security=True;Initial Catalog=cms;Pooling=true;Max Pool Size=11"); //conn.Open(); var items = await conn.QueryAsync("SELECT TOP 20 a.[id], a.[parent_id], a.[name] FROM[dbo].[tag] a"); conn.Close(); return new { count, items }; }
链接池最大分别为:10,11并发
使用 apache ab 命令行测试上面两个接口app
ab -c 10 -n 1000 -s 6000 测试结果差很少。异步
-c 100 时,vs_dapper直接挂了,vs_gen没影响(使用了SafeObjectPool)
实践证实ado.net过于暴露,忽然的高并发招架不住。
它是一个对象池,可用于控制任何资源紧缺的对象,使用容器化管重复使用提高性能,有序的排队获取,使用完后归还资源。
与ado.net链接池不一样的地方,SafeObjectPool 解决池用尽后,再请求不报错,进行排队等待机制,而且适用任何对象不局限于数据库链接对象。
SafeObjectPool 提供可用性检查方法,好比 redisClient 不可用时,全部Get/GetAsync都将报错,直到后台服务检查并恢复状态。
Install-Package SafeObjectPool
var pool = new SafeObjectPool.ObjectPool<MemoryStream>(10, () => new MemoryStream(), obj => { if (DateTime.Now.Subtract(obj.LastGetTime).TotalSeconds > 5) { // 对象超过5秒未活动,进行操做 } }); var obj = pool.Get(); //借 pool.Return(obj); //归还 //或者 using 自动归还 using (var obj = pool.Get()) { }
【链接池名称】状态不可用,等待后台检查程序恢复方可以使用。
SafeObjectPool.Get 获取超时(10秒),设置 Policy.IsThrowGetTimeoutException 能够避免该异常。
【链接池名称】状态不可用,等待后台检查程序恢复方可以使用。
SafeObjectPool.GetAsync 无可用资源且队列过长,Policy.AsyncGetCapacity =10000。
CheckAvailable 没法得到资源,Pool: 0/10, Get Wait: 0, GetAsync Wait: 0
var pool = new System.Data.SqlClient.SqlConnectionPool("名称", connectionString, 可用时触发的委托, 不可用时触发的委托); var conn = pool.Get(); try { // 使用 ... pool.Return(conn); //正常归还 } catch (Exception ex) { pool.Return(conn, ex); //发生错误时归还 }
var pool = new MySql.Data.MySqlClient.MySqlConnectionPool("名称", connectionString, 可用时触发的委托, 不可用时触发的委托); var conn = pool.Get(); try { // 使用 ... pool.Return(conn); //正常归还 } catch (Exception ex) { pool.Return(conn, ex); //发生错误时归还 }
var pool = new Npgsql.NpgsqlConnectionPool("名称", connectionString, 可用时触发的委托, 不可用时触发的委托); var conn = pool.Get(); try { // 使用 ... pool.Return(conn); //正常归还 } catch (Exception ex) { pool.Return(conn, ex); //发生错误时归还 }
本文由 ado.net 链接池,衍生到 SafeObjectPool,基于 SafeObjectPool 现实了 SQLServer链接池、MySQL链接池、PostgreSQL链接池。还有不少链接对象,好比redis client、rpc client、各大消息队列client,均可以封装起来。
谢谢观看,支持开源思想和奉献。
SafeObjectPool github:https://github.com/2881099/SafeObjectPool