H2 数据库性能异常 --- 或如何有效地 `count(*)`

Posted

技术标签:

【中文标题】H2 数据库性能异常 --- 或如何有效地 `count(*)`【英文标题】:H2 database performance strangeness --- or how to efficiently `count(*)` 【发布时间】:2017-02-09 09:05:18 【问题描述】:

设置再简单不过了:

H2 版本 1.3.176 一个表,10 列,其中两列有点长,典型值长度为 300 和 3500 个字符 简单查询:select count(*) from requestrepository where request_type = 'ADD' 索引在查询列上。 查询的列只是 varchar(20)(即不是较长的列之一) 查询的列只包含两个不同的值,一个出现 20 万次,另一个出现 1200 万次。 DB 使用 SSD、当前服务器硬件、当前 Java 8(略有不同,但结果没有变化)

我做了什么:(0) 运行analyze,(1) 通过键字段删除一行,(2) 为刚刚删除的键插入一行,(3) 运行上面引用的查询,计数到 10并重复。

我所看到的:上面引用的查询每次需要 3 到 5 秒,explain analyze 说:

SELECT
    COUNT(*)
FROM PUBLIC.REQUESTREPOSITORY
    /* PUBLIC.IX_REQUESTS: REQUEST_TYPE = 'ADD' */
    /* scanCount: 12098748 */
WHERE REQUEST_TYPE = 'ADD'
/*
REQUESTREPOSITORY.IX_REQUESTS read: 126700
*/

我在不同的机器、hardware/linux/ssd、VM/Windows/netapp 上尝试了相同的 DB,但趋势始终相同:count(*) 花费的时间太长(?)。

这是我不确定的。预计这需要很长时间吗?我原以为至少在第二轮中,缓存已被填满,这应该会快得多,但explain analyze 总是列出 126700 次读取。

任何关于 H2 参数或设置如何改进的提示都表示赞赏。

编辑(不确定这是否应该作为答案) 同时我们尝试了很多东西,包括 mvstore、1.4.x、并行线程、不同磁盘的计算机、Linux、Windows。情况总是一样的。占用超过 10 或 1200 万行,一个具有三个状态值的 varchar 列,例如 PROCESSING、ADD、DELETE,列上的索引和一个严重过度表示的状态:然后像 count(*) where colname='ADD' 这样的东西在每次更新后需要 1 到很多秒表。

为了防止这造成问题,我们最终修复了我们自己的代码,该代码执行了三个count(*),每个状态一个,而不是一个group by,并且每 5 秒运行一次,而不是按需运行。当然不是我们拥有的最伟大的设计。

我唯一的借口是,我仍然很惊讶count(*) 在这样的设置中花了这么长时间。我的直觉是,必须在更新后通过真正计数来计算索引,而我希望可以从某处的数据结构中读取计数。 (没有批评,我自己肯定无法实现数据库。)

【问题讨论】:

您是否尝试过使用最新版本的 H2(MVStore 格式)? 【参考方案1】:

不确定 H2,但您是否尝试过 COUNT(request_type) 而不是 COUNT(*)

SQL 标准的 COUNT(*) 往往需要很长时间来计算,因为它需要全表扫描来过滤掉仅包含 NULL 值的行。

在单个索引列上使用COUNT() 可以加快速度。这种方式不需要读取表行,因为索引足以决定列的值是否为NULL。

【讨论】:

以上是关于H2 数据库性能异常 --- 或如何有效地 `count(*)`的主要内容,如果未能解决你的问题,请参考以下文章

SQL 性能 - 更好地插入和引发异常或检查是不是存在?

如何有效地从大型数据框字典中提取同一列? (性能警告)

演员:如何有效地处理以读取为主的数据

H2 刀片性能

我怎样才能阻止 h2 如此多地阻止我的休眠查询

带有 .NET 应用程序的 H2 数据库