`

jdbc大数据量查询优化

 
阅读更多

博客分类: Oracle
javasql

大略的看了一下觉得编好文章:http://hi.baidu.com/lovemywolf/blog/item/b2e3a912e22e67cfc2fd78a3.html
在实际的任何一个系统中,查询都是必不可少的一个功能,而查询设计的好坏又影响到系统的响应时间和性能这两个关键指标,尤其是当数据量变得越来越大时,于是如何处理大数据量的查询成了每个系统架构设计时都必须面对的问题。本文将从数据及数据查询的特点分析出发,结合讨论现有各种解决方案的优缺点及其适用范围,来阐述J2EE平台下如何进行查询框架的设计。

Value List Handler模式及其局限性
在J2EE应用中,对于大数据量查询的处理有许多好的成功经验,比如Value List Handler设计模式就是其中非常经典的一个,见图1。该模式创建一个ValueListHandler对象来控制查询的执行以及结果集的缓存,它通过DAO(Data Access Object)来执行查询,并将数据库返回的结果集(传输对象Transfer Object的集合)缓存起来,接下来的客户端查询请求将直接从缓存中获得。它的特点主要体现在两点:服务器端缓存数据,每次只返回客户端本次操作所需的数据,通过这两个措施来减少数据库的访问次数以及增加客户端的响应速度,达到最优的查询效果。当然,这里面隐含一个前提就是客户端采用分页的方式来浏览数据。关于该模式的详细介绍,请参考[Core J2EE Patterns]一书。
http://www.chinaitpower.com/members/ok_winnerboy/1121167368078Value_List_Handler.bmp' />
图1:Value List Handler类图

但是在实际的应用过程中,会发现该模式存在一定的局限性,其实可以说是该模式应用具有一些前提条件:
1、由于缓存是以内存来换性能,这对于小数据量会工作得很好,但是如果结果集很大,内存消耗将会非常严重。同时,消耗在处理结果集上的时间也会越来越长,比如要循环读取记录集中的数据,然后依次填充每个传输对象,想想看几百万条数据这样处理起来肯定让人不能忍受。过长的处理时间不仅降低反应速度,同时还会占用宝贵的数据库连接资源,造成其它地方无连接可用。虽然,在DAO模式中利用CachedRowSet,Read Only RowSet ,RowSet Wrapper List等策略(详见参考资料)来代替Transfer Object Collection策略,有效地提高了处理速度,但是仍然存在着在大集合数据中进行定位、遍历等问题。试想一想,即使在CachedRowSet中的absolute(2000000)也是非常费时的操作。所有这一切的根源就在于缓存是一次性读取所有的数据,虽然有时你可以利用业务逻辑来强制性增加一些限制条件(比如产品查询必须选择大类和次类),但这种限制往往是不牢靠的或者说只是一时的权宜之计。也有人提出,可以不必缓存所有的查询结果,而采取只缓存部分结果集,比如500,1000条,但这样一来,就涉及到复杂的查询数据是否越界的控制,增加了复杂度,同时也不易实现。

2、既然使用缓存,那就不得不面对一个数据更新的问题,使用缓存,实际上就假定了在数据缓存期间,数据库中的数据不会改变,或者这些改变可以不被反映出来。但是,在很多场合下(比如常见的业务系统中)这些数据库中的数据经常会发生变化,而且这些改变需要及时反映给客户端。

3、缓存其实存在一个基本前提,就是缓存的数据会被客户端反复查询使用,具体到分页查询就是客户会选择不同的页数来查看数据。如果客户端的查询条件始终变化,或者用户基本上只关心第一页的数据(仔细琢磨一下用户的习惯,这在很多中应用场合都很常见),那缓存就失去了应有的意义,变得多此一举了。

数据分析
所以说,在决定是否应用某种设计模式前,我们需要对被查询数据的特点以及这些数据以何种方式被使用(查询的特点)进行一个分析,根据不同的结论来决定采用何种处理策略。而且,数据本身的特点和被使用的方式往往交织在一起,需要综合起来考虑,但这其中主要的考量点还是数据查询的特点。

一般来说,可以从以下几个方面来分析数据:
1、     数据量大。
这是我们今天讨论的数据的一个最基本特点,这个特点在查询框架设计时要引起足够的重视。
注意:大数据量的查询是指查询时匹配条件的数据量大,而不是指表中的数据量大,虽然大部分时候这两者都是一致的。因为在某些情况下,业务逻辑可以限制或者只需要一次获取很少量的数据,而查询的表中的数据量却可能很大,那这种情况就不属于本文的讨论范围。
2、     关联复杂,多表关联。
越是简单的数据可能关联越少,而越是复杂的数据往往都是多表关联,这样很多时候你需要将这几张表作为一个整体来考虑。
3、     变化频率。
从这个角度出发,可以大致将数据分为以下几类:几乎不变化的睡眠数据;有规律定时更新的数据,比如招聘网站的职位信息;经常性无规律更新的数据。
4、     成长性。
数据是否具有成长性,要预见数据的成长性,并在现有方案中考虑这种成长性,避免到时候查询框架的重新设计,象大部分的业务数据都具有这种成长性。
注意:这里也要特别注意区分数据本身的成长性和数据查询的成长性,这看似等同的两者其实还是存在很大的区别。就拿招聘网站来说,有效职位的数据肯定是一天天在增加,具有高成长性,但是在某个区间(比如一个月,一个星期)内的有效职位查询则变化不会太大,不具有成长性。而后者却往往是实际系统中最常遇到的查询情况。
5、     数据查询的频率和方式。
所有的数据查询不可能被等同地使用,你要分清楚系统中的几个关键查询,这些查询使用频率高,响应要快。试想一想,如果一个电子商务系统的产品查询每次都要让顾客等上十秒钟,结果就可想而知。

用户的使用习惯分析
除了对数据查询本身需要进行分析之外,我们还需要去分析一下用户如何来使用或者看待这些数据,用户的使用习惯如何。有人可能觉得这作用不大,或者很难去分析,其实查询的最终使用者是用户,他们的一些习惯会很大程度上左右你的设计。

1、     用户关心数据哪些方面的特性,不关心哪些方面的特性。
上面我们分析了数据本身的许多特性,那用户对其中哪些特性最敏感呢?比如说对脏数据特别不能接受,那我们就必须在查询框架设计时特别照顾到这一点。因为再好的框架设计都不可能在每个方面都能达到最优的效果,当必须有所取舍的时候,我们就要明白哪些特性是客户最关心的。

2、     用户如何来使用数据。
现在一般查询的客户端都采用分页的方式,一个查询可能会存在十几页甚至几十页结果。对于某些查询,用户可能往往只关心第一页或者前几页的结果,比如用户需要查询出最近完成的工单,而对于另外一些查询,用户可能对所有页结果都很关注,比如用户查询出最近三天新增的招聘职位。这不同类型的查询在查询框架设计的时候都需要有所考虑并给予不同的处理策略。

查询框架的设计
对数据及用户使用习惯进行了仔细的分析,接下来就可以根据这些分析来设计你的查询框架了。在J2EE架构下,对于大数据量的查询主要采取以下两种方法:
基于缓存的方式:
从数据库得到全部(部分)数据,并将其在服务器端进行缓存,接下来的客户端请求,将直接从缓存中取得需要的数据。这其实就是Value List Handler模式的原理,它主要适用于数据量不是非常大,变化不是很频繁(或者变化频繁但是有规律)且不具有成长性的情况,比如招聘网站或者电子商务网站的大部分查询就非常适合采取这种方式。

采用这种方式,要特别注意第一次查询问题,避免响应性能达不到要求,因为每个查询第一次都需要连接数据库,从中获取数据并缓存起来,所以第一次查询会比接下来的查询都显得更慢一些。

对于数据的缓存,有以下几种实现方式:
     直接缓存在服务器端
Value List Handler模式就采取这种方式,并且可以根据不同的情况采取不同的缓存策略,比如Transfer Object集合,CachedRowSet等,这取决于你的DAO实现策略。
     用临时表来保存查询结果
WLDJ(www.sys-con.com/weblogic/)杂志2004年第7期上有一篇名为“Handling Large Database Result Sets”的文章,它详细介绍了如何利用临时表来改良Value List Handler模式以支持大型的J2EE应用。

当然除了以上这些方法以外,实现缓存也可以求助于操作系统的特定实现,以前我在IBM DW发表过一篇探讨MMF在Java中应用的文章(见参考资料),可惜未有深入,有兴趣的朋友可以参考一下。

在使用Value List Handler模式时,要特别注意以下几点:
1、     该模式一般和DAO模式搭配使用。
2、     该模式有POJO,stateful session bean两种实现策略。
3、     如果采取stateful session bean实现策略,则默认该缓存的时间长度为整个用户会话。

前面我们也提到过,如果数据不是绝对不变的,那缓存就面临更新的问题,一旦更新就可能存在着数据不一致,如果恰巧客户也希望能够看到变化的效果,这个时候就需要采取某种措施来保证这种一致性。常见的措施可以是设置一个标志位,每次发生数据更新后都将其对应的标志位更新,查询时如果发现标志位更新了,就直接从数据库获取数据,而不是从缓存中获取数据。另外一种方式就是数据更新的同时主动去清空session中的缓存,如果采用stateful session bean实现策略的话。

当然,采取缓存方式的大数据量查询一般来说都不大可能遇到设置更新标志位的问题,因为这种应用方式决定了数据不大可能变化,或者数据变化不要求立刻反应给用户。比如招聘网站新增加了一些职位信息,如果这些更新恰巧发生在某些用户的会话期间,且没有设置更新标志位,那这些新增信息就不会反应到用户的查询结果中,这种处理方式也是可以接受的。

基于查询的方式:
不进行数据缓存,客户端的每次数据请求都需要进行实际的数据库查询,这种方式适用于量大,具有成长性,变化频繁的数据。该方式的特点是每次查询的时间都大致相等,不会存在基于缓存的方式的第一次查询问题,但后续的操作会比缓存方式的查询慢一些。采取这种方式的查询框架设计更具有可扩展性以及对数据变化更好的应变能力,在大部分的业务系统中都推荐使用该方式。

使用这种方法,每次查询应该只从数据库获得客户端所需的数据,这样就涉及到如何获得部分数据的问题。一种是查询出符合条件的所有记录,然后遍历该记录集根据上次查询结果来比较记录中的某些字段获取本次查询需要的部分数据,由于要对记录集进行遍历,效率不高,一般都不推荐使用,而往往采用另一种增加sql查询语句条件的方式,这种方式有以下几种实现策略:
     专属于数据库的,比如Oracle的rownum
有些数据库提供了标识查询结果集中行号的功能,利用该标识就可以限定某个范围的记录,比如下面这个方法就是利用Oracle数据库中的rownum功能来包装sql查询语句以获得部分记录集。
private String wrapSQL(String strSql) {
       String strWrapSql = "";
       strWrapSql = "select * from (select rownum mynum, xxx.* from (" + strSql + ") xxx ) where mynum between " + getFrom() + " and " + getTo();
       return strWrapSql;
}

当然这种实现策略的缺点很明显,就是绑定于特定数据库的实现,有的数据库可能并不提供这个功能,不能独立于特定数据库实现而提供一个通用的实现。
     利用业务数据的规律性来获得部分记录集
对于实际系统中的每个查询,都会被指定按照某种方式来进行排序(不管是业务逻辑默认的还是客户端指定的),也就是说查询的sql中存在着order by子句。既然查询结果集对于order by子句中的字段会呈现一定的规律性,那我们是不是可以利用这种有序性来获得部分的记录集呢?答案是肯定的。

参考资料中的Paging in J2EE一文对这种方式进行了详细的阐述,并且给出了一个实例,有兴趣的朋友可以参考一下。但是,该篇文章针对的是那些排序方式是业务逻辑默认的情况,即排序字段可以通过属性文件定义下来的。但在实际情况中,还存在另外一种可能,排序方式是由客户端指定时,这个时候排序的字段和升降序都由客户端指定,这个时候就需要在查询框架中提供某种机制来将排序字段和where子句进行映射。

获得结果集的总数
当采用获取部分记录集的方式时,获得的记录集大小并不能真实地反映出查询结果集的大小(因为只是其中的一部分),所以又涉及到对于记录总数的处理:
     如果不需要,或者没有意义,不提供,因为count(*)操作耗资源
     在sql语句中获得
在有些情况下,你可以在sql语句中直接加上count(*)来获得记录集总数,比如你用Statement接口的setMaxRows(int max)方法来控制返回的记录数时。
     单独进行一次查询
为count(*)语句单独进行一次数据库查询,只要将构造好的sql语句中的select字段替换为count(*)即可。当然count(*)的查询sql应该尽量简练,不要加上order by子句之类的。

其实,以上提到的这两种方式在实际的查询框架设计时不可能完全分开来,只不过以哪种方式为主而已,它们往往是混合在一起使用以达到最优的查询效果。

查询框架设计时的注意点
当然,在优化大数据量查询的过程中,数据库本身的优化必不可少,比如建立索引等措施。但是,数据库的优化要循序渐进,要和程序代码的优化相一致。在进行数据库优化之前,要做的是sql语句的优化,这可以通过观察它的执行计划来进行考量。这些工作最好都由DBA来参与共同完成。

对于性能的个人态度:要提早考虑,确定一个方案前要进行测试,并预见未来的变化。

对于“大数据量”这几个词不要只停留于口头的重视,要想有切身的体会,最好在框架设计好后,进行大数据量的模拟压力测试,实际地检验你的设计。不要总是回答:我想,应该没有问题,应该支持几百万的数据,最好拿出最有说服力的数据。

以后关注的方向:
1、     EJB 3.0规范中EJB QL语法得到加强,比如可以支持原生SQL语句,使得利用实体Bean来实现部分类型查询成为可能。
2、     JDBC规范是否能够在某些方面对这种大数据量查询提供某些功能接口,比如获得部分记录集,以及统一的缓存接口。

总结
本文给出了查询框架设计时的一些思路和方法,最重要的一点就是在设计之前要充分研究你的目标系统,了解最终的数据查询特点。由于本文只是个人的一些观点和经验总结,偏颇之处在所难免,希望有兴趣的朋友一起来讨论这个问题。

参考资料:
1、     [Core J2EE Patterns] Core J2EE Patterns: Best Practices and Design Strategies, Second Edition, By Deepak Alur, John Crupi, Dan Malks.
2、     关于DAO模式中CachedRowSet等策略的详细讨论,请参考[Core J2EE Patterns]一书。
3、     Paging in J2EE: Manage Large Result Sets Efficiently. By Lara D'Abreo.
(http://www.devx.com/Java/Article/21383)
4、     如何提高系统性能指标??MMF在Java中的应用
(http://www-900.ibm.com/developerworks/cn/java/l-java-performance/index.shtml)
分享到:
评论

相关推荐

    sharding-jdbc数据分片

    针对单表数据量大导致的查询缓慢问题进行数据分片从而达到性能优化效果。代码罗列了针对springboot形式的各种分片策略,以及性能对比测试方法。

    5Java性能优化五.zip

    当使用JDBC进行查询的时候,对于大量拥有相同结构的SQL查询,能够使用PerparedStatement取代Statement。以提高数据库的查询效率。在使用Select语句中,显示指定要查询的列名 ,避免使用* 在对数据库优化时,主要...

    MySQL大量数据插入各种方法性能分析与比较

    不管是日常业务数据处理中,还是数据库的导入导出,都可能遇到需要处理大量数据的插入。插入的方式和数据库引擎都会对插入速度造成影响,这篇文章旨在从理论和实践上对各种方法进行分析和比较,方便以后应用中插入...

    Java_JDBC由浅入深

    第十五节 jdbc轻量级封装 88 15.1 将结果集封装为Map 88 15.1.1 ResultSetMetaData演示 88 15.1.2解决多行记录的问题 89 15.1.3 Map结果集的封装 90 15.2 将结果集封装为对象 91 15.2.1 user表POJO的编写 91 15.2.2 ...

    深度解析ShardingJDBC:Java开发者的分库分表利器

    ShardingJDBC是一个轻量级的Java框架,专为处理分库分表场景而设计。起源于当当网,后经由多家大型互联网企业的验证与发展,它已成为Apache软件基金会的顶级项目​​。ShardingJDBC通过数据分片和读写分离,使Java...

    hbase phoenix sql

    DML支持:用于逐行插入的UPSERT VALUES、用于相同或不同表之间大量数据传输的UPSERT SELECT、用于删除行的DELETE 通过客户端的批处理实现的有限的事务支持 单表——还没有连接,同时二级索引也在开发当中 紧跟...

    数据算法 hadoop spark大数据处理技巧

    《数据算法:Hadoop/Spark大数据处理技巧》介绍了很多基本设计模式、优化技术和数据挖掘及机器学习解决方案,以解决生物信息学、基因组学、统计和社交网络分析等领域的很多问题。这还概要介绍了MapReduce、Hadoop和...

    03开源NewSql数据库TiDB-Deep Dive into TiDB

    所以如果能提升大数据量下的查询性能,对用户会很有帮助。 优化 TiDB 的易用性和可维护性。TiDB 整套系统的复杂性比较高,运维及使用的难度要大于单机数据库,所以希望能提供尽可能方便的方案帮助用户使用 TiDB。...

    用JDBC访问ORACLE数据库 关于commit 增快效率 大数据 等的整理

     对于大量数据更新,Oracle有建议一些优化措施。  (1)首先是把auto-commit给关闭。因为你每删一条数据,oracle要自动执行一次commit。commit是需要资源的。所以如果你手动设置为每删数据1000条,执行一次commit....

    基于Java Swing + MySQL + JDBC 的图书管理系统.zip

    移动应用与物联网(IoT):作为数据存储层,支撑大量设备的数据采集、存储和查询需求。 嵌入式系统:在资源有限的环境中,作为轻量级数据库解决方案。 总之,MySQL凭借其开源、跨平台、高性能、灵活扩展、丰富...

    很好的一个jsp分页

    二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。  其它常见的方法还有每次翻页都查询一次数据库,从ResultSet中只取出一页数据(使用rs.last();rs....

    MSSQL批量插入数据优化详细

    主要为大家分享一下批量插入数据的方法,有时候我们需要插入大量的数据那么就需要优惠了,要不根本受不了

    Java开发工程师面试题资料

    11. 大数据量下的分页解决方法. 12. 简述建立索引的作用和索引的分类 ? 13. 什么是存储过程,有什么优缺点(重点) 14. 存储过程与 SQL 的区别? 15. 如何创建视图? 16. JAVA 中常用的运算符有哪些?这些运算符...

    通用数据库分析工具

    4.支持异步查询,这对大量数据查询时十分有效(一般情况下,这种查询会超时,异步查询不会,边查询边显示数据,注意要求数据库支持游标操作) 5.新增异步查询终断功能,当大量数据查询时,可以进行终止。 =========...

    SparkSql和DataFrame实战.docx

    文档主要介绍了环境搭建和配置使用 1. 什么是Spark SQL Spark SQL的一个用途是...spark 通过DataFrame操作大量的数据源,包括外部文件(如 json、avro、parquet、sequencefile 等等)、Hive、关系数据库、cassandra等

    构建最高可用Oracle数据库系统 Oracle 11gR2 RAC管理、维护与性能优化

    1.3.5高吞吐量 1.4 RAC存在的问题 1.4.1稳定性 1.4.2高性能 1.5 RAC软件 1.5.1存储管理软件 1.5.2集群管理软件 1.5.3数据库管理软件 1.6本章小结 第2章 搭建类似生产环境的RAC 2.1搭建环境 2.1.1 RAC的...

    JDBC专题(七)-数据库连接池 DataSource Pool.docx

    1.应用程序直接获取数据库连接的缺点 2.使用数据库连接池优化程序...假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:

    JAVA-JSP+SQL房屋租赁管理信息系统JDBC(源代码+论文+答辩PPT)

    2.因为建立的系统要成为整个组织的心脏和信息交换中心,因此它要包括企业各种数据输入,存储,加工,查询,生成计划,物资供应,帐务帐目,生产,销售等日常信息处理。 3.数据处理要速度高,成本低。 4.因为信息模型...

    Java数据编程指南

    Java数据库连接(JDBC) 什么是JDBC JDBC结构 开始起步 使用JDBC 一个简单的范例 对映Java与SQL类型 处理SQL错误 ResultSet与数据库元数据 JDBC中的事务处理 一个JDBC事务范例 ...

Global site tag (gtag.js) - Google Analytics