2.1.4. 性能优化之数据库连接池原理详解

服务系统 -> 从DB获取数据,每次都需要创建通道链接,用完之后需要关闭,每次的开销比较大,所以就出现了连接池。

2.1.4.1. 大体思路

  • 初始化一部分链接
  • 获取链接
    • 判断物理链接是否有效
    • 扩容连接池
  • 创建链接
  • 关闭链接(把池连接返回连接池)

总结:就是把物理链接获取来放入缓存,提供外部获取到有效的缓存中的物理链接的过程。

这里的代码就是一个骨架。不过思路也是学到了。

2.1.4.2. 实现代码

/**
 * 池链接
 * @author : zhuqiang
 * @version : V1.0
 * @date : 2017/2/12 16:02
 */
public class PooledConnection {
    // 真正的物理链接对象
    private Connection connection;
    // 是否被占用
    private boolean isBusy = false;

    public PooledConnection(Connection connection) {
        this.connection = connection;
    }

    public void close() {
        this.isBusy = false;
    }

    /**
     * 测试链接方法
     * @param sql
     * @return
     */
    public ResultSet queryBySqlTest(String sql) {
        Statement sm = null;
        ResultSet rs = null;
        try {
            sm = connection.createStatement();
            rs = sm.executeQuery(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }

    public Connection getConnection() {
        return connection;
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public boolean isBusy() {
        return isBusy;
    }

    public void setBusy(boolean busy) {
        isBusy = busy;
    }
}


/**
 * 连接池
 * @author : zhuqiang
 * @version : V1.0
 * @date : 2017/2/12 15:59
 */
public interface IMyPool {
    PooledConnection getConnection();

    /**
     * 创建连接池
     * @param count 指定个数
     */
    void createConnection(int count);
}
/**
 * @author : zhuqiang
 * @version : V1.0
 * @date : 2017/2/12 16:03
 */
public class MyPoolImpl implements IMyPool {
    private Logger log = LoggerFactory.getLogger(getClass());
    private String jdbcDriver;
    private String jdbcUrl;
    private String userName;
    private String password;
    private int initCount; // 初始化数量
    private int stepSize; //步进数量,连接池最小一次性创建多少
    private int maxSize; //最大运行存在多少链接

    private Vector<PooledConnection> poolConnects = new Vector<>();

    public MyPoolImpl() {
        init();
    }

    // 初始化信息
    private void init() {
        InputStream is = this.getClass().getResourceAsStream("/jdbc.properties");
        Properties pro = new Properties();
        try {
            pro.load(is);
            this.jdbcDriver = pro.getProperty("jdbcDriver");
            this.jdbcUrl = pro.getProperty("jdbcUrl");
            this.userName = pro.getProperty("userName");
            this.password = pro.getProperty("password");
            this.initCount = Integer.valueOf(pro.getProperty("initCount", "1"));
            this.stepSize = Integer.valueOf(pro.getProperty("stepSize", "2"));
            this.maxSize = Integer.valueOf(pro.getProperty("maxSize", "5"));

            // 获取数据库驱动对象
            Driver driver = (Driver) Class.forName(this.jdbcDriver).newInstance();
            DriverManager.registerDriver(driver);
            createConnection(this.initCount);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public PooledConnection getConnection() {
        if (poolConnects.size() <= 0) {
            log.error("pools is no init");
            throw new RuntimeException("pools is no init");
        }
        // 获取一个有效的链接
        PooledConnection realConnection = getRealConnection();
        if (realConnection != null) {
            return realConnection;
        }

        while (realConnection == null) {
            // 需要扩容
            createConnection(stepSize);
            realConnection = getRealConnection();
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return realConnection;
    }

    /**
     * 获取一个有效的链接
     * @return
     */
    private synchronized PooledConnection getRealConnection() {
        // 非占用,有效
        for (PooledConnection poolConnect : poolConnects) {
            if (poolConnect.isBusy()) {
                continue;
            }
            // 判断是否合法的
            try {
                // isValid 源码是去查询数据库,如果不能正常返回则说明无效
                if (!poolConnect.getConnection().isValid(2000)) {
                    Connection validConnection = DriverManager.getConnection(this.jdbcUrl, this.userName, this.password);
                    poolConnect.setConnection(validConnection);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            poolConnect.setBusy(true);
            return poolConnect;
        }
        return null;
    }

    @Override
    public void createConnection(int count) {
        int size = poolConnects.size();
        if (count > 0 && (size + count) < maxSize) {
            for (int i = 0; i < count; i++) {
                try {
                    Connection connection = DriverManager.getConnection(this.jdbcUrl, this.userName, this.password);
                    PooledConnection pooledConnection = new PooledConnection(connection);
                    poolConnects.add(pooledConnection);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } else {
            log.error("创建池链接失败:currentSize={},after create sie={},maxSize={},超过上限", size, size + count, maxSize);
            throw new RuntimeException("创建池链接失败:大于 maxSize");
        }
    }
}

2.1.4.3. 测试用例

public class MyPoolImplTest {
    private Logger log = LoggerFactory.getLogger(getClass());
    private IMyPool pool = new MyPoolImpl();

    private synchronized void selectData() throws SQLException {
        PooledConnection connection = pool.getConnection();
        ResultSet resultSet = connection.queryBySqlTest("SELECT * FROM sys_config");
        while (resultSet.next()) {
            log.info(resultSet.getString("SKEY"));
        }
        resultSet.close();
        // 这里一定要记得关闭
        connection.close();
    }

    @Test
    public void singleTest() throws SQLException, InterruptedException {
        for (int i = 0; i < 101; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        selectData();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }, i + "").start();
        }
        synchronized (this){
            wait();
        }
    }
}

2.1.4.4. 更多的测试信息

mysql 的最大链接数量在:MySQL\MySQL Server 5.5\my.ini 中max_connections=100,修改该数量可以有效的测试连接池。

© All Rights Reserved            updated 2017-08-03 16:56:54

results matching ""

    No results matching ""