在 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) 中的空表上重复选择 * 有多糟糕?的主要内容,如果未能解决你的问题,请参考以下文章
for 循环是不是遍历 Lua 和 Love2d 中的空表?