一直觉得数据库连接池是一个很神秘的东西,现在来自己动手模拟一个数据库连接池。
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
上边是复制百度百科的(有事问百度),好了废话不多说,开工。
要实现数据库连接池要求有:
- 池的大小(最大、最小);
- 数据库连接的空闲时间设置;
- 连接字符串;
- 从池中取数据库连接;
- 释放数据连接到池中;
- 连接销毁。
现在数据库连接池的定义基本架构完成了
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data.SqlClient;
- using System.Threading;
- namespace Young.PoolDemo
- {
- public sealed static class ConnectionPool
- {
- //存放数据库连接的 池
- private static Dictionary<Guid, SqlConnection> pool = new Dictionary<Guid, SqlConnection>();
- private static int minSize;
- private static int maxSize;
- private static readonly string ConnectionStr;
- private static int lifeSpan;
- /// <summary>
- /// 在数据库连接池中的生命周期
- /// </summary>
- public static int LifeSpan
- {
- get { return lifeSpan; }
- set { lifeSpan = value; }
- }
- /// <summary>
- /// 数据库连接字符串
- /// </summary>
- public string PoolConnectionString
- {
- get { return ConnectionStr; }
- }
- /// <summary>
- ///数据库连接池中最大连接数目
- /// </summary>
- public int MaxSize
- {
- get { return maxSize; }
- }
- /// <summary>
- /// 数据库连接池中最小连接数
- /// </summary>
- public int MinSiz
- {
- get { return minSize; }
- }
- /// <summary>
- /// Get Connection from ConnectionPool
- /// </summary>
- /// <returns>Connection</returns>
- public static SqlConnection GetConnection()
- {
- return null;
- }
- /// <summary>
- /// Remove connection from Connection Pool.
- /// </summary>
- /// <param name="connection">The will be removed connection.</param>
- static void RemoveConnection(SqlConnection connection)
- {
- }
- }
- }
但是按照数据连接的定义应该具有优先选择空闲资源,和排队等待机制。
这又该如何实现呢?
优先选择空闲资源,自然可以实现。我们可以遍历池中的数据然后判断其状态时候可用。排队等在机制改如何实现呢,都使用这个数据库连接池其线程安全又何在?
不要着急。按照普通的变成风格,我们在使用完数据连接这中资源会在finally中将其释放掉。为了方便对数据库连接的控制,我们可以将其二次封装增加一些特别的地方。如可以实现IDisposable接口,在Dispose方法中做些特别的处理,使他可以完成数据库连接状态的更改,并实现自动回收机制。那么我们开始定义自己的 Connetion 类。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data.SqlClient;
- namespace Young.PoolDemo
- {
- internal delegate void BackToPool(Connection connection);
- public class Connection : IDisposable
- {
- internal event BackToPool BackToPoolEvent;
- private SqlConnection connection;
- private bool isBusy = false;
- private Guid connectionGuid;
- /// <summary>
- /// Connection的唯一性标识
- /// </summary>
- public Guid ConnectionGuid
- {
- get { return connectionGuid; }
- set { connectionGuid = value; }
- }
- /// <summary>
- /// 数据库连接是否
- /// </summary>
- internal bool IsBusy
- {
- get { return isBusy; }
- set { isBusy = value; }
- }
- /// <summary>
- /// 数据库连接
- /// </summary>
- public SqlConnection DBConnection
- {
- get { return connection; }
- set { connection = value; }
- }
- public void Dispose()
- {
- lock (this)
- {
- if (DBConnection != null && BackToPoolEvent != null)
- {
- isBusy = false;
- BackToPoolEvent(this);
- }
- }
- }
- }
- }
在这里我定义了一个事件BackToPoolEvent,是实现了设计模式中的订阅者模式。用Dispose去显示的通知订阅者数据库连接对象已经空闲,同时也能通知等待队列中的线程去线程池中获取数据库连接。
到现在,整个模拟基本完成。剩下的就是等待机制,和线程安全。 把上边代码中的SqlConnection 改成 自定义的Connection类,然后就是方法填写了。我们在创建Pool中创建Connection的时候需要注意的是给增加订阅者(其实就是显现Connection的BackToPoolEvent事件)。在订阅的方法中我们可以使用计时器定时销毁Connection对象。Connection类几乎就没有什么改动了,多了一个公共的属性
- public AutoResetEvent ConnectionResetEvent = new AutoResetEvent(false);
在Dispose方法中,增加了这样一句代码
- ConnectionResetEvent.Set();
还有一个排队等待机制没有实现,这就是我们为什么在Connection中定义一个AutoResetEvent。在Pool中我们不单要维护所有的Connection,还要维护它的ConnectionResetEvent属性。当我们需要排队等待的时候就可以使用WaitHandler的WaitAny方法实现排队等待机制。
相关的多线程知识 百度一下吧。别忘了注意线程安全lock lock
转载于:https://blog.51cto.com/official/687742