一、为什么在连接数据库时要使用连接池 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
二、数据库连接池的基本原理 数据库连接池的基本思想就是为数据库连接 建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定 连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量?使用情况,为系统开发?测试及性能调 整提供依据。
三、数据库连接池的工作原理 连接池的工作原理主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、连接池的关闭。
第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。
第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:
当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。
四、连接池关键问题分析 1、并发问题
为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为Java语言自身提供了对并发管理的支 持,使用synchronized关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字,如:
public synchronized Connection getConnection()
2、多数据库服务器和多用户
对于大型的企业级应用,常常需要同时连接不同的数据库(如连接Oracle和Sybase)。如何连接不同的数据库呢?我们采用的策略是:设计 一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的url地址()?用户名()?密 码()等信息。如 tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。根据资源文件提 供的信息,创建多个连接池类的实例,每一个实例都是一个特定数据库的连接池。连接池管理类实例为每个连接池实例取一个名字,通过不同的名字来管理不同的连 接池。
对于同一个数据库有多个用户使用不同的名称和密码访问的情况,也可以通过资源文件处理,即在资源文件中设置多个具有相同url地址,但具有不同用户名和密码的数据库连接信息。
3、事务处理
我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。
在Java语言中,Connection类本身提供了对事务的支持,可以通过设置Connection的AutoCommit属性为 false,然后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用,就必须提供相应的事务支持机制。可采用 每一个事务独占一个连接来实现,这种方法可以大大降低事务管理的复杂性。
4、连接池的分配与释放
连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时,系统首先检查空闲池内 有没有空闲连接。如果有就把建立时间最长(通过容器的顺序存放实现)的那个连接分配给他(实际是先做连接是否有效的判断,如果可用就分配给用户,如不可用 就把这个连接从空闲池删掉,重新检测空闲池是否还有连接);如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数(maxConn),如果没有 达到,就新建一个连接,如果已经达到,就等待一定的时间(timeout)。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户,如果 等待时间超过预定时间timeout,则返回空值(null)。系统对已经分配出去正在使用的连接只做计数,当使用完后再返还给空闲池。对于空闲连接的状 态,可开辟专门的线程定时检测,这样会花费一定的系统开销,但可以保证较快的响应速度。也可采取不开辟专门线程,只是在分配前检测的方法。
5、连接池的配置与维护
连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConn)和最大连接数(maxConn)来控制连接 池中的连接。最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很 快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连 接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过反复测试,找到最佳点。
如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。
五、连接池实现代码(java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 package book.util;import java.sql.Connection;import java.sql.DatabaseMetaData;import java.sql.Date;import java.sql.Driver;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Vector;public class Pool { public static void main (String[] args) { Pool pool = new Pool("com.microsoft.sqlserver.jdbc.SQLServerDriver" ,"jdbc:sqlserver://localhost:1433;DataBaseName=Book" ,"sa" ,"aaaaaa" ); try { pool.createConnections(4 ); } catch (SQLException e) { e.printStackTrace(); } Connection conn = pool.getConnection(); try { String sql = "select * from allbook" ; PreparedStatement ps; ps = conn.prepareStatement(sql); ResultSet rs=ps.executeQuery(); while (rs.next()){ System.out.println(rs.getString("BOOKNAME" )); } } catch (SQLException e) { e.printStackTrace(); }finally { pool.returnConnection(conn); } } private String jdbcDriver = "" ; private String dbUrl = "" ; private String dbUsername = "" ; private String dbPassword = "" ; private String testTable = "" ; private int initialConnectionsNum = 10 ; private int maxConnectionsNum = 50 ; private int incrementalConnections = 5 ; private Vector<PooledConnection> connections = null ; public Pool () {} public Pool (String driver, String url, String name, String pass) { this .jdbcDriver = driver; this .dbUrl = url; this .dbUsername = name; this .dbPassword = pass; try { this .createPool(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public synchronized void createPool () throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { if (this .connections != null ) { return ; } Driver driver = (Driver)(Class.forName(this .jdbcDriver).newInstance()); DriverManager.registerDriver(driver); this .connections = new Vector<PooledConnection>(); this .createConnections(this .initialConnectionsNum); } private void createConnections (int num) throws SQLException { for (int i = 0 ; i < num; ++i) { if (this .connections.size() >= this .maxConnectionsNum) { return ; } this .connections.addElement (new PooledConnection(newConnection())); } } private Connection newConnection () throws SQLException { Connection con = DriverManager.getConnection(this .dbUrl, this .dbUsername, this .dbPassword); if (this .connections.size() == 0 ) { DatabaseMetaData metadata = con.getMetaData(); int dbMaxConnectionsNum = metadata.getMaxConnections(); if (dbMaxConnectionsNum > 0 && this .maxConnectionsNum > dbMaxConnectionsNum) { this .maxConnectionsNum = dbMaxConnectionsNum; } } return con; } public synchronized Connection getConnection () { Connection con = null ; if (this .connections == null ) { return con; } try { con = this .getFreeConnection(); } catch (SQLException e) { e.printStackTrace(); } while (con == null ) { this .wait(30 ); try { con = this .getFreeConnection(); } catch (SQLException e) { e.printStackTrace(); } } return con; } private Connection getFreeConnection () throws SQLException { Connection con = null ; con = this .findFreeConnection(); if (con == null ) { this .createConnections(this .incrementalConnections); con = this .findFreeConnection(); } return con; } private Connection findFreeConnection () throws SQLException { Connection con = null ; for (int i = 0 ; i < this .connections.size(); ++i) { PooledConnection pol = (PooledConnection)this .connections.get(i); if (!pol.isBusy()) { con = pol.getCon(); pol.setBusy(true ); if (!this .testCon(con)) { con = this .newConnection(); pol.setCon(con); } break ; } } return con; } private boolean testCon (Connection con) { boolean useable = true ; try { Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select count(*) from " + this .testTable); rs.next(); } catch (SQLException e) { useable = false ; this .closeConnection(con); } return useable; } public void returnConnection (Connection con) { if (this .connections == null ) { return ; } for (int i = 0 ; i < this .connections.size(); ++i) { PooledConnection pool = this .connections.get(i); if (con == pool.getCon()) { pool.setBusy(false ); } } } public synchronized void refreshConneciontPool () throws SQLException { if (this .connections == null ) { return ; } for (int i = 0 ; i < this .connections.size(); ++i) { PooledConnection pool = this .connections.get(i); if (pool.isBusy()) { this .wait(5000 ); } this .closeConnection(pool.getCon()); pool.setCon(this .newConnection()); pool.setBusy(false ); } } public void closeConnectionPool () { if (this .connections == null ) { return ; } for (int i = 0 ; i < this .connections.size(); ++i) { PooledConnection pool = this .connections.get(i); if (pool.isBusy()) { this .wait(5000 ); } this .closeConnection(pool.getCon()); this .connections.remove(i); } this .connections = null ; } private void wait (int mSecond) { try { Thread.sleep(mSecond); } catch (InterruptedException e) { e.printStackTrace(); } } public String getJdbcDriver () { return jdbcDriver; } public void setJdbcDriver (String jdbcDriver) { this .jdbcDriver = jdbcDriver; } public String getDbUrl () { return dbUrl; } public void setDbUrl (String dbUrl) { this .dbUrl = dbUrl; } public String getDbUsername () { return dbUsername; } public void setDbUsername (String dbUsername) { this .dbUsername = dbUsername; } public String getDbPassword () { return dbPassword; } public void setDbPassword (String dbPassword) { this .dbPassword = dbPassword; } public String getTestTable () { return testTable; } public void setTestTable (String testTable) { this .testTable = testTable; } public int getInitialConnectionsNum () { return initialConnectionsNum; } public void setInitialConnectionsNum (int initialConnectionsNum) { this .initialConnectionsNum = initialConnectionsNum; } public int getMaxConnectionsNum () { return maxConnectionsNum; } public void setMaxConnectionsNum (int maxConnectionsNum) { this .maxConnectionsNum = maxConnectionsNum; } public int getIncrementalConnections () { return incrementalConnections; } public void setIncrementalConnections (int incrementalConnections) { this .incrementalConnections = incrementalConnections; } public Vector<PooledConnection> getConnections () { return connections; } public void setConnections (Vector<PooledConnection> connections) { this .connections = connections; } private void closeConnection (Connection con) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } class PooledConnection { private Connection con = null ; private boolean busy = false ; public PooledConnection (Connection con) { this .con = con; } public Connection getCon () { return con; } public void setCon (Connection con) { this .con = con; } public boolean isBusy () { return busy; } public void setBusy (boolean busy) { this .busy = busy; } } }
转载自: java数据库连接池实现原理