在 h2 数据库 (v1.4.195) 中的空表上重复选择 * 有多糟糕?

Posted

技术标签:

【中文标题】在 h2 数据库 (v1.4.195) 中的空表上重复选择 * 有多糟糕?【英文标题】:How bad is repeated select * on an empty table in h2 database (v1.4.195)? 【发布时间】:2018-01-17 13:44:44 【问题描述】:

所以我有一个 h2 数据库事件表,我正在监视事件。有一个线程每 2 秒触发一次,并使用 select * from eventTable limit 10 offset 0 检查。

我想知道这种锤击在 h2 数据库表中的性能影响是什么。它是基于 B 树的,但 db 本身是一个文件。 h2db 是否进入文件并必须读取块,从而确定表是否为空。想想 Oracle Db 和 High Water Mark 问题,用于查询具有大行的表,这些行稍后会在没有截断的情况下被删除,这会导致不必要的块读取以完成 select * 并且对性能不利。

如果这很糟糕,是否建议使用此 qt here 中描述的用于插入操作的触发器方法来交换线程部分。

问候

【问题讨论】:

【参考方案1】:

以下是 io 监控的一些数字:

我使用 select 语句启动了我的应用程序,该应用程序具有监视线程。然后我开始“sudo fsusage MYPID”。 4 次读取和 2 次写入的模式重复自身:

16:18:10.809774  pread F=44   B=0x400      O=0x00037000 0.000020  java.11010
16:18:10.809809  pread F=44   B=0x400      O=0x00037000 0.000005   java.11010
16:18:10.809825  pread  F=44   B=0x400      O=0x00037000 0.000003   java.11010
16:18:10.809839  pread F=44   B=0x400      O=0x00037000 0.000004   java.11010
16:18:10.810044  pwrite F=44   B=0x1000     O=0x00031000 0.000034   java.11010
16:18:10.810087  pwrite F=44   B=0x2000     O=0x00000000 0.000010   java.11010

FD是文件描述符,是通过lsof -p PID确认为数据库文件的。 B = 读取或写入的字节数。 O 是文件中的偏移量。

我经常看到上面的读写模式。读取很可能是选择。 DB 上没有其他活动。因此,即使对于空表,我也会持续读取 1600 字节的内容,并在 3000 到 4000 字节的范围内进行 2 次写入。

但是比我更详细地介绍,因为我在 macosx 上,strace 不是一个选项,但 Dtruss 工作得很好。所以只是给了 dtruss -a -p PID,下面是读写的相关输出:

632/0x653a:   2229289      37     24 pread(0x2C, "chunk:3157,block:31,len:2,map:9,max:1540,next:4d,pages:6,root:c55c0000027cf,time:18ffdc4,version:3157                                                         \n\0", 0x400, 0x31000)   = 1024 0
632/0x652c:    773689      86      2 gettimeofday(0x70000B107C68, 0x0, 0x0)      = 0 0
632/0x653a:   2229327      13      5 pread(0x2C, "chunk:3157,block:31,len:2,map:9,max:1540,next:4d,pages:6,root:c55c0000027cf,time:18ffdc4,version:3157                                                         \n\0", 0x400, 0x31000)   = 1024 0
632/0x653a:   2229347      10      4 pread(0x2C, "chunk:3157,block:31,len:2,map:9,max:1540,next:4d,pages:6,root:c55c0000027cf,time:18ffdc4,version:3157                                                         \n\0", 0x400, 0x31000)   = 1024 0
632/0x653a:   2229373      11      4 pread(0x2C, "chunk:3157,block:31,len:2,map:9,max:1540,next:4d,pages:6,root:c55c0000027cf,time:18ffdc4,version:3157                                                         \n\0", 0x400, 0x31000)   = 1024 0
632/0x653a:   2229621      45     34 pwrite(0x2C, "chunk:3159,block:24,len:1,map:9,max:b80,next:35,pages:4,root:c5640000027cf,time:19001ef,version:3159                                                          \n\0", 0x1000, 0x24000)     = 4096 0
632/0x653a:   2229686      32     24 pwrite(0x2C, "H:2,block:24,blockSize:1000,chunk:3159,created:1610362b746,format:1,version:3159,fletcher:1d05a51e\n\0", 0x2000, 0x0)         = 8192 0

因此,在 pread 和 pwrite 的返回值之上添加我可以看到实际读取为 1024 x 4 字节,写入为 4096 + 8192 字节。还可以看到读取和写入的内容。最后一次写入有时会出现,有时不会。 fread 和 fwrite 的第一个参数是文件描述符 0x2c,它与数据库文件的描述符相匹配。第二个参数是正在写入的缓冲区。我想知道为什么我们需要在这里写任何东西。但是当我在 h2 项目页面中阅读以下架构解释时,这得到了解释:

上面的写和读可以通过h2database.com/html/mvstore.html#fileFormat来解释

浏览源代码我发现我在分析器中注意到的 BackgroundWriterThread 类以及随着时间的推移不断增加字节(但没有内存泄漏,它会正确清理),每秒都在醒来并且只是盲目地提交商店。这给出了上面代码中写入和读取的位置。

更多谷歌搜索揭示了这个问题在 google 组中讨论过,尽管没有解决问题,除非后来有人发布参数 WRITE_DELAY 为他解决了问题。 groups.google.com/forum/#!searchin/h2-database/... 然后我想知道他是否没有尝试将连接时的 autoCommit 设置为 false。我试过了,上面的读写模式为我停止了。

所以将 ;AUTOCOMMIT=OFF 添加到连接参数就可以了,并且查询在内存中,因此从空表中选择 * 的开销非常小。这结束了我的调查。数据在内存中,因为我使用的是版本 1.4.195,并且默认使用 MVStore 数据库。所以查询内存中的空表应该是一个相对便宜的操作。

问候

【讨论】:

以上是关于在 h2 数据库 (v1.4.195) 中的空表上重复选择 * 有多糟糕?的主要内容,如果未能解决你的问题,请参考以下文章

Create ML 中的“指定数据源中的空表”错误

如何摆脱gridview WPF中的空表?

UIPopover 中的空表视图

for 循环是不是遍历 Lua 和 Love2d 中的空表?

如何去除SQLSERVER中的空表,要使用啥命令,或者啥方法

在脚本的空表中删除具有 PK 约束的列