【发布时间】:2010-10-21 02:04:45
【问题描述】:
我想在浏览器/多个页面上显示 100000 条记录,对内存的影响最小。即每页 100 条记录。
我想来回移动页面。我的疑惑是
1. 我可以保留内存中的所有记录吗?这是个好主意吗?
2) 我可以为每页建立数据库连接/查询吗?如果是这样如何编写查询?
谁能帮帮我..
【问题讨论】:
我想在浏览器/多个页面上显示 100000 条记录,对内存的影响最小。即每页 100 条记录。
我想来回移动页面。我的疑惑是
1. 我可以保留内存中的所有记录吗?这是个好主意吗?
2) 我可以为每页建立数据库连接/查询吗?如果是这样如何编写查询?
谁能帮帮我..
【问题讨论】:
在内存中维护这么多记录通常不是一个好主意。如果应用同时被多个用户访问,内存影响会很大。
我不知道您使用的是什么 DBMS,但在 MySQL 和其他几个数据库中,您可以依靠 DB 进行分页查询,例如:
SELECT * FROM MyTable
LIMIT 0, 100
limit 之后的第一个数字是偏移量(它将跳过多少条记录),第二个是它将获取的记录数。
请记住,SQL 在每个 DB 上都没有相同的语法(有些甚至不支持)。
【讨论】:
我不会将数据保存在内存中(无论是在浏览器中还是在服务应用程序中)。相反,我会使用 SQL 对结果进行分页。
如何做到这一点可能是特定于数据库的。有关 MySql 中的一个示例,请参见 here。其他数据库将存在机制。
【讨论】:
1) 不,将所有记录都保存在内存中有点违背了拥有数据库的意义。考虑拥有一个可滚动的结果集,这样您就可以获得所需的功能,而无需使用 SQL。您还可以调整一次提取的记录数,以免加载的记录超出您的需要。
2) 创建和销毁数据库连接的成本很高,但任何严重的系统都会将连接池化,因此对性能的影响不会那么大。
如果您想要更花哨,您可以完全取消页面,只需在用户滚动列表时加载更多记录。
【讨论】:
这不是一个好主意,因为您正在让浏览器可执行文件包含所有这些。
当我有这样的事情要做时,我使用 javascript 来呈现页面,然后进行 ajax 调用以获取下一页。在您获取下一个表时,显示下一个表会有一点延迟,但用户已经习惯了。
如果你显示 100 条记录/页,使用 json 从服务器传递数据,因为 javascript 可以快速解析它,然后使用 innerHTML 放置 html,因为 DOM 在呈现表格时要慢得多。
【讨论】:
正如其他人在这里提到的,将大量结果列表存储在内存中并不是一个好主意。查询每个页面的结果无疑是一种更好的方法。为此,您有两种选择。一种是使用您的 DBMS 提供的任何数据库特定功能来定位查询结果的特定子部分。另一种方法是使用 JDBC 提供的泛型方法来达到相同的效果。这可以防止您的代码被绑定到特定的数据库:
// get a ResultSet from some query
ResultSet results = ...
if (count > 0) {
results.setFetchSize(count + 1);
results.setFetchDirection(ResultSet.FETCH_FORWARD);
results.absolute(count * beginIndex);
}
for (int rowNumber = 0; results.next(); ++rowNumber) {
if (count > 0 && rowNumber > count) {
break;
}
// process the ResultSet below
...
}
使用像 Spring JDBC 或 Hibernate 这样的库可以使这更容易。
【讨论】:
在许多 SQL 语言中,您都有 LIMIT (mysql, ...) 或 OFFSET (mssql) 的概念。 您可以使用这种东西来限制每页的行数
【讨论】:
取决于数据。如果您正在缓存它,100k int 可能还不错。
T-SQL 有 SET @@ROWCOUNT = 100 来限制返回的记录数量。
但要正确执行并返回总页数,您需要更高级的分页 SPROC。
这是一个非常热门的话题,有很多方法可以做到。
这是我写的一个旧 sproc 的示例
CREATE PROCEDURE Objects_GetPaged
(
@sort VARCHAR(255),
@Page INT,
@RecsPerPage INT,
@Total INT OUTPUT
)
AS
SET NOCOUNT ON
--Create a temporary table
CREATE TABLE #TempItems
(
id INT IDENTITY,
memberid int
)
INSERT INTO #TempItems (memberid)
SELECT Objects.id
FROM Objects
ORDER BY CASE @sort WHEN 'Alphabetical' THEN Objects.UserName ELSE NULL END ASC,
CASE @sort WHEN 'Created' THEN Objects.Created ELSE NULL END DESC,
CASE @sort WHEN 'LastLogin' THEN Objects.LastLogin ELSE NULL END DESC
SELECT @Total=COUNT(*) FROM #TempItems
-- Find out the first and last record we want
DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@Page - 1) * @RecsPerPage
SELECT @LastRec = (@Page * @RecsPerPage + 1)
SELECT *
FROM #TempItems
INNER JOIN Objects ON(Objects.id = #TempItems.id)
WHERE #TempItems.ID > @FirstRec AND #TempItems.ID < @LastRec
ORDER BY #TempItems.Id
【讨论】:
我建议您选择使用 CachedRowSet 。
CachedRowSet 对象是数据行的容器,将其行缓存在内存中,这样就可以在不始终连接到其数据源的情况下进行操作。
CachedRowSet 对象是一个断开连接的行集,这意味着它仅短暂地使用与其数据源的连接。它在读取数据以用行填充自身时连接到其数据源,并在将更改传播回其底层数据源时再次连接。
因为 CachedRowSet 对象将数据存储在内存中,所以它在任何时候可以包含的数据量取决于可用的内存量。为了解决这个限制,CachedRowSet 对象可以从 ResultSet 对象中以数据块(称为页面)的形式检索数据。为了利用这种机制,应用程序使用 setPageSize 方法设置要包含在页面中的行数。换句话说,如果页面大小设置为 5,则一次会从数据源中获取包含 5 行数据的块。应用程序还可以选择设置一次可以获取的最大行数。如果最大行数设置为零,或者没有设置最大行数,则一次可以提取的行数没有限制。
设置属性后,必须使用方法填充或执行方法向 CachedRowSet 对象填充数据。以下代码行演示了使用方法填充。请注意,此版本的方法有两个参数,一个 ResultSet 句柄和 ResultSet 对象中的行,从该行开始检索行。
CachedRowSet crs = new CachedRowSetImpl();
crs.setMaxRows(20);
crs.setPageSize(4);
crs.populate(rsHandle, 10);
当此代码运行时,crs 将从 rsHandle 中填充四行,从第十行开始。
在类似的路径上,您可以建立一个策略,在 JSP 上对数据进行分页等等。
【讨论】: