java数据库链接池实现原理

1、为何在链接数据库时要使用链接池

 数据库链接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤其突出。  一个数据库链接对象均对应一个物理数据库链接,每次操做都打开一个物理链接,使用完都关闭链接,这样形成系统的 性能低下。 数据库链接池的解决方案是在应用程序启动时创建足够的数据库链接,并讲这些链接组成一个链接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的链接进行申请、使用和释放。对于多于链接池中链接数的并发请求,应该在请求队列中排队等待。而且应用程序能够根据池中链接的使用率,动态增长或减小池中的链接数。 链接池技术尽量多地重用了消耗内存地资源,大大节省了内存,提升了服务器地服务效率,可以支持更多的客户服务。经过使用链接池,将大大提升程序运行效率,同时,咱们能够经过其自身的管理机制来监视数据库链接的数量、使用状况等。 java

2、数据库链接池的基本原理

数据库链接池的基本思想就是为数据库链接 创建一个“缓冲池”。预先在缓冲池中放入必定数量的链接,当须要创建数据库链接时,只需从“缓冲池”中取出一个,使用完毕以后再放回去。咱们能够经过设定 链接池最大链接数来防止系统无尽的与数据库链接。更为重要的是咱们能够经过链接池的管理机制监视数据库的链接的数量?使用状况,为系统开发?测试及性能调 整提供依据。sql

 

3、数据库链接池的工做原理

链接池的工做原理主要由三部分组成,分别为链接池的创建、链接池中链接的使用管理、链接池的关闭。数据库

        第1、链接池的创建。通常在系统初始化时,链接池会根据系统配置创建,并在池中建立了几个链接对象,以便使用时能从链接池中获取。链接池中的链接不能随意建立和关闭,这样避免了链接随意创建和关闭形成的系统开销。Java中提供了不少容器类能够方便的构建链接池,例如Vector、Stack等。服务器

        第2、链接池的管理。链接池管理策略是链接池机制的核心,链接池内链接的分配和释放对系统的性能有很大的影响。其管理策略是:多线程

        当客户请求数据库链接时,首先查看链接池中是否有空闲链接,若是存在空闲链接,则将链接分配给客户使用;若是没有空闲链接,则查看当前所开的链接数是否已经达到最大链接数,若是没达到就从新建立一个链接给请求的客户;若是达到就按设定的最大等待时间进行等待,若是超出最大等待时间,则抛出异常给客户。并发

        当客户释放数据库链接时,先判断该链接的引用次数是否超过了规定值,若是超过就从链接池中删除该链接,不然保留为其余客户服务。app

        该策略保证了数据库链接的有效复用,避免频繁的创建、释放链接所带来的系统资源开销。函数

        第3、链接池的关闭。当应用程序退出时,关闭链接池中全部的链接,释放链接池相关的资源,该过程正好与建立相反。sqlserver

 

4、链接池关键问题分析

  一、并发问题性能

  为了使链接管理服务具备最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,由于Java语言自身提供了对并发管理的支 持,使用synchronized关键字便可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字,如:

  public synchronized Connection getConnection()

  二、多数据库服务器和多用户

  对于大型的企业级应用,经常须要同时链接不一样的数据库(如链接Oracle和Sybase)。如何链接不一样的数据库呢?咱们采用的策略是:设计 一个符合单例模式的链接池管理类,在链接池管理类的惟一实例被建立时读取一个资源文件,其中资源文件中存放着多个数据库的url地址()?用户名()?密 码()等信息。如 tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。根据资源文件提 供的信息,建立多个链接池类的实例,每个实例都是一个特定数据库的链接池。链接池管理类实例为每一个链接池实例取一个名字,经过不一样的名字来管理不一样的连 接池。

  对于同一个数据库有多个用户使用不一样的名称和密码访问的状况,也能够经过资源文件处理,即在资源文件中设置多个具备相同url地址,但具备不一样用户名和密码的数据库链接信息。

  三、事务处理

  咱们知道,事务具备原子性,此时要求对数据库的操做符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全作,要么全不作。

  在Java语言中,Connection类自己提供了对事务的支持,能够经过设置Connection的AutoCommit属性为 false,而后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用,就必须提供相应的事务支持机制。可采用 每个事务独占一个链接来实现,这种方法能够大大下降事务管理的复杂性。

  四、链接池的分配与释放

  链接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,能够提升链接的复用度,从而下降创建新链接的开销,同时还能够加快用户的访问速度。

  对于链接的管理可以使用空闲池。即把已经建立但还没有分配出去的链接按建立时间存放到一个空闲池中。每当用户请求一个链接时,系统首先检查空闲池内 有没有空闲链接。若是有就把创建时间最长(经过容器的顺序存放实现)的那个链接分配给他(实际是先作链接是否有效的判断,若是可用就分配给用户,如不可用 就把这个链接从空闲池删掉,从新检测空闲池是否还有链接);若是没有则检查当前所开链接池是否达到链接池所容许的最大链接数(maxConn),若是没有 达到,就新建一个链接,若是已经达到,就等待必定的时间(timeout)。若是在等待的时间内有链接被释放出来就能够把这个链接分配给等待的用户,若是 等待时间超过预约时间timeout,则返回空值(null)。系统对已经分配出去正在使用的链接只作计数,当使用完后再返还给空闲池。对于空闲链接的状 态,可开辟专门的线程定时检测,这样会花费必定的系统开销,但能够保证较快的响应速度。也可采起不开辟专门线程,只是在分配前检测的方法。

  五、链接池的配置与维护

  链接池中到底应该放置多少链接,才能使系统的性能最佳?系统可采起设置最小链接数(minConn)和最大链接数(maxConn)来控制链接 池中的链接。最小链接数是系统启动时链接池所建立的链接数。若是建立过多,则系统启动就慢,但建立后系统的响应速度会很快;若是建立过少,则系统启动的很 快,响应起来却慢。这样,能够在开发时,设置较小的最小链接数,开发起来会快,而在系统实际使用时设置较大的,由于这样对访问客户来讲速度会快些。最大连 接数是链接池中容许链接的最大数目,具体设置多少,要看系统的访问量,可经过反复测试,找到最佳点。

  如何确保链接池中的最小链接数呢?有动态和静态两种策略。动态即每隔必定时间就对链接池进行检测,若是发现链接数量小于最小链接数,则补充相应数量的新链接,以保证链接池的正常运转。静态是发现空闲链接不够时再去检查。

 

5、链接池实现代码(java)

[java] view plain copy

  1. package book.util;  
  2. import java.sql.Connection;  
  3. import java.sql.DatabaseMetaData;  
  4. import java.sql.Date;  
  5. import java.sql.Driver;  
  6. import java.sql.DriverManager;  
  7. import java.sql.PreparedStatement;  
  8. import java.sql.ResultSet;  
  9. import java.sql.SQLException;  
  10. import java.sql.Statement;  
  11. import java.util.Vector;  
  12. public class Pool {  
  13.     public static void main(String[] args) {  
  14.         Pool pool = new Pool("com.microsoft.sqlserver.jdbc.SQLServerDriver","jdbc:sqlserver://localhost:1433;DataBaseName=Book","sa","aaaaaa");  
  15.         try {  
  16.             pool.createConnections(4);  
  17.               
  18.         } catch (SQLException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.         Connection conn = pool.getConnection();   
  22.         try {  
  23.             String sql = "select * from allbook";  
  24.             PreparedStatement ps;  
  25.             ps = conn.prepareStatement(sql);  
  26.             ResultSet rs=ps.executeQuery();  
  27.             while(rs.next()){  
  28.                 System.out.println(rs.getString("BOOKNAME"));  
  29.             }  
  30.         } catch (SQLException e) {  
  31.             // TODO Auto-generated catch block  
  32.             e.printStackTrace();  
  33.         }finally{  
  34.             pool.returnConnection(conn);  
  35.         }  
  36.         //long startTime=System.currentTimeMillis();   
  37.         //long endTime=System.currentTimeMillis();   
  38.         //System.out.println("程序运行时间: "+(endTime-startTime)+"ms");   
  39.     }  
  40.       
  41.     private String jdbcDriver = "";//数据库驱动  
  42.     private String dbUrl = "";//数据库url  
  43.     private String dbUsername = "";//数据库用户名  
  44.     private String dbPassword = "";//数据库密码  
  45.     private String testTable = "";  
  46.     private int initialConnectionsNum = 10;//链接池初始链接数  
  47.     private int maxConnectionsNum = 50;//链接池最大链接数  
  48.     private int incrementalConnections = 5;//每次动态添加的链接数  
  49.     private Vector<PooledConnection> connections = null;//向量,存放链接池中的链接,初始为空  
  50.       
  51.     /*无参构造函数*/  
  52.     public Pool()  
  53.     {}  
  54.       
  55.     /*带参数的构造函数 
  56.      * 初始化数据库驱动、数据库url、数据库用户名、数据库密码、测试表 
  57.      * */  
  58.     public Pool(String driver, String url, String name, String pass)  
  59.     {  
  60.         this.jdbcDriver = driver;  
  61.         this.dbUrl = url;  
  62.         this.dbUsername = name;  
  63.         this.dbPassword = pass;  
  64.         //this.testTable = table;  
  65.         try {  
  66.             this.createPool();  
  67.         } catch (InstantiationException e) {  
  68.             // TODO Auto-generated catch block  
  69.             e.printStackTrace();  
  70.         } catch (IllegalAccessException e) {  
  71.             // TODO Auto-generated catch block  
  72.             e.printStackTrace();  
  73.         } catch (ClassNotFoundException e) {  
  74.             // TODO Auto-generated catch block  
  75.             e.printStackTrace();  
  76.         } catch (SQLException e) {  
  77.             // TODO Auto-generated catch block  
  78.             e.printStackTrace();  
  79.         }  
  80.     }  
  81.       
  82.     /*函数,建立链接池*/  
  83.     public synchronized void createPool()   
  84.     throws InstantiationException, IllegalAccessException,   
  85.     ClassNotFoundException, SQLException  
  86.     {  
  87.         /*确保链接池为建立,若是已经建立,则保存链接的向量不为空 
  88.          * */  
  89.         if (this.connections != null)  
  90.         {  
  91.             return ;  
  92.         }  
  93.         //驱动器实例化  
  94.         Driver driver = (Driver)(Class.forName(this.jdbcDriver).newInstance());  
  95.         //注册驱动器  
  96.         DriverManager.registerDriver(driver);  
  97.         //建立保存链接的向量  
  98.         this.connections = new Vector<PooledConnection>();  
  99.         //建立数据库链接  
  100.         this.createConnections(this.initialConnectionsNum);  
  101.     }  
  102.       
  103.     /*函数,建立数据库链接 
  104.      * */  
  105.     private void createConnections (int num) throws SQLException  
  106.     {  
  107.         /*循环建立链接 
  108.          * 须要首先检查当前链接数是否已经超出链接池最大链接数 
  109.          * */  
  110.         for (int i = 0; i < num; ++i)  
  111.         {  
  112.             //检查  
  113.             if (this.connections.size() >= this.maxConnectionsNum)  
  114.             {  
  115.                 return;  
  116.             }  
  117.             //建立链接  
  118.             this.connections.addElement  
  119.             (new PooledConnection(newConnection()));  
  120.         }  
  121.           
  122.     }  
  123.       
  124.     /*函数,建立一个数据库链接*/  
  125.     private Connection newConnection() throws SQLException  
  126.     {  
  127.         /*建立链接*/  
  128.         Connection con = DriverManager.getConnection(this.dbUrl,   
  129.                 this.dbUsername, this.dbPassword);  
  130.         /*若是是第一次建立链接,则检查所链接的数据库的容许最大链接数是否小于 
  131.          * 咱们所设定的最大链接数*/  
  132.         if (this.connections.size() == 0)  
  133.         {  
  134.             DatabaseMetaData metadata = con.getMetaData();  
  135.             //获得数据库最大链接数  
  136.             int dbMaxConnectionsNum = metadata.getMaxConnections();  
  137.             //若是数据库最大链接数更小,则更改咱们所设定的链接池最大链接数  
  138.             if (dbMaxConnectionsNum > 0   
  139.                     && this.maxConnectionsNum > dbMaxConnectionsNum)  
  140.             {  
  141.                 this.maxConnectionsNum = dbMaxConnectionsNum;  
  142.             }  
  143.         }  
  144.         return con;  
  145.     }  
  146.       
  147.     /*函数,获得一个可用链接 
  148.      * */  
  149.     public synchronized Connection getConnection ()   
  150.     {  
  151.         Connection con = null;  
  152.         /*检查链接池是否已经创建*/  
  153.         if (this.connections == null)  
  154.         {  
  155.             return con;  
  156.         }  
  157.         //获得一个可用链接  
  158.         try {  
  159.             con = this.getFreeConnection();  
  160.         } catch (SQLException e) {  
  161.             // TODO Auto-generated catch block  
  162.             e.printStackTrace();  
  163.         }  
  164.         //若是未找到合适链接,循环等待、查找,知道找到合适链接  
  165.         while(con == null)  
  166.         {  
  167.             this.wait(30);  
  168.             try {  
  169.                 con = this.getFreeConnection();  
  170.             } catch (SQLException e) {  
  171.                 // TODO Auto-generated catch block  
  172.                 e.printStackTrace();  
  173.             }  
  174.         }  
  175.           
  176.         return con;  
  177.     }  
  178.       
  179.       
  180.     /*函数,获得一个可用链接*/  
  181.     private Connection getFreeConnection() throws SQLException  
  182.     {  
  183.         Connection con = null;  
  184.         //查找一个可用链接  
  185.         con = this.findFreeConnection();  
  186.         //若是未找到可用链接,就创建一些新的链接,再次查找  
  187.         if (con == null)  
  188.         {  
  189.             this.createConnections(this.incrementalConnections);  
  190.             //再次查找  
  191.             con = this.findFreeConnection();  
  192.         }  
  193.         return con;  
  194.     }  
  195.       
  196.       
  197.     /*函数,从现有链接中查找一个可用链接 
  198.      * 在现有的链接中(向量connections中)找到一个空闲链接, 
  199.      * 并测试这个连接是否可用,若不可用则从新创建链接,替换原来的链接*/  
  200.     private Connection findFreeConnection () throws SQLException  
  201.     {  
  202.         Connection con = null;  
  203.         for (int i = 0; i < this.connections.size(); ++i)  
  204.         {  
  205.             PooledConnection pol = (PooledConnection)this.connections.get(i);  
  206.             if (!pol.isBusy())  
  207.             {  
  208.                 /*若是此连接未被使用,则返回这个链接并,设置正在使用标志*/  
  209.                 con = pol.getCon();  
  210.                 pol.setBusy(true);  
  211.                 /*测试链接是否可用*/  
  212.                 if (!this.testCon(con))  
  213.                 {  
  214.                     con = this.newConnection();  
  215.                     pol.setCon(con);  
  216.                 }  
  217.                 break;  
  218.             }  
  219.         }  
  220.         return con;  
  221.     }  
  222.       
  223.     /*函数,测试链接是否可用 
  224.      * */  
  225.     private boolean testCon (Connection con)  
  226.     {  
  227.         boolean useable = true;  
  228.         try  
  229.         {  
  230.             Statement st = con.createStatement();  
  231.             ResultSet rs = st.executeQuery("select count(*) from " + this.testTable);  
  232.             rs.next();  
  233.         }  
  234.         catch(SQLException e)  
  235.         {  
  236.             /*上面抛出异常,链接不可用,关闭*/  
  237.             useable = false;  
  238.             this.closeConnection(con);  
  239.         }  
  240.         return useable;  
  241.     }  
  242.       
  243.     /*函数,将使用完毕的链接放回链接池中 
  244.      * */  
  245.     public void returnConnection(Connection con)  
  246.     {  
  247.         /*确保链接池存在*/  
  248.         if (this.connections == null)  
  249.         {  
  250.             return ;  
  251.         }  
  252.         for (int i = 0; i < this.connections.size(); ++i)  
  253.         {  
  254.             PooledConnection pool = this.connections.get(i);  
  255.             //找到相应链接,设置正在使用标志为false  
  256.             if (con == pool.getCon())  
  257.             {  
  258.                 pool.setBusy(false);  
  259.             }  
  260.         }  
  261.           
  262.     }  
  263.       
  264.     /*函数,刷新链接池中的链接*/  
  265.     public synchronized void refreshConneciontPool () throws SQLException  
  266.     {  
  267.         /*确保链接池存在*/  
  268.         if (this.connections == null)  
  269.         {  
  270.             return ;  
  271.         }  
  272.         for (int i = 0; i < this.connections.size(); ++i)  
  273.         {  
  274.             PooledConnection pool = this.connections.get(i);  
  275.             if (pool.isBusy())  
  276.             {  
  277.                 this.wait(5000);  
  278.             }  
  279.             this.closeConnection(pool.getCon());  
  280.             pool.setCon(this.newConnection());  
  281.             pool.setBusy(false);  
  282.         }  
  283.     }  
  284.   
  285.     /*函数,关闭链接池*/  
  286.     public void closeConnectionPool()  
  287.     {  
  288.         /*确保链接池存在*/  
  289.         if (this.connections == null)  
  290.         {  
  291.             return ;  
  292.         }  
  293.         for (int i = 0; i < this.connections.size(); ++i)  
  294.         {  
  295.             PooledConnection pool = this.connections.get(i);  
  296.             if (pool.isBusy())  
  297.             {  
  298.                 this.wait(5000);  
  299.             }  
  300.             this.closeConnection(pool.getCon());  
  301.             this.connections.remove(i);  
  302.         }  
  303.         this.connections = null;  
  304.     }  
  305.       
  306.     /*函数,暂时无可用链接,进入等待队列等待m秒,再试 
  307.      * */  
  308.     private void wait(int mSecond)  
  309.     {  
  310.         try {  
  311.             Thread.sleep(mSecond);  
  312.         } catch (InterruptedException e) {  
  313.             // TODO Auto-generated catch block  
  314.             e.printStackTrace();  
  315.         }  
  316.     }  
  317.       
  318.     /** 
  319.      * @return the jdbcDriver 
  320.      */  
  321.     public String getJdbcDriver() {  
  322.         return jdbcDriver;  
  323.     }  
  324.   
  325.     /** 
  326.      * @param jdbcDriver the jdbcDriver to set 
  327.      */  
  328.     public void setJdbcDriver(String jdbcDriver) {  
  329.         this.jdbcDriver = jdbcDriver;  
  330.     }  
  331.   
  332.     /** 
  333.      * @return the dbUrl 
  334.      */  
  335.     public String getDbUrl() {  
  336.         return dbUrl;  
  337.     }  
  338.   
  339.     /** 
  340.      * @param dbUrl the dbUrl to set 
  341.      */  
  342.     public void setDbUrl(String dbUrl) {  
  343.         this.dbUrl = dbUrl;  
  344.     }  
  345.   
  346.     /** 
  347.      * @return the dbUsername 
  348.      */  
  349.     public String getDbUsername() {  
  350.         return dbUsername;  
  351.     }  
  352.   
  353.     /** 
  354.      * @param dbUsername the dbUsername to set 
  355.      */  
  356.     public void setDbUsername(String dbUsername) {  
  357.         this.dbUsername = dbUsername;  
  358.     }  
  359.   
  360.     /** 
  361.      * @return the dbPassword 
  362.      */  
  363.     public String getDbPassword() {  
  364.         return dbPassword;  
  365.     }  
  366.   
  367.     /** 
  368.      * @param dbPassword the dbPassword to set 
  369.      */  
  370.     public void setDbPassword(String dbPassword) {  
  371.         this.dbPassword = dbPassword;  
  372.     }  
  373.   
  374.     /** 
  375.      * @return the testTable 
  376.      */  
  377.     public String getTestTable() {  
  378.         return testTable;  
  379.     }  
  380.   
  381.     /** 
  382.      * @param testTable the testTable to set 
  383.      */  
  384.     public void setTestTable(String testTable) {  
  385.         this.testTable = testTable;  
  386.     }  
  387.   
  388.     /** 
  389.      * @return the initialConnectionsNum 
  390.      */  
  391.     public int getInitialConnectionsNum() {  
  392.         return initialConnectionsNum;  
  393.     }  
  394.   
  395.     /** 
  396.      * @param initialConnectionsNum the initialConnectionsNum to set 
  397.      */  
  398.     public void setInitialConnectionsNum(int initialConnectionsNum) {  
  399.         this.initialConnectionsNum = initialConnectionsNum;  
  400.     }  
  401.   
  402.     /** 
  403.      * @return the maxConnectionsNum 
  404.      */  
  405.     public int getMaxConnectionsNum() {  
  406.         return maxConnectionsNum;  
  407.     }  
  408.   
  409.     /** 
  410.      * @param maxConnectionsNum the maxConnectionsNum to set 
  411.      */  
  412.     public void setMaxConnectionsNum(int maxConnectionsNum) {  
  413.         this.maxConnectionsNum = maxConnectionsNum;  
  414.     }  
  415.   
  416.     /** 
  417.      * @return the incrementalConnections 
  418.      */  
  419.     public int getIncrementalConnections() {  
  420.         return incrementalConnections;  
  421.     }  
  422.   
  423.     /** 
  424.      * @param incrementalConnections the incrementalConnections to set 
  425.      */  
  426.     public void setIncrementalConnections(int incrementalConnections) {  
  427.         this.incrementalConnections = incrementalConnections;  
  428.     }  
  429.   
  430.     /** 
  431.      * @return the connections 
  432.      */  
  433.     public Vector<PooledConnection> getConnections() {  
  434.         return connections;  
  435.     }  
  436.   
  437.     /** 
  438.      * @param connections the connections to set 
  439.      */  
  440.     public void setConnections(Vector<PooledConnection> connections) {  
  441.         this.connections = connections;  
  442.     }  
  443.   
  444.     /*函数,链接使用完毕,关闭链接*/  
  445.     private void closeConnection (Connection con)  
  446.     {  
  447.         try  
  448.         {  
  449.             con.close();  
  450.         }  
  451.         catch(SQLException e)  
  452.         {  
  453.             e.printStackTrace();  
  454.         }  
  455.     }  
  456.       
  457.       
  458.     /*内部使用的保存数据库链接的类 
  459.      * 两个成员变量:链接、是否正在使用*/  
  460.     class PooledConnection  
  461.     {  
  462.         private Connection con = null;//链接  
  463.         private boolean busy = false;//是否正在使用,默认为非  
  464.           
  465.         /*构造函数*/  
  466.         public PooledConnection(Connection con)  
  467.         {  
  468.             this.con = con;  
  469.         }  
  470.   
  471.         /** 
  472.          * @return the con 
  473.          */  
  474.         public Connection getCon() {  
  475.             return con;  
  476.         }  
  477.   
  478.         /** 
  479.          * @param con the con to set 
  480.          */  
  481.         public void setCon(Connection con) {  
  482.             this.con = con;  
  483.         }  
  484.   
  485.         /** 
  486.          * @return the busy 
  487.          */  
  488.         public boolean isBusy() {  
  489.             return busy;  
  490.         }  
  491.   
  492.         /** 
  493.          * @param busy the busy to set 
  494.          */  
  495.         public void setBusy(boolean busy) {  
  496.             this.busy = busy;  
  497.         }  
  498.     }  
  499.   
  500. }  
相关文章
相关标签/搜索