【问题标题】:Strategy for locale sensitive sort with pagination带分页的区域敏感排序策略
【发布时间】:2010-05-05 05:54:34
【问题描述】:

我正在开发一个部署在网络上的应用程序。该应用程序的一部分是搜索功能,其中结果显示在排序列表中。该应用程序针对使用不同语言环境(= 排序规则)的多个国家/地区的用户。我需要为所有用户找到正确排序的解决方案。

我目前在我的 SQL 查询中使用 ORDER BY 进行排序,因此排序是根据为数据库设置的语言环境(或 LC_LOCATE)进行的。这些规则对于那些区域设置与数据库设置不同的用户是不正确的。

此外,为了使问题进一步复杂化,我在应用程序中使用了分页,因此当我查询数据库时,我会根据需要的页面询问第 1 - 15、16 - 30 行等。但是,由于排序错误,每个页面都包含排序错误的条目。在最坏的情况下,给定页面的整个结果集可能会出现乱序,具体取决于当前用户的区域设置/排序规则。

如果我要在(服务器端)代码中排序,我需要从数据库中检索所有行然后排序。考虑到数据量,这会导致巨大的性能损失。因此我想避免这种情况。

有没有人有策略(甚至是技术解决方案)来解决这个问题,这将导致正确排序的列表,而不必承担加载所有数据的性能损失?

技术细节:数据库是 PostgreSQL 8.3,应用程序是使用 EJB QL 进行数据查询的 EJB3 应用程序,在 JBoss 4.5 上运行。

【问题讨论】:

    标签: java postgresql sorting pagination locale


    【解决方案1】:

    你愿意用 C 开发一个小的 Postgres 自定义功能模块吗? (对于有经验的 C 编码人员来说可能只有几天。)

    strxfrm() 是基于当前 LC_COLLATE 设置(或多或少是当前语言)将依赖于语言的文本字符串转换为转换后的字符串的函数,如果按二进制字节排序,该字符串会在该语言中产生正确的排序顺序序列(例如strcmp())。

    如果你为 Postgres 实现这个,假设它需要一个字符串和一个排序规则,那么你将能够通过 strxfrm(textfield, collat​​ion_order) 排序。我认为您甚至可以使用该函数在您的文本列上创建多个功能索引(例如每种语言一个)来存储 strxfrm() 的结果,以便优化器使用该索引。

    或者,您可以加入 Postgres 开发人员的行列,在主流 Postgres 中实现这一点。以下是有关此问题的 wiki 页面:CollationICU(据我所知,Java 也使用它)。


    或者,如果数据仅通过 Java 输入,则作为一种不太复杂的解决方案,您可以在将数据添加到数据库时用 Java 计算这些 strxfrm() 值(Java 可能对此概念有不同的名称),并且然后让 Postgres 按这些预先计算的值进行索引和排序。

    【讨论】:

      【解决方案2】:

      您与 PostgreSQL 的关系如何? documentation 并不乐观:

      某些语言环境类别的性质是它们的值必须在数据库集群的生命周期内固定。也就是说,一旦 initdb 运行,您就不能再更改它们了。 LC_COLLATELC_CTYPE 就是这些类别。它们会影响索引的排序顺序,因此它们必须保持固定,否则文本列上的索引将损坏。 PostgreSQL 通过记录 initdb 看到的 LC_COLLATELC_CTYPE 的值来强制执行此操作。服务器在启动时会自动采用这两个值。

      (排序规则定义文本的排序方式。)

      谷歌抛出patch under discussion

      PostgreSQL 目前一次只支持一种排序规则,由初始化数据库集群时的 LC_COLLATE 变量修复。

      我不确定我是否想在数据库之外管理它,但我有兴趣阅读有关如何完成它的信息。 (任何想要对这些问题有一个好的技术概述的人都应该在Oracle globalization site 上查看Sorting Your Linguistic Data inside the Oracle Database。)

      【讨论】:

      • PostgreSQL 8.4(当前版本)支持每个数据库的区域设置。它远非完美,但比 8.3 好很多。
      【解决方案3】:

      我不知道有什么方法可以切换数据库order by 命令。因此,必须考虑其他解决方案。

      如果结果的数量真的很大(数十万?),我没有解决方案,除了只显示结果的数量,并要求用户提出更精确的请求。否则,服务器端可以这样做,具体取决于确切的条件......

      特别是,使用缓存可以极大地改善事情。对数据库的第一个请求(无限制)不会比结果数量有限的查询慢多少。随后的请求会快得多。通常,分页和重新排序会产生多个请求,因此缓存可以正常工作(即使持续几分钟)。

      我使用 EhCache 作为技术解决方案。 排序和分页一起进行,排序然后分页。 原始结果可以存储在缓存中。

      为了降低性能损失,一些提示:

      • 您可以针对结果集大小运行一次查询,并在结果过多时警告用户(要求确认慢速查询,或添加一些选择字段)
      • 只请求您需要的列,放开所有其他列(通常某些数据不会立即显示所有结果,而是会在例如鼠标移动时显示;这些数据可以懒惰地请求,仅根据需要,因此减少了为所有结果请求的列)
      • 如果您有计算值,缓存数据库列和计算值之间的较小值
      • 如果您在多个结果中有重复值,您可以分别请求该数据/列(因此您从数据库中检索一次,只缓存一次),仅检索一个键(通常,和 id) 在主请​​求中。

      【讨论】:

        【解决方案4】:

        您可能想签出这个包:http://www.fi.muni.cz/~adelton/l10n/postgresql-nls-string/。它已经很久没有更新了,可能不再工作了,但如果你想构建一个可以为你做这件事的函数,这似乎是一个合理的起点。

        【讨论】:

          【解决方案5】:

          此模块在 Postgres 8.4.3 中已损坏。我修复了它 - 您可以从 http://www.itreport.eu/__cw_files/.01/.17/.ee7844ba6716aa36b19abbd582a31701/nls_string.c 下载固定版本,您必须手动编译和安装它(如相关 README 和原始模块中的安装所述),但无论如何排序工作不正确。我在 FreeBSD 8.0 上试过,LC_COLLATE 是 cs_CZ.UTF-8

          【讨论】:

          • 实际上看起来 UTF-8 + Postgres + Collat​​ion 是有史以来最糟糕的组合。很可能我们必须对 Postgres 本身应用一些 ICU 补丁,因为简单的测试表明 strcoll 在 FreeBSD 下对 cs_CZ.UTF-8 工作不正确。
          • 找到了解决方案 - 对于 FreeBSD 必须标记使用 ICU(我选择版本 4) - 在这种情况下,Postgres 将使用内部排序规则而不是 FreeBSD 损坏的排序规则。这种情况下的 nls_string 已过时。另外,当然,为 DB 设置正确的 LC_COLLATE。
          猜你喜欢
          • 2011-03-14
          • 2018-04-08
          • 1970-01-01
          • 2011-12-08
          • 1970-01-01
          • 1970-01-01
          • 2012-08-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多