#数据库连接池 ##建立连接 通过conncetion = DrvierManager.getConnection()
获取数据库连接,这段代码通过对应的JDBC驱动,完成了客户端与服务器的大量交互。由于数据库连接经过4次,才能连接成功。则在复杂的网络环境下,建立数据连接的网络开销比较高。 ###MySQL获取数据库连接
- 1.请求建立连接
MySQL客户端向MySQL服务器发起请求建立连接 - 2.发送随机密码种子
服务器端会随机生成一组密码种子返回给客户端 - 3.发送加密密码
客户端利用密码种子和连接数据库的密码,按照约定的加密算法,可以计算得到加密的密码,然后在将这个密码发送到MySQL服务器端进行验证 - 4.连接建立成功
MySQL服务器经过对加密密码的验证,连接建立成功
##多线程数据库访问 在实际业务场景中,多个用户访问Java应用程序,我们会启动线程去处理用户请求,如果要访问数据库,则我们需要创建connection对象,建立到后端数据库的物理连接,在SQL执行结束之后,随着close方法结束,数据库连接被销毁,线程也会被释放。
当用户再次访问时,会重复这个过程,这个过程花费大量的时间建立连接。
##连接复用 由于建立连接需要花费大量时间,我们可以使用数据库连接池对连接进行重复使用。每个线程在使用数据库连接后并不是立即销毁。而是把数据库连接交给下一个需要访问数据库的线程。多个线程共用相同的物理连接。实现连接的复用。
我们以连接池的形式来管理数据库连接,每个需要访问数据库的线程,需要从连接池中租借数据库连接。使用完毕以后,在归还给连接池,这样我们就可以实现连接的重复使用。避免每次访问数据库都要建立数据库连接。
我们从创建数据库连接改变为租借数据库连接!
##建立连接 连接池存在的第二个重要原因,数据库服务端在处理我们的数据库请求时,都会在服务器端分配一定的资源,比如说内存用来保存数据库查询的结果。在请求处理结束以后,这些资源也会被释放,服务器端的资源总是有限的,不可能无限制的去分配。当同时有多个数据库请求去访问数据库时,服务器端能够处理的连接数是有限制的。当超过最大可分配的资源时,就会出现服务器宕机的问题。为了限制并发访问的连接数。数据库服务器端一般会设置最大连接数。如果超过最大连接数,就会抛出too many connection的异常。
##限制连接 虽然服务器端做了必要的保护限制,但是对于应用程序,一方面服务器端直接抛SQL服务异常,对Java程序的处理不够友好。Java程序必须要捕获这些异常,进行异常处理。第二点,我们也不应当仅仅依靠服务器的最大连接数限制。我们应当在数据库访问客户端的时候,就实现这种限制。必须被有序可控的被线程使用。一旦发生异常,我们也多了一种保护手段。
所以我们需要在Java客户端程序就实现业务线程排队获取数据库练级。同时获取数据库连接的线程数,起到限流对后端数据库保护的措施。同时连接数过多,对后端数据库的性能造成严重的影响。应为连接数的增多,后端数据库就会存在更多的锁的冲突与检测。加大数据库服务器端资源的消耗。我们应当保证应用程序可控获取数据库连接。
##连接池 使用连接池两个最重要的原因:
连接池实际上就是一组Java Jar包,介于Java应用程序与JDBC数据库物理连接之间。负责帮助应用程序管理JDBC连接。通过连接池暴露的接口,连接池可以获取数据库连接,使用完毕后,将数据库连接归还给数据库连接池。
连接池对JDBC进行有效的管理。当连接不足时,会自动创建连接。在空闲的连接比较多的时候,会自动的销毁链接。在多个线程同时去获取数据库连接时,连接池还提供排队等待的功能。能够保证应用程序能够有序的获取数据库连接。
##连接池DBCP
DBCP连接池是Apache开源的Java连接池项目,同时是Tomcat连接池组件。同时在互联网企业中,应用最为广泛。DBCP连接池包括三个Java Jar包。
##DBCP官网 Apache Commons
DBCP
可以从Apache官方网站下载Java Jar包,也可以在mvnrepo网站下载。注意:DBCP三个Jar都是必须的。
##创建连接池对象 DBCP连接池使用叫做BasicDataSource
对象来表示一个连接池。首先我们要创建BasicDataSource
对象,由于JDBC连接池只是JDBC连接的管理单位。底层数据库访问依然通过JDBC连接实现。所以我们必须要告诉DBCP必要的信息,才能让DBCP去帮助我们去创建连接,这些信息与我们去创建JDBC连接的信息是一致的。
##DBCP初始化
public static BasicDataSource basicDataSource = null;
public final static String JDBC_DRIVER = "com.mysql.jdbc.Driver";
public final static String DB_URL = "jdbc:mysql://192.168.1.200/test";
public final static String USER = "root";
public final static String PASSWORD = "dVHJtG0T:pf*";
public void init()
{
basicDataSource = new BasicDataSource();
basicDataSource.setUrl(DB_URL);
basicDataSource.setDriverClassName(JDBC_DRIVER);
basicDataSource.setUsername(USER);
basicDataSource.setPassword(PASSWORD);
}
##获取数据库连接
我们可以注意到,这里除了获取连接外,和JDBC代码方式没有任何区别。
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = basicDataSource.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery("SELECT * FROM user");
while(resultSet.next())
{
String username = resultSet.getString("UserName");
System.out.println("[UserName]:" + username);
}
} catch (SQLException e) {
e.printStackTrace();
}
finally {
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
##高级配置 在实际开发过程中,我们还会去设置一些连接数据库的参数,来帮助我们优化连接池,提高数据库访问的性能。
setInitialSize()
当我们应用第一次访问数据库时,会发生很慢的情况。这是由于数据库连接池中没有数据库连接。需要去创建连接,此过程需要花费较多的时间。当连接创建好以后,后续的数据库访问就不会再去创建数据库连接。速度相对较快。InitialSize
就是只在数据库连接池创建时,预制一些数据库连接。来保证应用程序第一次访问时,连接池中就有一定数量的数据库连接。第一次访问就不会变的很慢。可以通过InitialSize
设置连接池预制的连接数。一般设置成预期业务访问量,是比较合适的。setMaxTotal()
当连接池中没有空闲的连接,又有线程需要去访问数据库时,连接池此时会创建一个新的数据库连接。但是此时,连接数已经达到了MaxTotal
设定的最大值。则连接池就不会为等待线程新建数据库连接。而是强制让该线程进入等待队列。直到有其他线程归还数据库连接,再进行分配。MaxTotal
起到限流保护数据库的作用,进入队列的线程不可能无限制的等待。setMaxWaitMillis()
通过设置MaxWaitMillis
来设置队列线程最大的等待时间,如果超过等待时间,则会得到SQLExecption
异常。setMaxIdle()
当应用程序使用完线程连接以后,将连接归还给连接池,如果此时空间连接池超过了MaxIdle
的值时,则连接池会自动销毁这个数据库连接。这样减少后端数据库的连接数,减少资源的损耗。
setMinIdle()
当然如何连接池中的空闲连接低于MinIdle
时,则会自动触发和创建数据库连接。来保证连接池有足够的连接被租借。一般来说,我们为了避免数据库连接池频繁的创建和销毁连接。建议吧MinIdle
和MaxIdle
设置为相同的值。
##DBCP定期检查 数据库服务为了释放,空闲等待的资源,默认会关闭超过一定时间阈值的数据库连接。MySQL数据库服务器端默认会关闭时间超过,8个小时的连接。但是这时,数据库连接池端并不知道数据库连接被关闭。当线程向数据库连接池租借数据库连接时,连接池会将失效的数据库连接,租借给应用程序。线程在使用这样的数据库连接,就会抛出SQLException异常。为了防止以上情况发生,尽量保持数据库连接池连接的有效性。则定期对数据库连接池的空闲连接的时间进行检查。在服务器端关闭连接之前,我们保证,会把这个连接销毁掉,在创建新的连接。来保证线程到数据库连接池租借的连接都是有效的。
setTestWhileIdle(True)
开启定期检查空间数据库连接有效性setMinEvictableIdleTimeMillis()
销毁连接最小空闲时间,当连接的空间时间超过该值时,会被连接池自动的销毁。建议:MinEvictabledleTimeMillis
要小于数据库服务器端自动关闭的预制时间,MySQL小于8个小时,我们才能够检测空闲时间超过该值。才能保证主动关闭连接。setTimeBetweenEvictionRunsMillis()
检查运行时间的间隔
##验证连接池有效 连接池帮助我们起到限流的作用,实现连接的重复使用。对比使用连接池与JDBC的后端连接数。
我们通过MySQLshow processlist
来可以看到线程数据库连接的情况。
如果启动十个线程,则JDBC方式会启动十个数据库连接。 而如果使用数据库连接池,限制数据库连接数,则只有两个数据库连接。
##总结 使用数据库连接提高数据库访问的效率。起到对后端数据库进行限流保护的作用。