`
季铵盐
  • 浏览: 57384 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

java数据库连接池

阅读更多
[color=blue][/color][size=x-small][/size][align=left][/align]

最近研究了下java数据库连接池技术,废话不说啦 ,直接如下:
连接:一个连接池最基本的问题就是需要提供连接。
监控:对连接池提供有效的监控手段,监控要实现动态生效更好,可通过监控对连接使用率、可能泄露的连接、获取连接平均响应时间、获取连接最大响应时间、连接最长归还时间、创建连接总数、物理释放连接数,最好能够根据这些数据再加上时间,形成一些可视化的报表,方便开发人员或者是维护人员对连接池进行优化,以达到在建立过剩的连接情况下最大限度的达到连接重用。
高并发:必须能够应对高并发,保证连接池的稳定性的同时,达到高性能。
数据源:好的连接池应当以一种数据源的形式出现DataSource,这也是连接池的一个规范。
高性能:连接池对于应用来说扮演着重要的角色,同时也会影响到应用的性能。所以构建高性能的连接池至关重要。
缓存:缓存要从多个维度去理解,比如连接缓存、查询结果缓存。只有很好的应用缓存特性才能够构建高性能的连接池。
连接释放:连接释放应当分为两种:一种是对连接的物理释放,也就是真正的断开与数据库的连接,通常需要在关闭连接池前做的事情。一种是对连接的逻辑释放,是指通过该操作将一些用过的连接放回到连接池中。而这里的连接释放方式需要注意,应当通过Connection的close方法来进行操作,这样可以避免改变用户的编程习惯。
池:池的设计对于连接池非常重要,可以说是连接池的核心灵魂,经过认真周密设计的池,可能满足高并发、高性能、高重用性等特点。
连接可用性探测:连接池应当对自身提供给应用的连接负责,提供连接前,必须对连接是否存活,是否可用做检测,将不可用的连接进行物理释放,以免造成应用拿到不可用的连接,导致操作失败。
灵活的配置:通常连接池是需要根据具体的应用场景来进行个性化的配置,以便在任何场景下连接池都可以以最优的方式工作,这需要连接池支持当前比较常见的配置方式,例如:xml、properties、注解。提供丰富的多样的配置方式还不够,最好还要实现配置的动态生效,即不重启应用的情况下,动态使得最新的配置进行工作。
安全性:连接池是需要配置数据库的一些基本信息的,例如:url、password、username等,这些涉及到安全性,至少应当对password进行加密,防止数据库密码泄露。
学习成本:通常情况下,连接池的学习成本是基本相同的,连接池比较的通用。这里所将的学习成本是指要尽量贴近或者是完全按照规范进行实现,这样可以大大减少用户的学习成本,可保持良好的上手性和编程习惯。
集成问题:应当无缝的集成到应用当中,例如:spring、tomcat等。
多数据源:应当提供多数据源的解决方案。
分布式事务:在多数据源的场景下,应当实现分布式事务。


在我实现的这个连接池中,主要实现了两个类:DBManager和DBpoolFactory两个类,而DBconn和pstobj主要是对connection和PreparedStatement的封装,以便实现一些功能,以达到解耦和对数据库连接池的优化。

DBManager类:主要对数据库连接池的管理,DBManager采用的单例模式,对外提供对象,和对数据的初始化配置,通过DBManager,可以直接获取来自连接池的连接和回收管理功能。


package com.game.DB;

import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Iterator;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.game.logger.DBLogger;


/**
*
* 数据库连接池配置
* @author CHENLEI
*
*/
public class DBManager {
/**
* 閥值和定时机制管理内存
*/
public static boolean isRunning=true;
public  static String dataDriver="";//驱动
public  static String  url="";//连接串
public  static String  username="";//连接串
public  static String  password="";//连接串

public static int conMaxNum=0;//连接池最大连接数量
public static int conMinNum=0;//初始化连接池大小
public static int conStep=0;//增加的步长


public static int DBconMaxNum=0;//连接池最大连接数量
public static int DBconMinNum=0;//初始化连接池大小
public static int DBconStep=0;//增加的步长
public static int DBcomMax=0;//单个连接最大连接数

public static int Port=0;//端口号
public static byte ThreadMax=0;//dispatcher线程数量
public static byte poolsNum=0;//线程池线程的数量

private static DBManager instance=new DBManager();
private DBManager(){

}
public static DBManager getinstance(){
return instance;
}
/**
* 获取pst对象
* @param sql
* @return
*/
public static pstObj getObj(String sql){

return DBPoolFactory.getInstance().getObj(sql);
}

/**
*释放pst
* @param pstObj
*/
public static void putObj(pstObj pstObj){

DBPoolFactory.getInstance().putPstobj(pstObj);
}

/**
* 获取一个 Connection
* @return
*/
public static Connection getConn(){
return DBPoolFactory.getInstance().getConn();
}
/**
* 释放connection
* @param conn
*/
public static void putConnction(Connection conn){

DBPoolFactory.getInstance().putConn(conn);
}
/***
* 初始化数据库基础数据
*/
public void IntiData(){
try {
DBLogger.logger.info("res/basedata.xml is start......");
InputStream in=new FileInputStream("res/basedata.xml");
SAXReader re=new SAXReader();
Document doc=re.read(in);
Element el=null;
Iterator<?> ite=doc.selectNodes("sys").iterator();// 重根节点开始迭代,返回节点集合
while(ite.hasNext()){
el=(Element)ite.next();//取出根节点,遍历每个子节点,子节点可能也是父节点(sys1)
dataDriver=el.elementText("dataDriver").trim();//取出子节点的值
url=el.elementText("url").trim();
username=el.elementText("username").trim();
password=el.elementText("password").trim();

ThreadMax=Byte.parseByte(el.elementText("ThreadMax").trim());
poolsNum=Byte.parseByte(el.elementText("poolsNum").trim());
Port=Integer.parseInt(el.elementText("Port").trim());


conMaxNum=Integer.parseInt(el.elementText("conMaxNum").trim());
conMinNum=Integer.parseInt(el.elementText("conMinNum").trim());
conStep=Integer.parseInt(el.elementText("conStep").trim());


DBconMaxNum=Integer.parseInt(el.elementText("DBconMaxNum").trim());
DBconMinNum=Integer.parseInt(el.elementText("DBconMinNum").trim());
DBconStep=Integer.parseInt(el.elementText("DBconStep").trim());
DBcomMax=Integer.parseInt(el.elementText("DBcomMax").trim());

}
} catch (Exception e) {
}
}

}

DBPoolFactory类:具体的创建连接,回收连接,PreparedStatement对象的缓存(这个PreparedStatement,还没有仔细的研究)检查连接的有效性;超时处理,维护连接数量(其实这里我个人觉得在这一块,还学要优化,研究中。。。。因为对于不同的应用服务的话,他的变化很大的)。
package com.game.DB;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;


import com.game.logger.DBLogger;

/**
* 数据库连接池
* @author CHENLEI
*
*/
public class DBPoolFactory implements Runnable{

public static int conInUseNum=0;//正在使用的connection连接数

public static int DBconInUse=0;//正在使用的connection连接数
private static  ConcurrentLinkedQueue<Connection> connections=new ConcurrentLinkedQueue<Connection>();

private static ConcurrentHashMap<Integer, DBconn> DBconns=new ConcurrentHashMap<Integer, DBconn>();//连接池

private static ConcurrentLinkedQueue<pstObj> pstobj=new ConcurrentLinkedQueue<pstObj>();

private static DBPoolFactory factory=new DBPoolFactory();
private DBPoolFactory(){
}

public static DBPoolFactory getInstance(){
return factory;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(DBManager.isRunning){
/**
* 检查创建的连接数
*/
Connection conn=null;
while(connections.size()+conInUseNum<DBManager.conMinNum){
conn=creatCon();
if(conn!=null){
connections.add(conn);
}else{
break;
}
}
/**
* 检查创建的连接数
*/
while(DBconns.size()+DBconInUse<DBManager.DBconMinNum){
conn=creatCon();
if(conn!=null){
DBconn dbcon=new DBconn(conn);
DBconns.put(dbcon.getID(), dbcon);
}else{
break;
}
}
/***
* 检查连接的有效性
*/
for(Map.Entry<Integer, DBconn> entrys:DBconns.entrySet()){
DBconn DBconn=entrys.getValue();
checkConn(DBconn.getConn());
}
/***
* 检查连接的有效性
*/
for(Connection con:connections){
checkConn(con);
}


}
}

/**
* 释放连接数
* 注意:保证创建的连接数不超过配置提供的最大连接数(连接超时,访问数据库失败)
*/
public synchronized void putConn(Connection conn){
if(connections.size()+conInUseNum>DBManager.conMaxNum){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
connections.add(conn);
conInUseNum--;
}
/**
* 获取一个连接,可能返回null
* 超时2秒----等待连接释放
* @return
*/
public synchronized Connection getConn(){
Connection conn=null;
int attempNum=0;
while(conn==null&&attempNum<20){
conn=AttempConn();
if(conn==null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}else{
break;
}
attempNum++;
}
if(conn!=null){
conInUseNum++;
}

// long startTime=System.currentTimeMillis();
// while((conn=AttempConn())==null){//当获取的连接为null
// try {
// wait(2000);//是尝试获取连接的线程阻塞
// } catch (InterruptedException e) {
// if(System.currentTimeMillis()-startTime>2000){
// return null;
// }
// }
// }
return conn;
}
/****
* 尝试获取一个连接且当前该连接的连接数最小,如果派发池没有可用,
* 那么创建新的连接
* @return null
*/
public Connection AttempConn(){
Connection con=null;
//尝试获取一个连接
con=connections.poll();
if(con==null){// 空闲池子中没有可用的连接
newCreatConn();
con=connections.poll();
}
return con;
}
/**
* 检查连接数是否达conMaxNum;没有则创建新的物理连接
* 按配置的步长,向缓冲池注入新的连接
*/
public void newCreatConn(){
Connection conn=null;
if(DBManager.conMaxNum-connections.size()-conInUseNum>=DBManager.conStep){
for(int i=0;i<DBManager.conStep;i++){
conn=creatCon();
connections.add(conn);
}
DBLogger.logger.info("连接注入个数:"+DBManager.conStep);
}else{
int num=DBManager.conMaxNum-connections.size()-conInUseNum;
if(num>0){
for(int j=0;j<num;j++){
conn=creatCon();
connections.add(conn);
}
DBLogger.logger.info("连接注入个数:"+num);
}else{
DBLogger.logger.info("连接注入失败");
}
}
}
/*******************************************
* 创建物理一个连接  ,如果失败尝试下一个连接                          
* 时间超时 3秒
*
*****************************************************/
public Connection creatCon() {
Connection con=null;
int atemp=0;
while(con==null&&atemp<30){
try {
Class.forName(DBManager.dataDriver);
con=DriverManager.getConnection(DBManager.url, DBManager.username, DBManager.password);
DBLogger.logger.info("创建连接[ok]");
} catch (Exception e) {
// TODO Auto-generated catch block
try {
Thread.sleep(100);
DBLogger.logger.info("创建连接失败", e);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
}
}
atemp++;
}
return con;
}

/**
* 检查连接的有效性,移除无效连接
* @return
*/
public void checkConn(Connection con){
try {
PreparedStatement ps=con.prepareStatement("SELECT 0 FROM DUAL");
ResultSet re=ps.executeQuery();
if(ps!=null&&re!=null)ps.close();re.close();
} catch (SQLException e) {
connections.remove(con);
}
}



public static ConcurrentHashMap<Integer, DBconn> getConns() {
return DBconns;
}

public static void setConns(ConcurrentHashMap<Integer, DBconn> conns) {
DBPoolFactory.DBconns = conns;
}

/***
* 获取DBconn对象,派发pstObj对象
* 超时2000毫秒
* 可能返回null
*/
public synchronized pstObj getObj(String sql){
pstObj pst=null;
DBconn dp=AttempGetDBconn();
if(dp==null){
newCreatDBConn();
dp=AttempGetDBconn();
}else if(dp.getCurrentNum()>DBManager.DBcomMax){//大于最大的连接数
newCreatDBConn();
dp=AttempGetDBconn();
}
if(dp!=null){
DBconInUse++;
dp.setCurrentNum(dp.getCurrentNum()+1);
dp.setLastTimeacess(System.currentTimeMillis());
}


pst=pstobj.poll();
if(pst==null){
pst=new pstObj();
try {
pst.setStatement(dp.getConn().prepareStatement(sql));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return pst;

}


public DBconn AttempGetDBconn(){
DBconn dp=null;
int tryNum=0;
dp=getDBconn();//尝试获取一个DBconn
if(dp==null){
newCreatDBConn();
dp=getDBconn();
}
/**
* 如果获取结果为NULL
* 等待可用资源释放 2秒
*/
while(dp==null&&tryNum<20){
dp=getDBconn();
if(dp==null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}else{
break;
}
tryNum++;
}
return dp;
}
/**
* 回收pstobj对戏那个
* @param pst
*/
public synchronized void putPstobj(pstObj pst){
int pstNum=pstobj.size();
Connection conn;
try {
conn = pst.getStatement().getConnection();
int keyID=conn.hashCode();
DBconn dp=DBconns.get(keyID);
if(pst.getStatement().getConnection().isClosed()){// 连接失效
pst.getStatement().close();
pst=null;
}else if(pstNum>5&&pstNum>2*pstNum){//pst对象创建过多
pst.getStatement().close();
pst=null;
}else{
pstobj.add(pst);
putConn(pst.getStatement().getConnection());
dp.setCurrentNum(dp.getCurrentNum()-1);
DBconInUse--;
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

}

/***
* 从连接池获取connection数最小的连接对象
* @return
*/
public  DBconn getDBconn(){
DBconn conn=null;
int kk=0;
DBconn tempCon=null;
for(Map.Entry<Integer, DBconn> entrys:DBconns.entrySet()){
conn=entrys.getValue();
if(kk==0){
tempCon=conn;
kk=1;
}else{
if(tempCon.getCurrentNum()>conn.getCurrentNum()){
tempCon=conn;
}
}

}
return tempCon;


/**
* 检查连接数是否达conMaxNum;没有则创建新的物理连接
* 按配置的步长,向缓冲池注入新的连接
*/
public void newCreatDBConn(){
Connection conn=null;
if(DBManager.DBconMaxNum-DBconns.size()-DBconInUse>=DBManager.DBconStep){
for(int i=0;i<DBManager.DBconStep;i++){
conn=creatCon();
DBconn db=new DBconn(conn);
DBconns.put(db.getID(),db);
}
DBLogger.logger.info("连接注入个数:"+DBManager.conStep);
}else{
int num=DBManager.DBconMaxNum-DBconns.size()-DBconInUse;
if(num>0){
for(int j=0;j<num;j++){
conn=creatCon();
DBconn db=new DBconn(conn);
DBconns.put(db.getID(),db);
}
DBLogger.logger.info("连接注入个数:"+num);
}else{
DBLogger.logger.info("连接注入失败");
}
}
}

}

DBconn类:主要对connection的封住,对外提供连接方法
package com.game.DB;

import java.sql.Connection;

public class DBconn {
private Connection conn=null;//连接对象
private int ID=0;//连接对象编号
private  int currentNum=0;//一个connection当前的连接数

private long lastTimeacess=0;//最近一次使用时间

public DBconn(Connection conn){
this.conn=conn;
this.ID=conn.hashCode();
}
/**一个connection当前的连接数***/
public int getCurrentNum(){
return currentNum;
}
/**改变一个connection当前的连接数***/
public void setCurrentNum(int currentNum) {
this.currentNum = currentNum;
}
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}

public Connection getConn() {
return conn;
}

public void setConn(Connection conn) {
this.conn = conn;
}
public long getLastTimeacess() {
return lastTimeacess;
}
public void setLastTimeacess(long lastTimeacess) {
this.lastTimeacess = lastTimeacess;
}
}


pstObj类:PreparedStatement的封装,

package com.game.DB;

import java.sql.PreparedStatement;

public class pstObj {
private PreparedStatement Statement=null;

public PreparedStatement getStatement() {
return Statement;
}
public void setStatement(PreparedStatement statement) {
Statement = statement;
}
}

/****************************************************************
下面做一个测试:
/**************************************************************


package Launcher;



import java.sql.SQLException;


import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;


import com.game.DB.DBManager;
import com.game.DB.DBPoolFactory;
import com.game.DB.pstObj;
import com.game.NioSocket.Gserver;


public class launcher {
private static Logger logger=Logger.getLogger(launcher.class.getClass());
/**
* @param args
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
// TODO Auto-generated method stub
DOMConfigurator.configure("res/log4j.xml");
logger.info("laoding logger is ok.......");

DBManager.getinstance().IntiData();
logger.info("初始化basedata.xml【ok】");


Thread t=new Thread(DBPoolFactory.getInstance());
t.setName("线程池");
t.setDaemon(false);
t.start();
logger.info("dataPool is start......");



/***启动服务器***/
new Gserver();

/*****************************************************************
*连接池测试
*****************************************************************/
// pstObj ps=null;
// ps=DBManager.getObj("INSERT INTO TEST(NUM)VALUES(?)");
// long s1=System.currentTimeMillis();
// for(int i=0;i<100;i++){
// try {
// ps.getStatement().setInt(1, i);
// ps.getStatement().addBatch();
// } catch (SQLException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// }
// ps.getStatement().executeBatch();
// System.out.println("耗用时间:"+ (System.currentTimeMillis()-s1));
// ps.getStatement().clearBatch();
// DBManager.putObj(ps);
//


pstObj ps=null;
long s1=System.currentTimeMillis();
for(int i=0;i<100;i++){
try {
ps=DBManager.getObj("INSERT INTO TEST(NUM)VALUES("+i+")");
ps.getStatement().executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
DBManager.putObj(ps);

}
}
System.out.println("lastingTime:"+ (System.currentTimeMillis()-s1));
}

}

不同的测试方式,插入100条记录大概2秒到3秒;这是批量插入addBatch();和单个插入的性能上的问题,这里就不在说,  主要是感觉这里面 还是很慢。希望javaer们一起学习交流下 ,恳请大牛指教哈

附上附件。。。。


















分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics