6

我正在设计一个多层数据库驱动的 Web 应用程序——SQL 关系数据库、用于中间服务层的 Java、用于 UI 的 Web。语言真的不重要。

中间服务层执行数据库的实际查询。UI 只是要求某些数据,并且不知道它是由数据库支持的。

问题是如何处理大型数据集?UI 要求提供数据,但结果可能很大,可能太大而无法放入内存。例如,路牌应用程序可能具有以下服务层:

StreetSign getStreetSign(int identifier)
Collection<StreetSign> getStreetSigns(Street street)
Collection<StreetSign> getStreetSigns(LatLonBox box)

UI 层要求让所有街道标志符合某些标准。根据标准,结果集可能很大。UI 层可能会将结果分成单独的页面(用于浏览器)或将它们全部呈现(服务于 Goolge Earth)。潜在的巨大结果集可能是性能和资源问题(内存不足)。

一种解决方案是不返回完全加载的对象(StreetSign 对象)。而是返回某种延迟加载每个单独对象的结果集或迭代器。

另一种解决方案是更改服务 API 以返回请求数据的子集:

Collection<StreetSign> getStreetSigns(LatLonBox box, int pageNumber, int resultsPerPage)

当然 UI 仍然可以请求一个巨大的结果集:

getStreetSigns(box, 1, 1000000000)

我很好奇这种场景的标准行业设计模式是什么?

4

10 回答 10

6

第一个问题应该是:

¿ 用户需要或有能力管理这些数据量吗?

虽然结果集应该被分页,但如果它的潜在大小如此巨大,答案将是“可能不会”,所以 UI 不应该尝试显示它。

我在医疗保健系统上从事 J2EE 项目,该项目处理大量存储数据,数以百万计的患者、访问、表格等,一般规则是任何用户搜索不显示超过 100 或 200 行,建议那些标准集产生更多他可以理解的信息的用户。

实现这一点的方式因项目而异,可以强制 UI 在启动查询之前询问服务层查询的大小,或者如果结果集增长,可以从服务层抛出异常太多(但是这种方式将服务层与有限的 UI 实现结合在一起)。

当心!这并不意味着服务层上的每个方法都必须在其结果大小超过 100 时抛出异常,此一般规则仅适用于直接向用户显示的结果集,这是将控件放置在 UI 中的更好理由而是在服务层上。

于 2008-10-23T23:39:05.040 回答
2

我在这种情况下看到的最常见的模式是某种分页,通常在服务器端完成以减少通过网络发送的信息量。

这是一个使用表变量(通常比临时表快)的 SQL Server 2000 示例以及您的街道标志示例:

CREATE PROCEDURE GetPagedStreetSigns
(
  @Page int = 1,
  @PageSize int = 10
)
AS
  SET NOCOUNT ON

  -- This memory-variable table will control paging
  DECLARE @TempTable TABLE (RowNumber int identity, StreetSignId int)

  INSERT INTO @TempTable
  (
     StreetSignId
  )
  SELECT [Id]
  FROM   StreetSign
  ORDER BY [Id]

  -- select only those rows belonging to the requested page
  SELECT SS.*
  FROM   StreetSign SS
         INNER JOIN @TempTable TT ON TT.StreetSignId = SS.[Id]
  WHERE  TT.RowNumber BETWEEN ((@Page - 1) * @PageSize + 1) 
                      AND (@Page * @PageSize)

在 SQL Server 2005 中,您可以更聪明地使用 Common Table Expressions 和新的 SQL Ranking 函数之类的东西。但总的主题是您使用服务器只返回属于当前页面的信息。

请注意,如果您允许最终用户将即时过滤器应用于她/他所看到的数据,这种方法可能会变得混乱。

于 2008-10-23T23:33:55.503 回答
1

我会说,如果存在大量数据的潜力,那么就走寻呼路线。

您仍然可以设置不希望他们超过的 MAX。

EG SO 使用的页面大小为 15、30、50...

于 2008-10-23T22:41:13.220 回答
1

在使用像您(显然)拥有的本土行包装类时要警惕的一件事是,在您(开发人员)不知道的情况下对数据库进行额外调用的代码。例如,您可能会调用一个返回 Person 对象集合的方法,并认为在后台发生的唯一事情是单个“SELECT * FROM PERSONS”调用。实际上,您调用的方法可能会遍历返回的 Person 对象集合,并进行额外的 DB 调用来填充每个 Person 的 Orders 集合。

正如您所说,您的解决方案之一是不返回完全加载的对象,因此您可能已经意识到这个潜在的问题。我倾向于避免使用行包装器的原因之一是它们总是难以调整您的应用程序并最小化数据库流量的大小和频率。

于 2008-10-23T23:56:30.153 回答
0

从数据检索层来看,标准设计模式是有两个方法接口,一个用于所有,一个用于块大小。

如果您愿意,您可以对在其上进行分页的组件进行分层。

于 2008-10-24T19:40:33.667 回答
0

在 ASP.NET 中,我将使用服务器端分页,您只检索用户从数据存储中请求的数据页面。这与检索整个结果集、将其放入内存并根据请求对其进行分页相反。

于 2008-10-23T22:39:37.670 回答
0

JSF 或 JavaServerFaces 具有用于将大型结果集分块到浏览器的小部件。它可以按照您的建议进行参数化。无论如何,我都不会称其为“标准行业设计模式”,但值得看看其他人如何解决这个问题。

于 2008-10-23T22:39:46.343 回答
0

当我处理这种类型的问题时,我通常将发送到浏览器(或瘦/胖客户端,以更适合您的情况)的数据分块,而不管满足某些特定标准的数据的实际总大小,仅一小部分确实可以一次在任何 UI 中使用。

我生活在微软的世界里,所以我的主要环境是带有 SQL Server 的 ASP.Net。这里有两篇关于分页的文章(其中提到了一些通过结果集进行分页的技术),它们可能会有所帮助:

使用 ASP.NET 2.0 高效地(并以 Ajax 方式)分页大量数据 使用 ASP.NET 2.0 DataList 控件和 ObjectDataSource 的高效数据分页

微软最近发布的另一种机制是他们的“动态数据”理念——你可能可以查看它的核心内容,以获得关于他们如何处理这个问题的一些指导。

于 2008-10-23T22:50:05.090 回答
0

我在两种不同的产品上做过类似的事情。在一种情况下,数据源可以选择分页——对于 java,实现类似于以下的 Pageable 接口:

public interface Pageable
{
    public void setStartIndex( int index );
    public int getStartIndex();
    public int getRowsPerPage() throws Exception;
    public void setRowsPerPage( int rowsPerPage );
}

数据源实现了另一种获取项目的方法,分页数据源的实现只返回当前页面。所以你可以设置你的起始索引,并在你的控制器中抓取一个页面。

要考虑的一件事是缓存您的游标服务器端。对于网络应用程序,您必须使它们过期,但它们确实有助于提高性能。

于 2008-10-23T22:51:27.913 回答
0

fedora 数字存储库项目使用结果集 ID 返回最大数量的结果。然后,您通过在后续查询中请求提供 result-set-id 的下一个块来获得其余的结果。只要您不想在查询之外进行任何搜索或排序,它就可以正常工作。

于 2008-10-23T23:25:12.557 回答