黑马程序员技术交流社区

标题: 数据库连接池 [打印本页]

作者: fxtahe    时间: 2018-7-19 12:53
标题: 数据库连接池
[size=1.25]连接池对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决我们的问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。
为什么使用连接池
[size=1.25]连接,是我们的编程语言与数据库交互的一种方式。我们经常会听到这么一句话“数据库连接很昂贵“。有人接受这种说法,却不知道它的真正含义。因此,下面通过实例解释它究竟是什么。
String connUrl ="jdbc:mysql://your.database.domain/yourDBname";  
Class.forName("com.mysql.jdbc.Driver");  
Connection con =DriverManager.getConnection (connUrl);
[size=1.25]当我们创建了一个Connection对象它在内部执行了  1.“DriverManager”检查并注册驱动程序;2.“com.mysql.jdbc.Driver”就是我们注册了的驱动程序,它会在驱动程序类中调用“connect(url…)”方法。3.com.mysql.jdbc.Driver的connect方法根据我们请求的“connUrl”,创建一个“Socket连接”,连接到IP为“your.database.domain”,默认端口3306的数据库。4.创建的Socket连接将被用来查询我们指定的数据库,并最终让程序返回得到一个结果。简单的获取一个连接,系统却要在背后做很多消耗资源的事情,大多时候,创建连接的时间比执行sql语句的时间还要长。
[size=1.25]用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机。
[size=1.25]数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
需要注意的问题
[size=1.25]1、并发问题为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)、lock(C#)关键字即可确保线程是同步的。2、事务处理我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。我们知道当2个线程公用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使Connection类提供了相应的事务支持,可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有2个线程都在进行事务操作而引起的。为此我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。3、连接池的分配与释放连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出);如果没有就抛出一个异常给用户,List中连接是否可以被分配由一个线程来专门管理。4、连接池的配置与维护连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。
DBCP: DataBase Connection Pool
[size=1.25]DBCP是Apache软件基金会组织下的一个开源数据库连接池的实现,使用DBCP连接池需要增加两个jar文件:  Commons-dbcp.jar:连接池的实现  Commons-pool.jar:连接池实现的依赖库  TomCat默认使用的连接池组件,也是DBCP,不过是被整合到了tomcat-dbcp.jar,我们在Tomcat解压文件的lib目录下可以找到  
[size=1.25]
[size=1.25]相较于C3P0和Druid,DBCP连接池的高并发和稳定性都相对较弱,一般不在使用
如何使用
[size=1.25]首先要便于管理我们DBCP配置参数我们新建一个properties文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/test
username=root
password=root
[size=1.25]接着我们可以编写一个工具类
public class DBCPUtils{
    private DataSource datasource=null
   
    static{
        try{
            InputStream is = DBCPUtils.class.getClassLoader().getResourceAsStream("db.propertues");
            Properties prop = new Properties();
            prop.load(is);
            datasource = BasicDataSourceFactory.createDataSource(prop);
        }catch(Exception e){
            e.printStackTrace();
        }
        public static Connection getConnection()throws SQLException{
            return datasource.getConnection();
        }
    }
}
[size=1.25]使用

Connection conn = null;
PreparedStatement psta = null;
ResultSet rs = null;

try{
    conn = DBCPUtils.getConnection();
    ...
}catch(Exception e){
    e.printStackTrace();
}finally{
    ...
}C3P0
[size=1.25]c3p0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。c3p0一般是与Hibernate,Spring等框架一块使用的,当然也可以单独使用。dbcp没有自动回收空闲连接的功能,c3p0有自动回收空闲连接功能。使用C3P0需要导入c3p0的jar包
如何使用
[size=1.25]首先我们需要编写c3p0-config.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
        <property name="user">root</property>
        <property name="password">root</property>
    </default-config>
</c3p0-config>
[size=1.25]创建C3P0工具类

public class C3P0Utils{
    private static ComboPooledDataSource datasource = new ComboPooledDataSource();
   
    public static Connection getConnection() throws SQLException{
        return datasource.getConnection();
    }
}
[size=1.25]使用

Connection conn = null;  
PreparedStatement psta = null;  
ResultSet rs = null;  
try{  
    //获取数据库连接  
    conn = C3P0Utils.getConnection();  
    ……  
}catch (Exception e) {  
    e.printStackTrace();  
}finally{  
    //释放资源
    ...
} Druid连接池
[size=1.25]为监控而生的数据库连接池,它是阿里巴巴开源平台上的一个项目。Druid是Java语言中最好的数据库连接池,Druid能够提供强大的监控和扩展功能(黄婆卖瓜,自卖自夸哈哈)。它可以替换DBCP和C3P0连接池。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。它与其他数据库连接池的不同是因为 1.其可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。 2.可以对数据库密码进行加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。 3.SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。 4.扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。 在项目中使用Druid非常简单,只要修改下配置文件就可以了

public calss DruidUtils{
    private DruidUtils(){}
   
    private static DataSource dataSource;
   
    static {
        try{
            Properties prop = new Properties();
            InputStream is = DruidUtils.class.getClassLoader().getSourceAsStream("druid.properties");
            prop.load(is);
            dataSource = DruidDataSourceFactory.createDataSource(prop);
        }catch(Exception e){
            e.printStackTrance();
        }
    }
}
[size=1.25]
DataSource 接口
[size=1.25]DataSource 接口为 JavaBeans Activation Framework 提供任意数据集合的抽象。它提供该数据的类型,并在适当的时候以 InputStream 和 OutputStream 的形式提供对该数据的访问。
[size=1.25]

建立一个连接的首选方法是使用DataSource接口,这使代码更具可移植性,允许更容易的程序维护,同时还允许Connection对象加入分布式事务处理以及透明的连接池,当应用程序的主要目标是性能的时候,连接池就是一个非常好的实现思想。重用Connection对象的能力消除了在每次有一个连接请求发生的时候,经常需要创建一个新的物理连接的需求分布式事务允许创建可以在健壮企业体系结构中运行良好的应用程序,在这种体系结构下需要应对大量并发数据库任务的发生可能性。
DataSource接口使用java命名以及目录接口(Java Naming and Directory Interface,JNDI)来存储用于数据源的一个逻辑名,而不是使用完全限定格式的驱动程序名称来连接一个数据源。




一、DataSource接口是一个更好的连接数据源的方法:
  JDBC1.0是原来是用DriverManager类来产生一个对数据源的连接。JDBC2.0用一种替代的方法,使用DataSource的实现,代码变的更小巧精致,也更容易控制。
  一个DataSource对象代表了一个真正的数据源。根据DataSource的实现方法,数据源既可以是从关系数据库,也电子表格,还可以是一个表格形式的文件。当一个DataSource对象注册到名字服务中,应用程序就可以通过名字服务获得DataSource对象,并用它来产生一个与DataSource代表的数据源之间的连接。

  关于数据源的信息和如何来定位数据源,例如数据库服务器的名字,在哪台机器上,端口号等等,都包含在DataSource对象的属性里面去了。这样,对应用程序的设计来说是更方便了,因为并不需要硬性的把驱动的名字写死到程序里面去。通常驱动名字中都包含了驱动提供商的名字,而在DriverManager类中通常是这么做的。如果数据源要移植到另一个数据库驱动中,代码也很容易做修改。所需要做的修改只是更改DataSource的相关的属性。而使用DataSource对象的代码不需要做任何改动。
  
  由系统管理员或者有相应权限的人来配置DataSource对象。配置DataSource,包括设定DataSource的属性,然后将它注册到JNDI名字服务中去。在注册DataSource对象的的过程中,系统管理员需要把DataSource对象和一个逻辑名字关联起来。名字可以是任意的,通常取成能代表数据源并且容易记住的名字。在下面的例子中,名字起为:InventoryDB,按照惯例,逻辑名字通常都在jdbc的子上下文中。这样,逻辑名字的全名就是:jdbc/ InventoryDB。

  一旦配置好了数据源对象,应用程序设计者就可以用它来产生一个与数据源的连接。下面的代码片段示例了如何用JNDI上下文获得一个一个数据源对象,然后如何用数据源对象产生一个与数据源的连接。开始的两行用的是JNDI API,第三行用的才是JDBC的API:
   Context ctx = new InitialContext();
       DataSource ds = (DataSource)ctx.lookup("jdbc/InventoryDB");
       Connection con = ds.getConnection("myPassword", "myUserName");

  在一个基本的DataSource实现中,DataSource.getConnection方法返回的Connection对象和用DriverManager.getConnection方法返回的Connection对象是一样的。因为DataSource提供的方便性,我们推荐使用DataSource对象来得到一个Connection对象。我们希望所以的基于JDBC2.0技术的数据库驱动都包含一个基本的DataSource的实现,这样就可以在应用程序中很容易的使用它。
[size=1.25]11.3.2. SmartDataSource接口
[size=1.25]
[size=1.25]SmartDataSource是DataSource 接口的一个扩展,用来提供数据库连接。使用该接口的类在指定的操作之后可以检查是否需要关闭连接。 该接口在某些情况下非常有用,比如有些情况需要重用数据库连接。
[size=1.25]11.3.4. SingleConnectionDataSource类
[size=1.25]
[size=1.25]SingleConnectionDataSource是SmartDataSource接口 的一个实现,其内部包装了一个单连接。该连接在使用之后将不会关闭,很显然它不能在多线程 的环境下使用。
[size=1.25]
[size=1.25]当客户端代码调用close方法的时候,如果它总是假设数据库连接来自连接池(就像使用持久化工具时一样), 你应该将suppressClose设置为true。 这样,通过该类获取的将是代理连接(禁止关闭)而不是原有的物理连接。 需要注意的是,我们不能把使用该类获取的数据库连接造型(cast)为Oracle Connection之类的本地数据库连接。
[size=1.25]
[size=1.25]SingleConnectionDataSource主要在测试的时候使用。 它使得测试代码很容易脱离应用服务器而在一个简单的JNDI环境下运行。 与DriverManagerDataSource不同的是,它始终只会使用同一个数据库连接, 从而避免每次建立物理连接的开销。
[size=1.25]
[size=1.25]11.3.5. DriverManagerDataSource类
[size=1.25]
[size=1.25]DriverManagerDataSource类实现了 SmartDataSource接口。在applicationContext.xml中可以使用 bean properties来设置JDBC Driver属性,该类每次返回的都是一个新的连接。
[size=1.25]
[size=1.25]该类主要在测试以及脱离J2EE容器的独立环境中使用。它既可以用来在application context中作为一个 DataSource bean,也可以在简单的JNDI环境下使用。 由于Connection.close()仅仅只是简单的关闭数据库连接,因此任何能够获取 DataSource的持久化代码都能很好的工作。不过使用JavaBean风格的连接池 (比如commons-dbcp)也并非难事。即使是在测试环境下,使用连接池也是一种比使用 DriverManagerDataSource更好的做法。
[size=1.25]
[size=1.25]11.3.6. TransactionAwareDataSourceProxy类
[size=1.25]
[size=1.25]TransactionAwareDataSourceProxy作为目标DataSource的一个代理, 在对目标DataSource包装的同时,还增加了Spring的事务管理能力, 在这一点上,这个类的功能非常像J2EE服务器所提供的事务化的JNDI DataSource。


作者: 奥斯托洛夫斯基    时间: 2018-8-9 17:33

作者: O-limin    时间: 2018-8-9 17:34

作者: 项老师    时间: 2018-8-9 17:37

作者: TomGe    时间: 2018-8-9 17:39

作者: 天亮1    时间: 2018-8-9 17:42

作者: hguilin    时间: 2018-8-9 17:55
6666
作者: shengweiyun    时间: 2018-8-9 18:12
     
作者: 黑马啸西风    时间: 2018-8-9 18:41

作者: wishyouwerehere    时间: 2018-8-9 18:50

作者: 合肥黑马9期    时间: 2018-8-9 18:56

作者: Joe0428    时间: 2018-8-9 23:01
,,,,,
作者: 鸟语花香    时间: 2018-8-10 09:59

作者: ZC971227    时间: 2018-8-14 11:20
希望楼主以后多发一点此类的分享,蟹蟹
作者: 曹瑞银    时间: 2018-8-14 11:20

作者: ms1249615386    时间: 2018-8-14 11:21
写的不错
作者: 595156223    时间: 2018-8-14 11:22

作者: pfjava6    时间: 2018-8-14 11:22
继续努力,加油加油
作者: 日常空大    时间: 2018-8-14 11:22
腻害腻害
作者: 梧桐花开    时间: 2018-8-14 11:24





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2