zoukankan      html  css  js  c++  java
  • 数据库连接池的一种实现方案

      数据库连接池有多个开源实现,像dbcp、druid等。这里我们再一次造轮子,思路很简单:当有SQL操作(增删查改)到来时,先到池子里看一眼,如果有可用的连接,拿来用,没有就新建一个连接。连接不在新建时入池,而是在被关闭时。本来应当被系统回收的连接被放入池中复用,当累计到最大连接数时,就不再入池,直接回收。

      为何要在关闭时判断最大连接数、入池,而不是在请求到来时做呢?我们把关闭入池叫方案1,请求入池叫方案2。采用方案1有两个好处:一、方案2的问题是,当一开始就有超过最大连接数的SQL操作的情况出现时,超过部分的请求会被拒绝或者等待。方案1避免了这种情况,就算超过仍然可以先把连接创建出来,因为它在关闭时才会校验最大连接数。二、方案2在系统启动时新建连接把池子填满,或者在每次请求到来时建立新的连接并入池,最终都会让池子满。方案1则支持动态调整池子大小,请求来多少给多少个连接,如果请求数少于最大连接数,根本不会让池子满。比如最大连接数是10,每批次并发请求来5个,用完后这个5个入池,下次再并发来5个,那么还是这5个出池,用完后再入池。

      方案1的优势是有前提的:一、当请求并发量很大时,池子就起不到保护缓冲的作用了,系统可能一开始就被请求洪峰冲毁了;反而方案2可以起到限流保护的作用。二、弹性的前提是请求连接数少于最大连接数,只要有一次池子满了,那以后也一直是满池运行,跟方案2没啥区别了。

      1、配置文件,放在main/java/resources目录下的jdbc.properties:

    db.driver=oracle.jdbc.driver.OracleDriver
    db.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
    db.username=wlf
    db.passwd=wlf
    db.max=10

      2、3类合一

    import javax.sql.DataSource;
    import java.io.IOException;
    import java.io.PrintWriter;import java.sql.Array;
    import java.sql.Blob;
    import java.sql.CallableStatement;
    import java.sql.Clob;
    import java.sql.Connection;
    import java.sql.DatabaseMetaData;
    import java.sql.DriverManager;
    import java.sql.NClob;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLClientInfoException;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.sql.SQLWarning;
    import java.sql.SQLXML;
    import java.sql.Savepoint;
    import java.sql.Statement;
    import java.sql.Struct;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.Executor;
    import java.util.logging.Logger;
    
    /**
     * 数据库连接池
     */
    public class DataSourcePool {
    
        private DataSource dataSource; // 数据源
    
        public DataSourcePool() {
            try {
                dataSource = new SimpleDataSource("/jdbc.properties");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 增删改
         *
         * @param sql
         * @param args
         * @return
         */
        public int update(String sql, Object... args) {
            try (Connection conn = dataSource.getConnection();
                 PreparedStatement ps = conn.prepareStatement(sql)) {
                for (int i = 1; i <= args.length; i++) {
                    ps.setObject(i, args[i - 1]);
                }
                return ps.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 查
         *
         * @param sql
         * @return
         */
        public List<String> query(String sql, int index) {
            List<String> queryResults = new ArrayList<>();
            try (Connection conn = dataSource.getConnection();
                 PreparedStatement ps = conn.prepareStatement(sql)) {
                ps.setInt(1, index);
                ResultSet rs = ps.executeQuery();
                while (rs.next()) {
                    queryResults.add(rs.getString("TITLE"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return queryResults;
        }
    
        public static void main(String[] args) throws InterruptedException {
            int threadNum = 10; // 起多个线程执行
            CountDownLatch countDownLatch = new CountDownLatch(threadNum); // 让插入先行,删除后行
            String updateSql = "insert into t_wlf_component_template values (?,?)";
            String deleteSql = "delete from t_wlf_component_template where id = ?";
            String querySql = "select TITLE from t_wlf_component_template where id = ?";
    
            DataSourcePool dataSourcePool = new DataSourcePool();
    
            for (int i = 0; i < threadNum; i++) {
                final int j = i + 1;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String currentThreadName = Thread.currentThread().getName();
                        long begin = System.currentTimeMillis();
                        System.out.println(currentThreadName + " 更新结果:" + dataSourcePool.update(updateSql, j, "heihei."));
                        System.out.println(currentThreadName + " 更新耗时: " + (System.currentTimeMillis() - begin) + " 微秒。");
    
                        begin = System.currentTimeMillis();
                        System.out.println(currentThreadName + " 查询结果:" + dataSourcePool.query(querySql, j));
                        System.out.println(currentThreadName + " 查询耗时: " + (System.currentTimeMillis() - begin) + " 微秒。");
                        countDownLatch.countDown(); // 每执行一次插入,减少一次
                    }
                }).start();
            }
    
            countDownLatch.await(); // 主线程在此等待
    
            for (int i = 0; i < threadNum; i++) {
                final int j = i + 1;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String currentThreadName = Thread.currentThread().getName();
                        long begin = System.currentTimeMillis();
                        System.out.println(currentThreadName + " 删除结果:" + dataSourcePool.update(deleteSql, j));
                        System.out.println(currentThreadName + " 删除耗时: " + (System.currentTimeMillis() - begin) + " 微秒。");
                    }
                }).start();
            }
        }
    
        /**
         * 数据源,从配置文件中加载驱动,获取连接
         */
        class SimpleDataSource implements DataSource {
    
            private List<Connection> conns; // 数据库连接列表
            private String dbDriver; // 驱动
            private String dbUrl; // 数据库连接url
            private String dbUserName; // 用户名
            private String dbPasswd; // 密码
            private int dbMax; // 最大连接数
    
            public SimpleDataSource(String configFilePath) throws IOException {
                // 读取数据库配置文件
                Properties properties = new Properties();
                properties.load(DataSourcePool.class.getResourceAsStream(configFilePath));
                dbDriver = properties.getProperty("db.driver");
                dbUrl = properties.getProperty("db.url");
                dbUserName = properties.getProperty("db.username");
                dbPasswd = properties.getProperty("db.passwd");
                dbMax = Integer.valueOf(properties.getProperty("db.max"));
    
                // 初始化数据连接池
                conns = Collections.synchronizedList(new ArrayList<>(dbMax));
            }
    
            @Override
            public Connection getConnection() throws SQLException {
    
                // 加载驱动
                try {
                    Class.forName(dbDriver);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
    
                // 每次返回一个新的数据库连接
                return DriverManager.getConnection(dbUrl, dbUserName, dbPasswd);
    
                // 每次从数据库连接池中获取
    //            return getConnection(dbUserName, dbPasswd);
            }
    
            /**
             * 获取指定用户名连接
             *
             * @param username
             * @param password
             * @return
             * @throws SQLException
             */
            @Override
            public synchronized Connection getConnection(String username, String password) throws SQLException {
    
                // 数据库连接池为空,新创建一个连接,否则从池中捞取一个
                if (conns.isEmpty()) {
                    try {
                        Class.forName(dbDriver);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                    Connection connection = new ConnectionWrapper(conns,
                            DriverManager.getConnection(dbUrl, username, password), dbMax);
    
                    return connection;
                } else {
                    System.out.println("当前数据库连接池还剩 " + conns.size() + " 个连接。");
                    return conns.remove(conns.size() - 1);
                }
            }
    
            // 下面的方法都是在包装,不管它
            @Override
            public <T> T unwrap(Class<T> iface) throws SQLException {
                return null;
            }
    
            @Override
            public boolean isWrapperFor(Class<?> iface) throws SQLException {
                return false;
            }
    
            @Override
            public PrintWriter getLogWriter() throws SQLException {
                return null;
            }
    
            @Override
            public void setLogWriter(PrintWriter out) throws SQLException {
    
            }
    
            @Override
            public void setLoginTimeout(int seconds) throws SQLException {
    
            }
    
            @Override
            public int getLoginTimeout() throws SQLException {
                return 0;
            }
    
            @Override
            public Logger getParentLogger() throws SQLFeatureNotSupportedException {
                return null;
            }
        }
    
        /**
         * 数据库连接包装器,在连接关闭时放入连接池
         */
        private class ConnectionWrapper implements Connection {
            private List<Connection> conns; // 连接池
            private Connection connection; // 连接
            private int max; // 最大连接数
    
            public ConnectionWrapper(List<Connection> conns, Connection connection, int max) {
                this.conns = conns;
                this.connection = connection;
                this.max = max;
            }
    
            // 下面的方法都是在包装,除了close方法
            @Override
            public Statement createStatement() throws SQLException {
                return connection.createStatement();
            }
    
            @Override
            public PreparedStatement prepareStatement(String sql) throws SQLException {
                return connection.prepareStatement(sql);
            }
    
            @Override
            public CallableStatement prepareCall(String sql) throws SQLException {
                return connection.prepareCall(sql);
            }
    
            @Override
            public String nativeSQL(String sql) throws SQLException {
                return connection.nativeSQL(sql);
            }
    
            @Override
            public void setAutoCommit(boolean autoCommit) throws SQLException {
                connection.setAutoCommit(autoCommit);
            }
    
            @Override
            public boolean getAutoCommit() throws SQLException {
                return connection.getAutoCommit();
            }
    
            @Override
            public void commit() throws SQLException {
                connection.commit();
            }
    
            @Override
            public void rollback() throws SQLException {
                connection.rollback();
            }
    
            /**
             * 关闭方法需自己实现
             * @throws SQLException
             */
            @Override
            public void synchronized close() throws SQLException {
    
                // 当数据库连接数等于最大值时,回收,否则仍回池里复用
                if (conns.size() == max) {
                    connection.close();
                } else {
                    conns.add(this);
                }
            }
    
            @Override
            public boolean isClosed() throws SQLException {
                return false;
            }
    
            @Override
            public DatabaseMetaData getMetaData() throws SQLException {
                return connection.getMetaData();
            }
    
            @Override
            public void setReadOnly(boolean readOnly) throws SQLException {
                connection.setReadOnly(readOnly);
            }
    
            @Override
            public boolean isReadOnly() throws SQLException {
                return connection.isReadOnly();
            }
    
            @Override
            public void setCatalog(String catalog) throws SQLException {
                connection.setCatalog(catalog);
            }
    
            @Override
            public String getCatalog() throws SQLException {
                return connection.getCatalog();
            }
    
            @Override
            public void setTransactionIsolation(int level) throws SQLException {
                connection.setTransactionIsolation(level);
            }
    
            @Override
            public int getTransactionIsolation() throws SQLException {
                return connection.getTransactionIsolation();
            }
    
            @Override
            public SQLWarning getWarnings() throws SQLException {
                return connection.getWarnings();
            }
    
            @Override
            public void clearWarnings() throws SQLException {
                connection.clearWarnings();
            }
    
            @Override
            public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
                return connection.createStatement(resultSetType, resultSetConcurrency);
            }
    
            @Override
            public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
                return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
            }
    
            @Override
            public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
                return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
            }
    
            @Override
            public Map<String, Class<?>> getTypeMap() throws SQLException {
                return connection.getTypeMap();
            }
    
            @Override
            public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
                connection.setTypeMap(map);
            }
    
            @Override
            public void setHoldability(int holdability) throws SQLException {
                connection.setHoldability(holdability);
            }
    
            @Override
            public int getHoldability() throws SQLException {
                return connection.getHoldability();
            }
    
            @Override
            public Savepoint setSavepoint() throws SQLException {
                return connection.setSavepoint();
            }
    
            @Override
            public Savepoint setSavepoint(String name) throws SQLException {
                return connection.setSavepoint(name);
            }
    
            @Override
            public void rollback(Savepoint savepoint) throws SQLException {
                connection.rollback(savepoint);
            }
    
            @Override
            public void releaseSavepoint(Savepoint savepoint) throws SQLException {
                connection.releaseSavepoint(savepoint);
            }
    
            @Override
            public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
                return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
            }
    
            @Override
            public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
                return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
            }
    
            @Override
            public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
                return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
            }
    
            @Override
            public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
                return connection.prepareStatement(sql, autoGeneratedKeys);
            }
    
            @Override
            public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
                return connection.prepareStatement(sql, columnIndexes);
            }
    
            @Override
            public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
                return connection.prepareStatement(sql, columnNames);
            }
    
            @Override
            public Clob createClob() throws SQLException {
                return connection.createClob();
            }
    
            @Override
            public Blob createBlob() throws SQLException {
                return connection.createBlob();
            }
    
            @Override
            public NClob createNClob() throws SQLException {
                return connection.createNClob();
            }
    
            @Override
            public SQLXML createSQLXML() throws SQLException {
                return connection.createSQLXML();
            }
    
            @Override
            public boolean isValid(int timeout) throws SQLException {
                return connection.isValid(timeout);
            }
    
            @Override
            public void setClientInfo(String name, String value) throws SQLClientInfoException {
                connection.setClientInfo(name, value);
            }
    
            @Override
            public void setClientInfo(Properties properties) throws SQLClientInfoException {
                connection.setClientInfo(properties);
            }
    
            @Override
            public String getClientInfo(String name) throws SQLException {
                return connection.getClientInfo(name);
            }
    
            @Override
            public Properties getClientInfo() throws SQLException {
                return connection.getClientInfo();
            }
    
            @Override
            public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
                return connection.createArrayOf(typeName, elements);
            }
    
            @Override
            public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
                return connection.createStruct(typeName, attributes);
            }
    
            @Override
            public void setSchema(String schema) throws SQLException {
                connection.setSchema(schema);
            }
    
            @Override
            public String getSchema() throws SQLException {
                return connection.getSchema();
            }
    
            @Override
            public void abort(Executor executor) throws SQLException {
                connection.abort(executor);
            }
    
            @Override
            public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
                connection.setNetworkTimeout(executor, milliseconds);
            }
    
            @Override
            public int getNetworkTimeout() throws SQLException {
                return connection.getNetworkTimeout();
            }
    
            @Override
            public <T> T unwrap(Class<T> iface) throws SQLException {
                return connection.unwrap(iface);
            }
    
            @Override
            public boolean isWrapperFor(Class<?> iface) throws SQLException {
                return connection.isWrapperFor(iface);
            }
        }
    }

      运行结果:

    Thread-5 更新结果:1
    Thread-5 更新耗时: 609 微秒。
    Thread-4 更新结果:1
    Thread-4 更新耗时: 616 微秒。
    Thread-1 更新结果:1
    Thread-1 更新耗时: 625 微秒。
    Thread-3 更新结果:1
    Thread-3 更新耗时: 626 微秒。
    Thread-8 更新结果:1
    Thread-8 更新耗时: 587 微秒。
    Thread-7 更新结果:1
    Thread-7 更新耗时: 624 微秒。
    Thread-2 更新结果:1
    Thread-2 更新耗时: 641 微秒。
    Thread-0 更新结果:1
    Thread-0 更新耗时: 649 微秒。
    Thread-6 更新结果:1
    Thread-6 更新耗时: 646 微秒。
    Thread-9 更新结果:1
    Thread-9 更新耗时: 608 微秒。
    Thread-5 查询结果:[heihei.]
    Thread-5 查询耗时: 88 微秒。
    Thread-4 查询结果:[heihei.]
    Thread-4 查询耗时: 97 微秒。
    Thread-1 查询结果:[heihei.]
    Thread-1 查询耗时: 115 微秒。
    Thread-8 查询结果:[heihei.]
    Thread-8 查询耗时: 134 微秒。
    Thread-3 查询结果:[heihei.]
    Thread-3 查询耗时: 152 微秒。
    Thread-7 查询结果:[heihei.]
    Thread-7 查询耗时: 166 微秒。
    Thread-2 查询结果:[heihei.]
    Thread-2 查询耗时: 186 微秒。
    Thread-6 查询结果:[heihei.]
    Thread-6 查询耗时: 199 微秒。
    Thread-9 查询结果:[heihei.]
    Thread-9 查询耗时: 219 微秒。
    Thread-0 查询结果:[heihei.]
    Thread-0 查询耗时: 241 微秒。
    Thread-10 删除结果:1
    Thread-10 删除耗时: 32 微秒。
    Thread-11 删除结果:1
    Thread-11 删除耗时: 52 微秒。
    Thread-13 删除结果:1
    Thread-13 删除耗时: 77 微秒。
    Thread-12 删除结果:1
    Thread-12 删除耗时: 101 微秒。
    Thread-15 删除结果:1
    Thread-15 删除耗时: 122 微秒。
    Thread-14 删除结果:1
    Thread-14 删除耗时: 144 微秒。
    Thread-16 删除结果:1
    Thread-16 删除耗时: 165 微秒。
    Thread-17 删除结果:1
    Thread-17 删除耗时: 187 微秒。
    Thread-19 删除结果:1
    Thread-19 删除耗时: 209 微秒。
    Thread-18 删除结果:1
    Thread-18 删除耗时: 234 微秒。

      我们把SimpleDataSource类的getConnection() 改写一下:

            @Override
            public Connection getConnection() throws SQLException {
    
    //            // 加载驱动
    //            try {
    //                Class.forName(dbDriver);
    //            } catch (ClassNotFoundException e) {
    //                e.printStackTrace();
    //            }
    //
    //            // 每次返回一个新的数据库连接
    //            return DriverManager.getConnection(dbUrl, dbUserName, dbPasswd);
    
                // 每次从数据库连接池中获取
                return getConnection(dbUserName, dbPasswd);
            }

      再跑一遍:

    Thread-6 更新结果:1
    Thread-6 更新耗时: 663 微秒。
    Thread-5 更新结果:1
    Thread-5 更新耗时: 665 微秒。
    Thread-7 更新结果:1
    Thread-7 更新耗时: 665 微秒。
    Thread-9 更新结果:1
    Thread-9 更新耗时: 665 微秒。
    Thread-1 更新结果:1
    Thread-1 更新耗时: 668 微秒。
    Thread-8 更新结果:1
    Thread-8 更新耗时: 665 微秒。
    当前数据库连接池还剩 6 个连接。
    当前数据库连接池还剩 5 个连接。
    当前数据库连接池还剩 4 个连接。
    当前数据库连接池还剩 3 个连接。
    当前数据库连接池还剩 2 个连接。
    Thread-4 更新结果:1
    Thread-4 更新耗时: 682 微秒。
    当前数据库连接池还剩 2 个连接。
    当前数据库连接池还剩 1 个连接。
    Thread-3 更新结果:1
    Thread-3 更新耗时: 689 微秒。
    Thread-2 更新结果:1
    Thread-2 更新耗时: 691 微秒。
    Thread-0 更新结果:1
    Thread-0 更新耗时: 698 微秒。
    Thread-1 查询结果:[heihei.]
    Thread-9 查询结果:[heihei.]
    Thread-7 查询结果:[heihei.]
    Thread-8 查询结果:[heihei.]
    Thread-7 查询耗时: 35 微秒。
    Thread-9 查询耗时: 34 微秒。
    Thread-1 查询耗时: 34 微秒。
    Thread-8 查询耗时: 34 微秒。
    当前数据库连接池还剩 7 个连接。
    Thread-6 查询结果:[heihei.]
    Thread-6 查询耗时: 69 微秒。
    当前数据库连接池还剩 7 个连接。
    Thread-5 查询结果:[heihei.]
    Thread-5 查询耗时: 67 微秒。
    当前数据库连接池还剩 7 个连接。
    当前数据库连接池还剩 6 个连接。
    Thread-0 查询结果:[heihei.]
    Thread-0 查询耗时: 36 微秒。
    当前数据库连接池还剩 6 个连接。
    Thread-2 查询结果:[heihei.]
    Thread-2 查询耗时: 42 微秒。
    Thread-3 查询结果:[heihei.]
    Thread-3 查询耗时: 45 微秒。
    Thread-4 查询结果:[heihei.]
    Thread-4 查询耗时: 51 微秒。
    当前数据库连接池还剩 8 个连接。
    当前数据库连接池还剩 7 个连接。
    当前数据库连接池还剩 6 个连接。
    当前数据库连接池还剩 5 个连接。
    当前数据库连接池还剩 4 个连接。
    当前数据库连接池还剩 3 个连接。
    当前数据库连接池还剩 2 个连接。
    Thread-12 删除结果:1
    Thread-12 删除耗时: 2 微秒。
    Thread-11 删除结果:1
    Thread-11 删除耗时: 3 微秒。
    Thread-10 删除结果:1
    Thread-13 删除结果:1
    Thread-13 删除耗时: 9 微秒。
    Thread-10 删除耗时: 9 微秒。
    Thread-14 删除结果:1
    Thread-14 删除耗时: 9 微秒。
    Thread-16 删除结果:1
    Thread-16 删除耗时: 8 微秒。
    Thread-15 删除结果:1
    Thread-15 删除耗时: 9 微秒。
    当前数据库连接池还剩 8 个连接。
    当前数据库连接池还剩 7 个连接。
    当前数据库连接池还剩 6 个连接。
    Thread-19 删除结果:1
    Thread-19 删除耗时: 2 微秒。
    Thread-17 删除结果:1
    Thread-18 删除结果:1
    Thread-18 删除耗时: 2 微秒。
    Thread-17 删除耗时: 11 微秒。
  • 相关阅读:
    Saltstack module acl 详解
    Saltstack python client
    Saltstack简单使用
    P5488 差分与前缀和 NTT Lucas定理 多项式
    CF613D Kingdom and its Cities 虚树 树形dp 贪心
    7.1 NOI模拟赛 凸包套凸包 floyd 计算几何
    luogu P5633 最小度限制生成树 wqs二分
    7.1 NOI模拟赛 dp floyd
    springboot和springcloud
    springboot集成mybatis
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/14146744.html
Copyright © 2011-2022 走看看