ok_winnerboy 原创  (参与分:7484,专家分:310)   发表:2005-07-12 19:30   版本:1.0   阅读:1172

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

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

转载于:https://www.cnblogs.com/menghe/articles/267996.html

如何处理大数据量的查询相关推荐

  1. 千万级别数据查询优化_MySQL大数据量分页查询方法及其优化

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

  2. 大数据量分页查询方法(转)

    本文旨在介绍一种对数据库中的大数据量表格进行分页查询的实现方法,该方法对应用服务器.数据库服务器.查询客户端的cpu和内存占用都较低,查询速度较快,是一个较为理想的分页查询实现方案. 1.问题的提出  ...

  3. mysql一样的查询在我本地很快但是线上很慢_MySQL大数据量分页查询方法及其优化...

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

  4. MySQL 和 Oracle 大数据量分页查询方法及其优化

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

  5. MySQL大数据量分页查询方法及其优化

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:收藏了!7 个开源的 Spring Boot 前后端分离优质项目个人原创+1博客:点击前往,查看更多 链接:ht ...

  6. 千锋重庆Java学习之MySQL大数据量分页查询方法及其优化

    方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺 ...

  7. python循环次数查询_大数据量Mysql查询后经过循环使用python分片

    1 问题描述: (1)使用mysql查询基础数据,这里只有三四个基础的查询条件,联了一个表,同时有limit分页了: (2)之后经过一系列逻辑处理,在这些处理中又包含了很多sql查询,而且是在第(1) ...

  8. oracle 大数据量分页查询

    (一)分页实现及性能 Oracle的分页查询语句基本上可以按照本文给出的格式来进行套用. 分页查询格式: SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (SEL ...

  9. 如何 提高企业网站大数据量 效率

    摘  要:目前企业信息化正在如火如荼地开展之中,企业信息量在急剧膨胀.这使得信息的搜索工作变得极为繁重起来.据调查统计,人们在平时的工作中,有70%的时间都花费在信息搜索上.由此,如何提高人们搜索信息 ...

  10. rdkafka线程过多_我是如何处理大并发量订单处理的 KafKa部署总结

    今天要介绍的是消息中间件KafKa,应该说是一个很牛的中间件吧,背靠Apache 与很多有名的中间件搭配起来用效果更好哦 ,为什么不用RabbitMQ,因为公司需要它. 网上已经有很多怎么用和用到哪的 ...

最新文章

  1. Distilling the Knowledge in a Neural Network 论文笔记蒸馏
  2. python 复制 移动 重命名文件
  3. 8088微型计算机pdf,微型计算机原理与接口技术:第3章 8086-8088微处理器及其体系结构.pdf...
  4. HashMap数据类型使用注意-不能使用基本数据类型
  5. 自定义验证控件CustomValidator
  6. App Store 扣费 知识产权 备忘
  7. java 读取raw文件_Android 读取assets和raw文件内容实例代码
  8. Web-HTML特殊符号的表示
  9. java esc的_如何用Java中的Receipt打印机和ESC / POS命令提高速度
  10. Castle 开发系列文章
  11. KVM详解(三)——KVM创建虚拟机
  12. Elasticell-聊聊Raft的优化
  13. IPX5、IPX6、IPX6K冲水方向
  14. 上市公司环境数据集:环境绩效明细表、排放明细表、资源消耗明细表等多项指标数据
  15. 【摄影笔记三】光圈和快门
  16. java钝化_javaEE之--------------session的活化与钝化
  17. curl+openssl,编译win32 兼容xp系统的静态库
  18. 2020 年 7 月编程语言排行榜
  19. 上海居住证办理 续办上海居住证全攻略
  20. Vue手写一个日历组件

热门文章

  1. Java EE开发系列教程 - 使用EJB组件与JPA层交互
  2. 关于字符串的长度和大小的定义
  3. 简单好用的作图工具,除了Visio还有它!
  4. 小程序中的image标签
  5. opencv学习十三:图像金字塔和图像梯度
  6. 净推荐值NPS(Net Promoter Score)
  7. Centos7查看硬盘容量
  8. XTU OJ 三角形
  9. 中芯国际:Q1营收同比增长22%至11亿美元,预计年底产能仍供不应求|看财报
  10. 微信公众号粉丝、文章迁移流程及方法