2005年02月23日

问题描述:
在通常的三层构架下,客户通过Browser请求Web服务器查询数据库,而查询结果是上千条甚至是上百万条记录,要求查询结果传送到客户端浏览器并分页显示。


考虑因素:
1. Web服务器的资源消耗,包括:内存(用来存储查询结果),数据库相关资源(数据库连接对象,ResultSet对象等等);
2. DB服务器资源的消耗,包括游标,会话等等。
3. 网络开销,包括与数据库建立会话,传输查询结果等等。


JDBC中的几个重要Class:

A ResultSet object maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The next method moves the cursor to the next row, and because it returns false  when there are no more rows in the ResultSet object, it can be used in a while loop to iterate through the result set.

ResultSet是直接在数据库上建立游标,然后通过ResultSet的行位置定位接口来获得指定行位置的记录。当用户通过get方法获取具体纪录的内容时,ResultSet才从数据库把所需数据读到客户端。

Oracle的ResultSet实现似乎会在本地缓存用户读取过的数据,导致内存消耗会随读取数据的增加而增加,这样,如果一次查询并读取海量数据,即使读出数据后马上丢弃(比如直接写入文件),内存消耗也会随查询结果的增加而递增???????


The RowSet interface extends the standard java.sql.ResultSet interface.  A RowSet object may make a connection with a data source and maintain that connection throughout its life cycle, in which case it is called a connected rowset. A rowset may also make a connection with a data source, get data from it, and then close the connection. Such a rowset is called a disconnected rowset. A disconnected rowset may make changes to its data while it is disconnected and then send the changes back to the original source of the data, but it must reestablish a connection to do so.

RowSet是JDBC2.0中提供的接口,Oracle对该接口有相应实现,其中很有用的是 oracle.jdbc.rowset.OracleCachedRowSet。 OracleCachedRowSet实现了ResultSet中的所有方法,但与ResultSet不同的是,OracleCachedRowSet中的数据在Connection关闭后仍然有效。



解决方案一:直接使用ResultSet来处理

从ResultSet中将查询结果读入collection,缓存在HttpSession或有状态bean中,翻页的时候从缓存中取出一页数据显示。这种方法有两个主要的缺点:一是用户可能看到的是过期数据;二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。

对上述方法的一种改进是当用户第一请求数据查询时,就执行SQL语句查询,获得的ResultSet对象及其要使用的连接对象都保存到其对应的会话对象中。以后的分页查询都通过第一次执行SQL获得的ResultSet对象定位取得指定页的记录(使用rs.last();rs.getRow()获得总计录条数,使用rs.absolute()定位到本页起始记录)。最后在用户不再进行分页查询时或会话关闭时,释放数据库连接和ResultSet对象等数据库访问资源。每次翻页都只从ResultSet中取出一页数据。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是回缓存所有记录而占用大量内存,同时速度也非常慢。

在用例分页查询的整个会话期间,一个用户的分页查询就要占用一个数据库连接对象和结果集的游标,这种方式对数据库的访问资源占用比较大,并且其利用率不是很高。

优点:减少了数据库连接对象的多次分配获取,减少了对数据库的SQL查询执行。

缺点:占用数据库访问资源-数据库连接对象,并占用了数据库上的资源-游标;会消耗大量内存;



解决方案二:定位行集SQL查询

使用数据库产品的提供的对查询的结果集可定位行范围的SQL接口技术。在用户的分页面查询请求中,每次可取得查询请求的行范围的参数,然后使用这些参数生产取得指定行范围的的SQL查询语句,然后每次请求获得一个数据库连接对象并执行SQL查询,把查询的结果返回给用户,最后释放说有的数据库访问资源。

这种方式需要每次请求时都要执行数据库的SQL查询语句;对数据库的访问资源是使用完就立即释放,不白白占用数据库访问资源。 对特定(提供了对查询结果集可定位功能的)的数据库产品,如:Oracle(rowid或rownum ),DB2(rowid或rownum ()), PostgreSQL(LIMIT 和 OFFSET),mySQL(Limit)等。(MS SQL Server 没有提供此技术。)

下面是在oracle下的查询语句示例:
SELECT * FROM ( SELECT row_.*, rownum rownum_ FROM (…… ) row_ WHERE rownum <= {pageNumber*rowsPerPage}) WHERE rownum_ > {(pageNumber-1)*rowsPerPage}

优点:对数据库的访问资源(数据库连接对象,数据库游标等)没有浪费,这些资源的充分重复的利用。

缺点:对每次分页面查询请求要频繁的从Web容器中获得数据库访问资源(数据库连接对象和数据库游标)并建立连接;要依赖于具体的数据库产品的支持。



事实上方案一只适合中小数据量,海量数据恐怕一定要借鉴方案二。

本文只是抛砖引玉,希望能有更好的方案。


2005年02月19日

1、上学时周末回家,晚饭后烟瘾犯了,打算借口去散步。在门口换鞋时,老爸问我干吗去?我说:“去抽个烟!”结果老爸从我身上搜出一包555,狠狠K了我一顿。

2、一次从妈妈那里出来后到老婆那里去,看见老婆后,习惯性的叫了一声:“妈!”

3、早上上班,发现自行车没气了,于是想叫妈妈推到门外打气。结果我说:“把我的轮胎推出去。”妈妈迷糊了,我笑着连忙改正,结果又说成:“帮我的汽车打点气!”

4、一次我开车,坐我旁边的女同事突然问:“你怎么开车不系安全套的?”

5、一次在厕所方便,没纸了。就对老婆说:“把擦纸的屁股拿来!”

6、一个女孩失恋了,我劝她:“两条腿的蛤蟆不好找,三条腿的男人有的是啊!”

7、两个人斗嘴,突然旁边一人冒出来一句:“你们真是吃饱了事情没饭做啊!”

8、同事和人争执,急了张口来了句:“你以为我吃饭长大的啊?”我一直纳闷他到底吃什么长大的。

10、边吃饭边看帖子,边念经典的给老婆听,笑死她了,于是她对我说:“吃完饭再看吧,不然脑子消化不良!”

11、一次问一个近视的人眼睛多少度,他本想说400度的,结果一出口就成了400瓦,肚子痛死!

12、一次教育局领导视察课间操,结束后,本应由体育老师宣布“解散”,但一时情急,忘词了,憋了半天,大喊:“撤退!”

13、一体育系学生上实习课时,很多老师听课,他太紧张,最后要解散队伍时,一时脑子空白,硬憋了句:“全体注意,立正!闪!!”

14、一群同学去郊区同学家玩。我们买了几个西瓜放在厨房。叫一个同学去拿刀切,好久不见回来,正疑惑间,他手里捧着个切开的瓜来了,惊慌地说:我把南瓜给切了。大家狂笑,但两秒钟后,大家更是笑翻,原来他手里捧着个冬瓜!

15、高中有一老师姓江,酷似罗家英(演大话西游唐僧的),我去问他问题,脱口而出:“唐老师,这题……”

16、有一同事,有天我开车在路上车胎没气了,问哪里有充气的,同事说:“街上到处都是打胎的啊!”

17、一次去麦当劳买甜桶,终于轮到了,我迫不及待的说:“给我两个滚筒!”没想到那服务员对我大声的说;“两个滚筒,四块钱!”

18、俺碰到一个心仪已久的女氦从澡堂里出来,想套近乎,憋了半天憋出一句:“你洗澡啊,里面男的多不多啊?”

19、有次去吃饭,结帐时对老板说:“老公!结帐!”当时老板娘就在旁边……

20、有一老师通宵麻将,见黑板没擦,大怒:“今天谁做庄啊?黑板都不擦!”

21、有一次我大叔见我小姑在搽大宝,突然大叫一声:“你皮肤这么好,还用护舒宝啊?”

22、刚买了房子,兴奋中给一哥们打电话:‘我买房啦,不过就一毛房(忘说“坯”字了)还得装修。”哥们说:“就只有一厕所吗?那你住哪里啊?”

23、本老师留下做作业,不会做就抄别人的,,然后去办公室交作业,看见老师说:“我抄完了!”

24、某君考驾驶证那天,特别紧张。考官为难他,叫他在路边有一个消防栓的地方停车。此君特紧张的说:“报告消防栓,路边有一个考官,不允许停车!”

25、公司养一狗叫:小白。某天大家逗狗,同事甲拿着一饼干对狗说到:“小白,整个办公室只有你喂我哦。”三秒钟后,整个办公室暴笑!