快捷搜索:

专家讲解优化Derby数据库程序性能

Derby这个完全Java开拓的开源的数据库也不例外,是以你必须包管它不会成为你法度榜样的一个瓶颈。只管人们可以在Derby的手册中找到关于这个话题周全的资料,我照样想更详尽的关注一下这些问题,基于我的履历供给一些详细的例子。本文将着重于那些由在大年夜的数据表中选择查询数据而孕育发生的法度榜样机能问题。

首先,有很多关于调剂Derby属性(诸如页面大年夜小懈弛存大年夜小等)的技术。改动这些参数可以在必然程度上调剂数据库的机能,然则在平日环境下,更主要的问题来自与你的法度榜样和数据库的设计,是以,我们必须首先关注这些问题,着末再来斟酌Derby的属性。

在接下来的段落里,我将先容一些能够优化法度榜样中有问题部分的技巧。然则,和其他机能优化操作一样,我们必要在优化前先丈量并确认问题所在。

一个简单的例子

让我们从一个简单的例子开始:假设我们Web法度榜样中拥有一个“search/list”的页面,要处置惩罚一个有靠近100,000行的表,并且那个表不是很小的(至少有10栏)。用简单的JDBC来写一个例子,这样我们可以专注在数据库和JDBC问题上来。这篇文章中先容的所有准则对所有的面向工具的映射对象都适用。

为了使得用户能够列出一个大年夜的表,平日应用下面简单的查询语句。

select * from tbl

对应的JDBC语句如下:

Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance();

Connection connection = DriverManager.getConnection (

"jdbc:derby://localhost:1527/testDb;");

Statement stmt = connection.createStatement();

ResultSet rs = stmt.executeQuery("select * from tbl");

ArrayList allResults = new ArrayList();while (rs.next()) {

// Object-Relation mapping code to populate your

// object from result set row

DomainObject domainObject = populate(rs);

allResults.add(modelObject);

}

System.out.println("Results Size: " + allResults.size());

在这儿,我们碰着了第一个问题。履行这样的代码,并孕育发生100,000(或更多)个domain工具将肯定会导致java用完客栈空间,孕育发生一个“java.lang.OutOfMemoryError”的差错。对付初学者来说,我们首先必须找到一个措施来使得这个法度榜样事情。

分页Result Sets

跟着法度榜样中数据量的增多,你首先想到的应该做的事便是为特定的记录(平日是视图)供给分页支持。正如你在这个先容性的例子中看到的,简单地去获取宏大年夜的result sets很轻易导致 out of memory的差错。

许多半据库办事器支持特定的SQL布局,它们可以用于得到一个查询结果的特定的子集。例如,在MySQL中,供给了LIMIT和OFFSET关键字,它们可以用于select查询。是以,假如你履行类似下面的查询

select * from tbl LIMIT 50 OFFSET 100

你的结果集将包孕从第100个结果开始的50行,纵然本来的查询返回了100,000行。许多其他的数据库供给商经由过程不合的布局供给了相似的功能。不幸的是,Derby并没有供给这样的功能,以是你必须继承应用本来的“select * from tbl”查询语句,然后在利用法度榜样中实现一个分页的机制。让我们来看下面的例子:

Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance();

Connection connection = DriverManager.getConnection(

"jdbc:derby://localhost:1527/testDb;");

Statement stmt = connection.createStatement();

ResultSet rs = stmt.executeQuery("SELECT * FROM tbl");

ArrayList allResults = new ArrayList();int i = 0;

while (rs.next()) {

if (i > 50 && i50 && i <= 100) {

// O-R mapping code populate your row from result set

DomainObject domainObject = populate(rs);

allResults.add(modelObject);

}

}

System.out.println("Results Size: " + allResults.size());

值得留意的是,我们把最大年夜行的值设置为了我们必要的着末一行(增添了1)。是以,经由过程这样的办理规划,我们不是仅仅取得了我们想要的50行,而是先获取了100行,然后从中筛选出我们感兴趣的50行。不幸的是,我们没有法子奉告JDBC驱动从一个详细的行开始,是以我们必须阐明要显示的记录的最大年夜行数。这就意味着返回最初的一些记录的操作的机能是很好的,然则跟着用户浏览的结果的增多,机能也会下降。好消息便是在大年夜多半的情形下,用户不会浏览的太多的记录,他们会在前几笔记录重得到他们探求的行,或者改变查询策略。在我本人的情况中,上述的例子的履行光阴从8秒降到了0.8秒。

这是一个描述若何浏览全部表的简单的例子。然则当查询语句中增添了特定的where前提和排序信息时,工作又开始变更了。在接下来的部分里,我将解释为什么这种环境会发生,今后我们若何包管在那些例子中得到可吸收的机能。

确保应用索引(避免全表扫描)

索引在数据库设计中是一个异常紧张的观点。由于本文所涉及的范围有限,我并不会具体的先容索引理论。简单来说,索引是特定的数据库布局,能够容许对表中的行进行快速造访。索引平日是在一栏或多栏上创建的,由于他们比全部表小了很多,他们的主要用场便是快速搜索一栏(多栏)中的值。

Derby自动的为主键和外键的栏以及具有独一性限定的栏创建索引。对付其他任何栏,我们必须显式的创建索引。在接下来的段落中,我们将钻研一些例子来先容索引在什么时刻有用以及为什么有用。

然则首先,我们必须做一些筹备。在我们开始优化之前,我们必要能够懂得我们履行查询操作的时刻数据库中发生了什么。Derby供给了derby.language.logQueryPlan这个参数。假如设置了这个参数,Derby将会把所有履行的查询的查询计划(query plan)记录在derby.log这个文件中(这个文件在derby.system.home文件夹中)。我们可以在启动办事器之前经由过程相宜的derby.properties文件或者履行如下的java语句来设置该参数。

System.setProperty("derby.language.logQueryPlan", "true");

经由过程反省查询计划,我们可以察看Derby在查询中是应用了索引照样进行了全表查询,全表查询是一个很耗光阴的操作。

既然我们已经设置好了情况,我们可以开始我们的例子了。假设我们先前应用的表 tb1中有一个没有索引的栏叫做owner。由于对查询结果的排序平日是查询机能低下的主要缘故原由,我将先容所有与排序有关的优化。现在,假如我们盼望改动先前的例子来根据这一栏的值来排序我们的结果,我们必要把我们的查询语句改成如下的样子:

SELECT * FROM tbl ORDER BY owner

假如我们用这个查询语句代替先前的语句,履行的光阴将是先前的很多多少倍。只管我们分页(paginated)了所有的结果,并小心的设置了要获取的行数,总的履行光阴将会是8秒。

假如我们查看derby.log文件中查询履行计划,我们可以随意马虎的发明问题:

Table Scan ResultSet for TBL at read committed isolation

level using instantaneous share row locking chosen

by the optimizer

这意味着Derby为了将记录排序,是在全部表中履行了查找这个操作。那我们可以做些什么来改良这个环境呢?谜底很简单,在这一栏上创建一个索引。我们可以经由过程如下的SQL语句来做这件事:

CREATE INDEX tbl_owner ON tbl(owner)

假如我们重复我们先前的例子,我们将获得一个和我们没有做排序前的那个例子相似的结果(在我的机械上是不到1秒)。

同样,假如你现在查询derby.log,你将看到下面的信息(而不是和上面的一样的):

Index Scan ResultSet for TBL using index TBL_OWNER

at read committed isolation level using share row locking

chosen by the optimizer

这就意味着我们可以确保Derby应用了刚创建的索引来获取相宜的行。

应用相宜的索引顺序

我们已经看到了索引是若何赞助我们改良了排序某一栏数据时的机能。然则假如我们考试测验去反转排序的顺序的时刻会发生什么呢?假设我们盼望根据owner栏降序分类我们的数据。在这种环境下,我们本来的查询就会变成如下的语句:

SELECT * FROM tbl ORDER BY owner DESC

留意,我们增添了DESC这个关键字,该关键字将按降序来排序我们的结果。假如我们履行这个新改动过的查询语句,将会发明全部履行的光阴又增添到先前的8-9秒。并且,在日志文件中,你将会发明又是履行了全表扫描。

办理的措施便是为这一栏创建一个降序的索引。对付我们的owner栏,我们履行如下的SQL语句。

CREATE INDEX tbl_owner_desc ON tbl(owner desc)

现在我们对这一栏有两个索引了(两个顺序),是以查询机能又规复到了可吸收的范围了。留意查询日志中这一行:

Index Scan ResultSet for TBL using index TBL_OWNER_DESC

at read committed isolation level using share row locking

chosen by the optimizer

这使我们确信我们应用了新建的索引。是以,假如你常常要对结果进行降序排序的话,你应该斟酌创建一个相宜的索引来获取更高的机能。

重修索引

跟着光阴的流逝,索引记录将孕育发生碎片,这将导致严重的机能下降。例如,假如我们有一个好久曩昔创建的索引,例如tb1表的time_create栏。

假如我们履行如下的查询

SELECT * FROM tbl ORDER BY time_create

我们获得很差的机能,很大年夜可能是由于我们根本没有一个索引。不过,假如我们看了以下日志,就可以发明问题所在。你会发明我们应用了索引,然则将看到和下面相似的信息。

Number of pages visited=1210

这意味着数据库在索引查询历程中履行了大年夜量的IO操作,这便是这个查询历程的瓶颈所在。

这种环境的办理措施便是重修索引(drop然后重修它)。这将使得索引进行收拾碎片,从而节省我们大年夜量的IO操作光阴。我们可以经由过程下面的SQL语句来重修索引:

DROP INDEX tbl_time_createCREATE INDEX tbl_time_create ON tbl(time_create)

你将发明履行光阴又降到一个可吸收的值(1秒以内)。

同样,你在日志文件中将发明如下的行:

Number of pages visited=5

正如你看到的,履行光阴显着的下降了是由于数据库履行了很少的IO操作。

是以,平日的规则便是让你的法度榜样按期重修索引。最好是在法度榜样计划义务一个后台的事情来时时的完成这个事情。

多栏索引

到今朝为止,我们专注于简单的单栏的索引和简单的查询。创建owner和time_create的单栏索引可以赞助我们进行过滤和排序。纵然时下面的查询语句也具有可吸收的机能。

SELECT * FROM tbl WHERE owner = 'dejan'AND time_create > '2006-01-01 00:00:00'ORDER BY time_create

然则假如你考试测验履行如下的查询:

SELECT * FROM tbl WHERE owner = 'dejan' ORDER BY time_create

那又会是一个漫长的履行历程。这是由于数据库为了排序数据必要履行额外的排序步骤。

办理这种类型的查询的法子便是创建一个包孕owner和time_create的索引。我们可以经由过程履行下面的查询来创建索引:

CREATE INDEX tbl_owner_time_create ON tbl(owner, time_create)

经由过程应用这个索引,查询的机能将会获得很大年夜的改良。现在,留意下面的阐发日志:

Index Scan ResultSet for TBL using index TBL_OWNER_TIME_CREATE

at read committed isolation level using share row locking

chosen by the optimizer

我们经由过程应用一个便利的索引来使得数据库可以快速的找到已经排好序的数据。

这个例子中值得留意的是,在“create index”语句中的栏的顺序是异常紧张的。多栏索引只有经由过程在创建索引时定义的第一个栏时才是可优化的。是以,假如我们创建了如下的索引:

CREATE INDEX tbl_time_create_owner ON tbl(time_create, owner)

而不是先前我们应用的索引,我们将不会发明什么机能的优化。那是由于,derby的优化器不觉得这个索引是最好的办理规划,从而轻忽了它。

索引的毛病

索引可以赞助我们在选择数据的时刻改良机能。当然,这也减慢了数据库插入删除以及一些更新操作。由于我们不仅仅有表布局,还有很多的索引布局,以是当数据发生变更时,掩护所有的布局是很耗光阴的。

例如,当我们在表中插入一行数据的时刻,数据库必须更新和这个表的栏有关的所有的索引。这就意味着它必须将一个已索引的栏的数据插入到相宜的索引中,这将很花光阴。同样的事也会在你删除一个特定的行的时刻发生,由于索引必须包管顺序。对付更新操作来说,只有当你更新了已索引的栏的时刻受到影响,由于数据库必须从新定位这些索性来维持索引的顺序。

是以,优化数据库和法度榜样设计的关键在于你的必要。不要索引每一个栏,你不必然会要用到这些索引,而且你可能必要优化你的数据库来进行快速的插入。在早期就开始测试数据库的机能并发明瓶颈。只有那时你才该去利用本文中提到的技巧。

结论

在本文中,我们钻研了一些在日常开拓历程中碰到的关于机能的问题。大年夜多半的准则(或进行适当的改动)都可用于任何关系数据库系统。还有很多其他的技巧可以赞助你改良你法度榜样的机能。缓存当然是最有效和利用最广泛的措施之一了。对付Java法度榜样员来说,许多的缓存办理规划(部分,如OSCache或者EHCache等开源许可的下的规划)都可以看作是法度榜样和数据库之前的缓存从而前进全部法度榜样的机能。同样,Java项目顶用到的许多面向工具的框架(如Hibernate)都拥有内置的缓存能力,以是你应该斟酌这些办理规划,不过那是另一个评论争论文章的内容了。

您可能还会对下面的文章感兴趣: