当我们为了完成某个业务逻辑,需要从数据库系统返回记录过多的结果集的时候,往往会引发多方面的问题,这些问题包括:性能优化设计策略、结果集的分页技术等等。对于开发人员来说,都是无法避免的。
先看看数据库查询大结果集可能带来的问题:
(1),内存占有过大。如果我们一次性把成千上万条记录保存在内存的话,内存势必会消耗过大,这使得JVM虚拟机能运用的内存空间减少,数据交换的速度减低,导致服务性能明显降低;而且,很容易引发内存不足不可恢复异常,最后导致整个服务瘫痪。
(2),网络传输阻塞。一般情况下,数据库服务都是独立一台机器,如果我们一次性从数据库返回大量数据的话,很容易瞬间导致网络阻塞,特别是当用户并发访问很大的时时候,情况更加明显。
(3),数据显示问题。如果一次性把成千上万条记录用一个页面或窗口呈献给查询用户,容易造成客户视觉疲劳,所以我们要考虑分页显示。
针对不同的问题,我们解决问题的方法也不尽相同。对待大结果集数据分页的处理上,我们一般的设计策略有以下3种方式:
(一),把结果集所有的数据一次性查询出来,放在应用服务器内存中,然后再分页处理。
这是一种很常见的思路,它的优点是:解藕了ResultSet操作类和数据库查询游标的关系,使得数据库连接得到有效的释放,提高了数据库并发访问的性能。同时,把数据导在本地,处理十分简单。由于数据都在内存中,翻页查询没有必要再从数据库请求数据,直接从内存获取就可以了,因而,响应更快。其缺点有:由于数据缓存在内存中,客户查询有可能看到的是过期数据;如果查询结果集数据量非常大的话,第一次查询遍历结果集会很耗时间,而且缓存这样大的数据也会造成内存消耗过大,使得JVM的效率明显降低。所以,这种方式对于查询结果在1000条以内的“小”结果集,而且数据完整性要求不高的情况下,可以采用,而且也十分简单有效。
(二),对查询结果集进行业务分析,针对查询客户的需要,把客户最为关心的数据首先呈献给客户,对于一些老数据,放入历史库,另外提供一个历史查询模块给客户使用。
这其实是把查询结果集按照客户的需要拆分开,避免了问题的主要矛盾,当然,前提是假设了用户实时关心的数据并不多。例如,对新闻数据查询,客户可能最关心的就是近3天的新闻信息,其它过期新闻可能1个月也不会查询一次。这个方式主要基于业务上设计策略,实现的技术也是甚为灵活。
(三),查询结果集所有的数据仍然留在数据库服务器端。每次翻页,我们都根据页面大小只从数据库服务器里面检索并返回块区数据,直到检索完所有的记录。
由于每次查询的记录数很少,网络传输数据量不大,不会造成阻塞。而且,数据都是由数据库统一提供,不会存在数据过期问题。应该说,这种方式是解决数据分页的理想方式。
实现方式(三)的技术常见有:
■直接使用JDBC底层的ResultSet接口,利用游标来处理。
ResultSet本质是在数据库服务器上建立游标,然后通过行位置定位数据记录。这种方法,当客户第一请求数据时,就执行查询语句,获得ResultSet对象,保存在会话中,以后分页获取数据时都是通过ResultSet对象来定位和获取指定位置的记录。最后,当客户不再进行分页查询的时候,就关闭会话,释放数据库连接和ResultSet等等数据访问资源。
优点:减少sql语句查询的次数,利用标准API实现,便于系统移植。
缺点:每一个用户查询都要占用一个数据库连接和结果集游标。当并发用户量大的时候,会浪费数据库连接资源,使数据库响应变慢,性能降低;另外,从系统设计的角度考虑,开发人员需要操作ResultSet和维护其状态,使得无法从结果集的处理独立出来,对系统框架的抽象设计很是不利;而且,ResultSet在数据定位上的操作也很是笨拙。
所以这方法基本上不值得提倡。
■
利用各种数据库服务器自己提供的对查询结果集的可定位行范围的接口技术来实现。
客户发出请求,数据库就根据查询请求的行范围参数,检索出所需的记录返回给客户端。客户获取记录集后就释放相关的数据访问资源,从而避免了对并发访问的影响。
优点:技术简单,直接利用了数据库产品特性实现,java开发人员无需关心其具体的实现原理;充分利用了数据库的连接资源;系统查询响应速度快;解藕了ResultSet与数据结果集的关系,便于系统框架的抽象设计。
缺点:需要涉及专有的数据库技术,对java开发人员要求高。每翻页一次,就要查询数据库一次;通用性不强,因为每种数据库的行范围定位技术都不一样,需要重新抽象。
虽然对开发人员数据库知识要求高,但这种方法往往是最为有效的解决方法。我们只要根据经验,把各种数据库的行范围技术总结一次,我们就重复利用,数据分页的问题因而也就变得十分简单了。BJAF数据存取框架总结来各个数据库系统分页特有技术,封装成
Pagination
查询分页组件,有效地解决来持久层数据库查询分页编程难题,组件UML结构图如下:
IPagination接口定义了
page(pInfo
:
PageParameter)
:
PageResult
分页方法,PaginationFactory用来解耦客户端与各个实现类之间的调用关系;PageParameter是分页参数对象,负责分页请求参数的构建和组织;PageResult是分页查询结果信息对象;PageHelper是计算分页的助手类。
假设前面查询EMP表薪水>2000的雇员记录数超过3000条,现在采取每页20返回条记录进行分页查询,其实现代码如下:
public static void main_testPagination(String[] args) {
// 建立一个分页参数对象
PageParameter param = new PageParameter();
param.setDataSourceName(Const.dataSourceName); // 设置数据源
param.setUserSql("select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp where sal>=?"); // 查询语句
param.addParameter(new SqlParameter(SqlType.FLOAT, new Float(2000)));// 设置sql参数
param.setPageNumber(1);// 设置查询页号码(现返回第1页数据)
param.setPageSize(20);// 设置每页返回数量大小(条数)
// 利用PaginationFactory获取Oracle分页实现的实例
IPagination pageOpt = PaginationFactory
.getPaginationInstance(PaginationFactory.ORACLE_ID);
// 执行分页查询,并返回查询结果信息对象
PageResult result = pageOpt.page(param);
// 打印此结果信息对象
System.out.println(result.getCurPageNumber());// 当前页号
System.out.println(result.getCurPageSize());// 当前页数量大小
System.out.println(result.getCurPos());// 当前记录集位置
System.out.println(result.getFirstPageNumber());// 首页号码
System.out.println(result.getLastPageNumber());// 最后页号码
System.out.println(result.getNextPageNumber());// 下一页号码
System.out.println(result.getPrePageNumber());// 前一页号码
System.out.println(result.getPageAmount());// 总页数
System.out.println(result.getRecordAmount());// 总记录数
// 利用RsDataSet解析查询记录集
RsDataSet rs = new RsDataSet(result.getSqlResultSet());// 记录数据
for (int i = 0; i < rs.rowCount; i++) {
Emp emp = new Emp();
rs.autoFillRow(emp);
System.out.println(emp.getEmpNo());
System.out.println(emp.getEname());
System.out.println(emp.getHireDate());
System.out.println(emp.getJob());
System.out.println(emp.getMgr());
System.out.println(emp.getSal());
System.out.println(emp.getComm());
System.out.println(emp.getDeptNo());
System.out.println("--");
emp = null;
rs.next();
}
rs.clearAll();
}
注:SqlServerPaginationImp和SybasePaginationImp实现依赖与
Sp_Pagination
数据库存储过程,具体请参考java doc的api文档。
- 大小: 13.3 KB
分享到:
相关推荐
Beetle J2ee Application Framework(BJAF)一个功能强大而简单易用的J2EE应用开发框架。它涵盖了J2EE体系结构的表示层、业务层和持久层,为构建一个可靠、高性能、可扩展、灵活缩放的高质量企业应用系统提供了一套...
BJAF(beetle-j2ee-application-framework)甲壳虫J2EE应用开发框架代码&开发指南 甲壳虫,一个功能强大而简单易用的J2EE应用开发框架。它涵盖了J2EE体系结构的表示层、业务层和持久层,为构建一个可靠、高性能、可...
beetle 开源框架书的源码 beetle 开源框架书的源码
软件测试的利器。 最新版本请到这下载www.popular-soft.com/beetle/zh-cn/
已经成功应用于多个高并发量的TCP、UDP框架通信接口,
数据操作的jar包,包括oracle/mssql,通用~
X协议解析测试程序,服务器和客户端都有,字数字数字数
Beetle.zipscratch2.0 3.0编程项目源文件源码经典游戏案例素材源代码Beetle.zipscratch2.0 3.0编程项目源文件源码经典游戏案例素材源代码Beetle.zipscratch2.0 3.0编程项目源文件源码经典游戏案例素材源代码Beetle....
BAS: Beetle Antennae Search Algorithm for Optimization Problems。 论文英文原文。
有需要的朋友就下吧
甲壳虫 PSX 剧本Beetle PSX 是 Mednafen 的 PSX 模块到 libretro API 的端口/分支。 可以用C++98模式编译,不包括Vulkan渲染器,暂时是用C++11写的。 Beetle PSX 目前在 Linux、OSX 和 Windows 上运行。 这个分叉中...
适用于Chrome的高清版19张adung甲虫图像新标签页。 粪便甲虫是部分或完全以粪便为食的甲虫。 一头甲虫可以在一个晚上掩埋比自己重250倍的粪便。 许多被称为“滚子”的粪便甲虫会将粪便滚成圆球,用作食物来源或繁殖...
:lady_beetle: SQLIZE-LIFE-API NodeJs Express MySQL Sequalize RESTful API :lady_beetle: 堆栈 固件/库 描述 :large_orange_diamond: 节点 :large_orange_diamond: 节点快车 宁静的API :large_orange_...
Python从零开始,一点一滴学习爬虫。-dung_beetle
c#传输文件传输用到的类, 听说效率不错,具体没用过
beetle是基于c#编写的高性能Socket tcp组件,它基于SocketAsyncEventArgs的实现并提供了发送队列和接收队列的支持,可以根据情况灵活地设置1-N个发送队列和接收队列。除了队列上的支持组件还提供Buffer缓冲池和基于...
蜣螂优化算法是最新的群智能优化算法,2022年底提出,里面有相关的文章和代码,可以结合自身课题进行研究,值得推荐,亲用优化效果非常的好。
scratch编程项目源代码文件案例素材-Beetle.zip
欢迎使用有趣的搜索框。 我们在27-02-2018中添加了最迷人的高品质大众甲壳虫壁纸您想看大众甲壳虫的壁纸吗? 有数百种壁纸可以查看更多壁纸,请浏览我们的网站。 您可以在funsearchox.com上找到很多壁纸以及更多! ...
使用submissionid查询题解 使用示例: LeetCodeHelper是一个帮助类,帮助使用者用于和LeetCode交互,具体的使用方法可以看test中的使用方法 实例化帮助类LeetCodeHelper:gear: 三个参数分别用来用于发起http请求,...