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(*)`的主要内容,如果未能解决你的问题,请参考以下文章