一种多线程应用程序访问数据库的方法

文档序号:6619508阅读:297来源:国知局
专利名称:一种多线程应用程序访问数据库的方法
技术领域
本发明涉及数据库访问技术领域,特别是指一种多线程应用程序访问数据库的方法。
背景技术
JDBCTMAPI是Java程序与数据库交互的一种应用程序接口,由一些Java语言编写的类和界面组成,用以使Java应用程序向数据库发送SQL语句以访问数据库。当然,要实现Java程序与数据库通讯,还需要加载JDBC驱动程序(JDBC Driver)(驱动程序可由DBMS厂商、或第三方厂商提供)。当Java程序已经加载了JDBC驱动后,就可以通过JDBC API提供的DriverManager类与数据库建立访问通道。
在访问数据库过程中,JDBC API通过其DriverManager.getConnection()函数获得数据库连接(Connection),一旦获得了与数据库的连接(Connection),Java应用程序就可以通过JDBC API向数据库发送SQL语句,去检索或者操作数据库。
同时参见图1示出的JDBC访问数据库的流程图,下面的斜体字部分示出了JDBC使用命令对象(Statement对象)执行数据库查询或者更新的基本逻辑//------调用getConnection()方法,获得一个与数据库的连接对象(Connection对象)------Connection con=DriverManager.getConnection()//------创建命令对象(Statement对象)并准备一个SQL语句,这里的SQL语句是被立即执行的语句Statement对象------Statement stmt=con.createStatement()
/*查询SQL命令例子,其中aSQL是一个数据库查询命令*///------使用Statement对象执行查询qurey语句(为了执行Statement对象,被发送到数据库的SQL语句aSQL将作为参数提供给Statement对象),得到一个存储结果的ResultSet对象ResultSet results=prepStmt.executeQuery(aSQL)//-----从ResultSet读取数据,关闭该对象接口------results. close()/*更新SQL命令例子,其中bSQL是一个数据库更新命令*///------使用Statement对象执行更新update语句------int rowsAffected=prepStmt.execute Update(bSQL)//------关闭Statement对象接口------stmt.close()//------关闭连接------con.close()上面的例子中,使用Statement对象(命令的对象)执行SQL命令,SQL命令由数据库侧DBMS(数据库管理系统)接收后,执行该SQL命令时,DBMS要进行以下的步骤第一步骤解析接受到的SQL语句;第二步骤编译此SQL语句;第三步骤计划和优化数据获取的路径;第四步骤执行SQL命令并返回结果给请求端Statement对象。
使用Statement对象实现数据库的访问的缺点是,当出现Statement对象执行SQL的命令被数据库DBMS再次调用时,DBMS还会重复上面的四个步骤,重复的解析、编译过程大大影响了访问数据库的性能。
为了解决上述问题,在Statement对象接口技术的基础上发展出PreparedStatement对象(准备好的命令对象)接口技术。PreparedStatement对象包含已编译的SQL语句,这就是使语句“准备好”。包含于PreparedStatement对象中的SQL语句可具有一个或多个IN参数。由于PreparedStatement对象已预编译过,所以其执行速度要快于Statement对象。
使用该技术,上面所述的DBMS所执行的前三步骤可以进行优化或者省略掉。同时参见图1示出的JDBC访问数据库的流程图,下面斜体字示出了JDBC一个使用PreparedStatement对象执行数据库查询或者更新的基本逻辑//------调用getConnection()方法,获得一个与数据库的连接对象(Connection对象)-----Connection con=DriverManager.getConnection()//------创建命令对象(Statement对象)并准备一个SQL语句,这里的SQL语句是被编译的语句PreparedStatement对象-----PreparedStatement prepStmt=con.prepareStatement(aSQL)prepStmt.setInt(1,integerParam)prepStmt.setString(2,stringParam)/*查询SQL命令例子,其中aSQL是一个数据库查询命令*/ResultSet results=prepStmt.executeQuery(aSQL)//-----从ResultSet读取数据------results.close()/*更新SQL命令例子,其中bSQL是一个数据库更新命令*/int rowsAffected=prepStmt.execute Update(bSQL)prepStmt.close()con.close()采用PreparedStatement对象技术实现上述优点来提高数据库访问的性能的关键技术其中之一是采用了连接池的策略。下面对目前采用的两种连接池技术进行具体说明。
1、缓存Connection的连接池连接池用来缓存Connection对象,使用连接池,缓存入池中的Connection仅需创建一次,之后会得到重用,可避免重复创建连接所耗费的时间和资源。
目前,DBMS厂商提供了JDBC API的DataSource和PooledConnection/XAConnection接口,可从这些接口中获得Connection并缓存到连接池。其中DataSource接口对象是典型的通过JNDI服务获取的实例。还包括如下示出的获得Connection的接口a、ConnectionPoolDataSource对象,可基于连接请求提供PooledConnection连接对象;b、XADataSource对象,可基于连接请求提供XAConnection连接对象。
连接池可以通过预先与DBMS建立连接的方式建立(称之为热连接)或者向数据库发出第一个请求的时候建立(称之为懒连接),甚至两种方式结合获取(先获取部分热连接,当获取后续连接的平均时间增加的时候,继续用懒连接的方式创建连接并缓冲到内区)。
2、缓存Connection和PreparedStatement对象的连接池不管PreparedStatement对象被应用程序在何时创建,都随同Connection一起缓存到连接池。这样,当PreparedStatement对象执行aSQL的命令被数据库DBMS再次调用时,与Connection一起缓冲到内存的编译后的PreparedStatement对象也可以连同Connection被应用程序再次使用,相对于仅连接的池化来说,进一步的,DBMS不需要重复解析SQL的步骤了。
但是,目前使用所述连接池访问数据库的方式还是存在着如下的缺点1、对于多线程应用程序,如果每个线程都从连接池获取Connection并执行一个SQL命令,那么此Connection直到被第一个使用的线程执行完SQL命令并返回值将该Connection和PreparedStatement对象释放回连接池后,该Connection和PreparedStatement对象才能被其他的线程使用。也就是说,在任意时刻一个Connection和PreparedStatement对象只能被一个线程使用,否则可能出现一个线程在发送一个查询而另一个线程正在接受结果,而导致访问数据库的混乱。
从另一个角度来看,在数据库连接上执行SQL命令的时候,此Connection会被此线程独占,对于其他线程来说,若同时请求获得该Connection时,其现象是调用该Connection发生阻塞,即不被允许调用,从而造成数据库访问性能的降低。即使是JDBC 3.0中也没有给出解决多线程同时访问数据库阻塞的方案。
2、对于大多数的应用程序的数据访问,不同的PreparedStatement对象的被执行次数也是不同的,一些命令可能被频繁执行,而有些命令很少被执行,甚至很有可能被执行的就那么一次而已,因此应用程序要求已池化的PreparedStatement对象个数应该依赖于或者取决于SQL命令被执行的次数。但是,上述方案中,与Connection一起缓存在连接池中的PreparedStatement对象的个数在大多数情况下是不可配置的,故无法根据不同应用程序访问情况进行调整,从而导致连接池中缓存的PreparedStatement对象个数难以做到负载均衡。

发明内容
有鉴于此,本发明的主要目的在于提供了一种多线程应用程序访问数据库的方法,以解决多线程程序通过JDBC API访问数据库阻塞问题,以提高数据库访问性能。
实现本发明所述的多线程应用程序访问数据库的方法,需要预先建立到数据库的不同的连接并进行缓存,并分别在所述不同连接里建立要使用的命令对象并缓存入命令池,当某个线程调用某连接和该连接上的命令对象访问数据库时,包括以下步骤A、判断命令池中是否有未被调用的所述命令对象,若有,则执行下一步,否则选择一个连接并在该连接上建立所述命令对象;B、选择所述的命令对象和该命令对象所属的连接;C、所述线程调用所选择的连接和命令对象访问数据库。
其中,所述分别在不同连接里建立要使用的命令对象的步骤包括从配置文件或者数据库配置项表中读取命令池的配置信息来构建命令池;所述配置信息包括要创建所述命令对象的数量;获取连接对象,根据所述配置信息依次在不同的连接对象上创建所述的命令对象。
其中,所述配置信息更改时,读取更新的配置信息更新命令池的配置。
其中,该方法进一步包括对命令池中的各个命令对象的调用的频率进行统计,在特定的周期内删除命令池中被调用频率低的命令对象。
其中,在线程结束调用所述连接和命令对象时,进一步包括该线程释放命令对象和所述的连接的步骤。
其中,取消使用所述命令池时,进一步包括销毁所述命令池的步骤。
其中,所述的命令对象为PreparedStatement对象。
由上述方法可以看出,本发明为每一个SQL命令提供一种独立于连接池的PreparedStatement命令对象缓冲池,在各个连接上均建立相应的PreparedStatement命令对象,从而避免某连接被占用而无法调用该PreparedStatement命令对象导致的不能访问数据库,解决了Java多线程应用程序访问数据库的阻塞问题。实现无事务操作且参数化的动态的SQL命令的无阻塞的需求。
另一方面,每一个SQL命令对应的PreparedStatement对象池的缓冲大小可配置,即缓存的PreparedStatement对象的个数可配置,并且根据被执行的SQL命令的频率动态调整PreparedStatement对象的个数,实现提供负载均衡。实现了在已建立的数据库连接上发出SQL命令的负载均衡。


图1为JDBC访问数据库的示意图。
图2为本发明PreparedStatement对象池的示意图。
图3为本发明访问数据库流程图。
图4为PreparedStatement对象池的建立、使用以及销毁流程图。
具体实施例方式
对于背景技术所述的连接池技术访问数据库,当出现多线程访问数据库时出现的阻塞问题,可以理解为从某个单一的Connection上生成PreparedStatement对象,然后多线程并发访问该PreparedStatement对象,就如独木桥上的行人,只能排队过桥,因此引起了多线程并发访问数据库的阻塞。
本发明的实现机制是这样的根据实际的应用情况,预先生成多个数据库连接(Connection),然后在不同的Connection上生成多个PreparedStatement对象,使得每个Connection上都有实现相同功能的PreparedStatement对象,然后把不同的PreparedStatement对象放入命令池(缓存区),不同的线程从池中获取的PreparedStatement对象时是从不同的Connection上获取的PreparedStatement对象,从而实现了线程的并发访问。如果在同一个堤口上架设了多度桥梁为多个行人服务。
下面对本发明所述的多线程应用程序访问数据库的方法进行说明。
预先,要初始化建立不同的Connection,以及在不同的Connection上建立要使用的PreparedStatement对象,并将所建立的PreparedStatement对象和Connection缓存入命令池。
如图2示出了本发明所建立的存放PreparedStatement对象的命令池的示意图,在池中缓存有不同的Connection,对于每个不同的Connection,又分配有不同个数的PreparedStatement对象。如图,图中的连接对象C1、C2、C3以及C4是所缓存的到数据库的连接,对于每一个连接,分别拥有缓存的PreparedStatement对象S1、S2、S3以及S4。命令对象池内的对应每一个SQL命令的PreparedStatement对象的个数可以在配置文件中或者数据库的配置项表格中进行配置。
参见图3,在初始化好上述命令池后,当某个线程访问数据库时,包括以下步骤
步骤301判断命令池中是否有未被调用的所述PreparedStatement对象,有,则下一步,否则在一Connection上建立所述PreparedStatement对象;步骤302选择该PreparedStatement对象、该PreparedStatement对象所属的Connection;步骤303该线程调用所选择的Connection和PreparedStatement对象访问数据库;步骤304在结束该调用时,释放PreparedStatement对象回命令池,结束。
另外,还可以对每个Connection中的PreparedStatement对象的个数进行动态调整,例如,对于预先建立的PreparedStatement对象或者是在步骤301中建立的PreparedStatement对象,可以对各个PreparedStatement对象的调用的频率进行统计,并在特定的周期内删除命令池中被调用频率低的PreparedStatement对象,以保证在必要的时刻有足够的空间建立新的PreparedStatement对象。
下面,对本发明的各个步骤进行详细的描述,来说明本发明是如何实现的。参照图4示出的缓存PreparedStatement对象的命令池的建立、使用以及销毁流程图,进行说明步骤401~403示出了命令池的初始化过程当应用程序启动的时候,命令池初始化模块首先获取一定数量的数据库连接(Connection),并从配置文件或者数据库配置项表中读取命令池的配置信息并传递给StatementPool对象去完成初始化工作。
一般地,考虑线程的并发数量和对应SQL命令的执行时长,需要对命令池内命令PreparedStatement对象的数量进行配置(相当于创建命令池的容量)。而这些配置信息的数据结构是这样的ID(SQL命令对象的标识),MIN(SQL命令在命令对象池的最小值),MAX(SQL命令在命令对象池的最大值),配置多个PreparedStatement对象来满足多线程的并发访问要求。其中MIN和MAX需要根据实际的使用场合进行配置,尽量考虑实际的应用场合的并发线程数量为MIN值,MAX值可以考虑为自增长模式的命令池使用,而自增长的生成机制和初始化命令池的机制是一样的。
实际应用中,将上述配置信息放入配置文件(如写入Config.txt),然后程序可以动态读取配置文件,实现动态配置命令池。如下面的一种实现动态配置的机制设定配置文件Config.txt包含如下4条SQL命令的信息SQLID=1,MIN=4,MAX=4SQLID=2,MIN=4,MAX=4初始化时,启动一个定时器,在指定的T时间读取上述的配置文件Config.txt,如果是第一次读取,则根据读取的配置值完成命令池的构建;如果已经读取过配置文件,则判断跟前一次读取的内容和本次读取的内容是否一致,如果一致则不更新命令池;如果不一致,则根据读取的Config.txt更新命令池的配置。
上面是命令池的初始化(相当于创建缓存区的容量),然后在获取的Connection上创建PreparedStatement对象写入命令池参见下面描述的初始化命令池的业务逻辑过程,如下第一步骤将调用者传递的初始化参数分配到不同的数据库连接(Connection)。初始化所需的参数如下需要用来创建命令对象池的数据库连接(Connection);每一种PreparedStatement对象的SQL语句字符串;每一种PreparedStatement对象的实例个数。
第二步骤根据所传递的初始化参数,从不同的数据库连接(Connection)上循环生成相应的PreparedStatement对象。
具体可如图2所示,传入C1、C2、C3和C4连接对象(C1、C2、C3、C4分别为不同的Connection对象)以及命令对象的大小为6。为了实现多线程访问数据库命令对象的负载均衡机制,先从C1上生成S1,继续从C2、C3、C4上生成新的S1对象(S1、S2、S3和S4分别为不同的SQL命令对应的PreparedStatement对象,相同的PreparedStatement对象构成命令队列),然后循环到C1和C2上继续创建剩下的两个命令对象。类似的机制创建S2、S3和S4命令对象。这样,形成了如图2所示的命令池,此命令池分别使用4个Connection,在C1对象上有两个S1对象、一个S2对象、两个S3对象、一个S4对象;在C2对象上有两个S1对象、一个S2对象、两个S3对象、一个S4对象;在C3对象上有一个S1对象、两个S2对象、两个S3对象、一个S4对象;在C4对象上有一个S1对象、一个S2对象、两个S3对象、一个S4对象。相当于S1、S2、S3和S3四个命令队列分配在不同的连接对象上。
下面示出了初始化命令池的逻辑//-----初始化连接对象的指针------connectionPointer=0//-----生成不同命令对象的队列------Queue[]statementQueue is a new Array of Queue objects//-----循环生成命令对象,循环次数为命令队列配置的大小,由调用者传入此参数------for statementIndex from 0 to numberOfSQLs-1//-----轮询连接池,从不同的连接上生成命令对象------for cacheCount from 0 to aPStmtCountArray[statementIndex]-1//-----获取连接对象的指针------connectionPointer=++connectionPointer%numOfConnections//-----从获得的连接上生成命令对象------pStmt=aConnArray[connectionPointer].prepareStatement(aSQLArray[statementIndex])//-----把生成的命令对象放入到命令对列/命令池------statementQueue[statementIndex].add(pStmt)end for
end for需要指出的是StatementQueue假定是一个线程安全的FIFO(先进先出)存储器,且支持并发访问的数据结构。而连接队列被认为是创建命令缓冲池队列的简单循环队列。
另一方面,若在请求调用的过程中,判断如果已缓冲的PreparedStatement对象全部被应用程序使用,且应用逻辑请求一个新的PreparedStatement对象,那么必须在现存的基础上继续创建新的PreparedStatement对象并放入池内以供新的请求使用,该创建的过程和初始化的过程相同。
初始化后,不同的命令对象(PreparedStatement对象)就分布在不同的连接(Connection)上,从而多线程程序在访问PreparedStatement对象的时候,是从命令池中获得命令对象,也就是说是从不同的Connection上完成SQL操作的,从而分担了负荷。举例来说,如果某个线程从图2的命令池中获取PreparedStatement对象完成相应的SQL操作,则首先会从C1的连接上获取PreparedStatement对象来完成所述SQL操作,若此刻另外一个线程同时访问命令池,则这种多线程的机制会分配C2连接给另一线程,不会与第一个线程同时在C1上完成操作,后续的线程进行数据库访问继续在C3、C4、C1、C2这样的循环机制上获取PreparedStatement对象,从而实现了均衡负载以及数据库访问并发的效率的提升。
步骤404~408提供了某一线程从命令池中获取PreparedStatement对象以用来访问数据库具体的实现方式。这几个步骤是上述步骤301~302的具体的实现。
其中,需要传入的参数是命令对象的索引(此索引与初始化池的时候的SQL数组的索引下标一致)。
从命令池的获取命令接口的简要程序逻辑如下//-----判断命令池的使用情况,如果存在命令对象则从可使用的命令对象池重获取命令对象,并返回给调用者------If statementQueue[statementIndex]is not emptyreturn statementQueue[statementIndex].remove()//-----如果池内无可使用的命令对象,且是池的管理是自增长模式,则生成新的命令并返回给调用者------else if autoIncreaseStmtCache[statementIndex]is truereturn aConnArray[connectionPointer].prepareStatement(aSQLArray[statementIndex])//-----其他情况,则抛出异常给调用者------elsethrow error“Statement Not Available”在该线程获取了命令对象,及对应的连接对象时,该线程就可以使用该连接作为访问数据库的连接,将该命令对象来向数据库发送SQL语句命令来访问数据库,对数据库进行检索或者操作数据库,来实现上述步骤303的内容。根据某连接对象的命令对象对数据库进行的访问步骤和现有技术相同,不再赘述。
步骤409为该线程访问数据库结束,释放所调用的连接(Connection)和命令对象(PreparedStatement对象),以提供给后续的请求使用的步骤,是上述步骤304的具体实现。如下为释放命令对象到池中的应用逻辑释其中,必须传入的参数包括命令对象的索引、将要返回到池内的PreparedStatement命令对象。
释放命令接口到PreparedStatement命令池的简要程序逻辑如下statementQueue[statementIndex].add(aStatement)这里,进一步强调说明的是PreparedStatement命令并没有被关闭,只是释放到StatementQueue缓冲队列中以供后续的请求使用。
另外,步骤410是该命令池不再使用时,销毁该命令池的步骤,如下为销毁命令池的简要程序逻辑//-----从命令池中获取所有的命令队列,并循环释放所占用的资源------for statementIndex from 0 to numberOfSQLs-1//-----循环从相应的命令队列上获取命令------for cacheCount from 0 to aPStmtCountArray[statementIndex]-1//-----从命令队列中获取命令并释放资源------statementQueue[statementIndex].remove().close()end forend for//-----循环释放连接池的资源------for connectionIndex from 0 to numOfConnectionsaConnArray[connectionIndex].close()end for通过本发明所述方法,不同的线程可以使用不同的连接(Connection)所提供的同一命令对象(PreparedStatement对象)访问数据库,消除了多线程应用程序数据库连接被线程独占的阻塞,并且在同一个连接上允许被不同的线程执行不同的命令,这样在给定的性能标准上,有效地减少应用与数据库建立的连接数。
通过不同连接上对命令对象的分布,实现对不同的SQL命令分别在连接上实现了负载均衡控制,满足了不同SQL命令不同执行的频率的有效性并提升了性能。
另外,简化了应用逻辑,因为只要关心PreparedStatement对象,不需要了解从数据库连接中如何获取命令对象和准备(解析SQL语句、编译SQL语句以及计划和优化数据获取路径)对象的过程。
以上所述仅为本发明的较佳实施例而已,并不用以限制本发明,凡在本发明的精神和原则之内,所作的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。
权利要求
1.一种多线程应用程序访问数据库的方法,预先建立到数据库的不同的连接并进行缓存,其特征在于,分别在所述不同连接里建立要使用的命令对象并缓存入命令池,当某个线程调用某连接和该连接上的命令对象访问数据库时,包括以下步骤A、判断命令池中是否有未被调用的所述命令对象,若有,则执行下一步,否则选择一个连接并在该连接上建立所述命令对象;B、选择所述的命令对象和该命令对象所属的连接;C、所述线程调用所选择的连接和命令对象访问数据库。
2.根据权利要求1所述的方法,其特征在于,所述分别在不同连接里建立要使用的命令对象的步骤包括从配置文件或者数据库配置项表中读取命令池的配置信息来构建命令池;所述配置信息包括要创建所述命令对象的数量;获取连接对象,根据所述配置信息依次在不同的连接对象上创建所述的命令对象。
3.根据权利要求2所述的方法,其特征在于,所述配置信息更改时,读取更新的配置信息更新命令池的配置。
4.根据权利要求1所述的方法,其特征在于,进一步包括对命令池中的各个命令对象的调用的频率进行统计,在特定的周期内删除命令池中被调用频率低的命令对象。
5.根据权利要求1所述的方法,其特征在于,在线程结束调用所述连接和命令对象时,进一步包括该线程释放命令对象和所述的连接的步骤。
6.根据权利要求1所述的方法,其特征在于,取消使用所述命令池时,进一步包括销毁所述命令池的步骤。
7.根据权利要求1所述的方法,其特征在于,所述的命令对象为准备好的命令对象PreparedStatement对象。
全文摘要
本发明提供了一种多线程应用程序访问数据库的方法,预先建立不同的连接进行缓存,在不同连接里分别建立要使用的命令对象缓存入命令池,当某个线程访问数据库时,包括以下步骤A、判断命令池中是否有未被调用的所述命令对象,若有,则执行步骤B,否则在一连接对象上建立所述命令对象;B、选择所述命令对象、命令对象所属的连接对象;C、该线程调用所选取的连接对象和命令对象访问数据库;在结束该调用时,释放命令对象回命令池。使用本发明,解决了多线程程序通过JDBC API访问数据库阻塞问题,提高数据库的访问性能。
文档编号G06F9/46GK1869939SQ20051007302
公开日2006年11月29日 申请日期2005年5月27日 优先权日2005年5月27日
发明者张前锋, 斯瑞佛尼, 郁建中, 商托斯 申请人:华为技术有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1