Closed cbqqkcel closed 1 year ago
昨天异常太多了。我没有全部发上来。后来代码改了。没有测试代码了。 遇到过 Async callback is null, may be a bug! packetId 这个异常信息。 操作步骤是我一条一条的从 room 表查询把好的数据导出到新的 room2 表中遇到的。刚开始很快到后面就特别慢。
java.lang.StackOverflowError
昨天遇到最多的错是这个。查询一个表全部数据的时候。我觉得是数据坏了的问题导致的。很多表中的字段都是异常信息。
同时出现 OutOfMemoryError 和数据损坏这两种情况,就说明没有比这个更糟糕的了,数据库出现任何错误提示都是不奇怪的。
这个大问题的原因总结起来就两个: 1. 不能在嵌入式场景下多线程使用 lealone 了,这个很明确是有问题,并且一定会导致数据损坏的;2. 把 -XX:MaxDirectMemorySize 这个参数去掉,没用的,还可能导致 OOM。
我看了你给我的数据,数据量很小的,比我压测 tpcc 少了几百倍,表结构也没什么特殊的。 换成 client-server 模式后试试吧。
clinet-server 依旧不行 因为客户着急用我昨天晚上把数据库临时迁移到 MySQL 了。 刚才从mysql导出的sql insert 语句数据都插入进去了。查询很多数据的时候还是会报错 insert 是用datagrip 命令行插入的。
为确保干净,5.2 的 zip我刚才又重新下载了一遍,data 目录下的数据全部删除重新搞得
是不是这个表有问题
要不我把sql文件和操作步骤发给你看看
room 表有多少行数据?你在 client-server 模式下重新 insert 数据是在一张空的 room 表下进行,还是在原有的数据上进行?
26549 条数据。空表,全部都是重新来过的。
26549 条数据根本就无关痛痒,你的内存那么大,写这点数据都是直接放内存中的,还轮不到写硬盘后数据损坏。 你直接把你导入数据的 sql 发到我邮箱吧,还是我直接操作调试能更快找到原因。
我只用256M内存压测 tpcc 上百万的记录都没问题,所以我就特别奇怪你的场景到底是哪出了问题。
26549 条数据根本就无关痛痒,你的内存那么大,写这点数据都是直接放内存中的,还轮不到写硬盘后数据损坏。 你直接把你导入数据的 sql 发到我邮箱吧,还是我直接操作调试能更快找到原因。
我只用256M内存压测 tpcc 上百万的记录都没问题,所以我就特别奇怪你的场景到底是哪出了问题。
步骤和文件发你了,如果是硬件问题那就尴尬了
我定位到大概的原因了,当使用 from room limit 0,30000 查询时,记录数只要超过1万,lealone 默认就把结果集写到一个临时文件,写临时文件可能存在 bug,所以导致读取的时候出现 java.lang.IllegalStateException: Position 0 那个错误。 临时的解决方案就是执行 set max_memory_rows 1000000; 语句,把在内存中查到的结果集记录数调大一些,不写到临时文件。
insert 到 room 表的数据是没问题的,只是读查询结果集的临时文件出问题了。
-XX:MaxDirectMemorySize的作用: 大的DirectByteBuffer在堆内占用很少的内存空间,但是在堆外占用大量的内存,如果不加以限制,超过物理内存的大小就麻烦了。特别是,一些使用时间比较长的DirectByteBuffer对象,晋升到老年代,然后又变成垃圾了,如果老年代gc迟迟不触发,这部分堆外内存就不会释放。加了-XX:MaxDirectMemorySize以后,分配堆外内存如果超限制了,会触发一次堆内的gc,释放掉占坑不拉屎的DirectByteBuffer对象。 当然更好的办法是手工释放DirectByteBuffer,在java里面没有直接的办法,需要通过非标准api。
lealone 内部自己实现了一套 GC 算法,分配的 DirectByteBuffer 的大小是受 -Xmx 限制的,所以不会超过物理内存的大小,同时 GC 算法也会动态把 DirectByteBuffer 对象置 null,这时 JVM 的 GC 线程可以及时回收。 所以设置 -XX:MaxDirectMemorySize 参数对 lealone 是没有任何意义的,反而误让 JVM 干坏事了。
这里有点疑惑,你的意思是你能保证堆和直接内存的占用之和不超过 Xmx 的配置?
我定位到大概的原因了,当使用 from room limit 0,30000 查询时,记录数只要超过1万,lealone 默认就把结果集写到一个临时文件,写临时文件可能存在 bug,所以导致读取的时候出现 java.lang.IllegalStateException: Position 0 那个错误。 临时的解决方案就是执行 set max_memory_rows 1000000; 语句,把在内存中查到的结果集记录数调大一些,不写到临时文件。
insert 到 room 表的数据是没问题的,只是读查询结果集的临时文件出问题了。
非常有可能,我之前导出数据的时候 5000一次的查询就不会有问题。过1万就有可能出问题。
-XX:MaxDirectMemorySize的作用: 大的DirectByteBuffer在堆内占用很少的内存空间,但是在堆外占用大量的内存,如果不加以限制,超过物理内存的大小就麻烦了。特别是,一些使用时间比较长的DirectByteBuffer对象,晋升到老年代,然后又变成垃圾了,如果老年代gc迟迟不触发,这部分堆外内存就不会释放。加了-XX:MaxDirectMemorySize以后,分配堆外内存如果超限制了,会触发一次堆内的gc,释放掉占坑不拉屎的DirectByteBuffer对象。 当然更好的办法是手工释放DirectByteBuffer,在java里面没有直接的办法,需要通过非标准api。
lealone 内部自己实现了一套 GC 算法,分配的 DirectByteBuffer 的大小是受 -Xmx 限制的,所以不会超过物理内存的大小,同时 GC 算法也会动态把 DirectByteBuffer 对象置 null,这时 JVM 的 GC 线程可以及时回收。 所以设置 -XX:MaxDirectMemorySize 参数对 lealone 是没有任何意义的,反而误让 JVM 干坏事了。
这里有点疑惑,你的意思是你能保证堆和直接内存的占用之和不超过 Xmx 的配置?
在 java 程序中肯定不能像 c++ 那样精确控制占用了多么内存,但是可以用个预估值,比如写入一条记录可以根据字段类型以及 jvm 分配一个对象占用多少字节做一个预估,然后动态累加,删除记录时再减去。启动时拿到-Xmx的最大值,取1/3,只要累加的内存使用量超过这个阈值了 lealone 就会启动 GC 任务,回收掉数据库中那些可以踢出去的对象。
clinet-server 依旧不行 因为客户着急用我昨天晚上把数据库临时迁移到 MySQL 了。 刚才从mysql导出的sql insert 语句数据都插入进去了。查询很多数据的时候还是会报错 insert 是用datagrip 命令行插入的。
调试了代码,确实是查询记录数超过1万条后生成临时文件有 bug 导致的。
java.lang.StackOverflowError
昨天遇到最多的错是这个。查询一个表全部数据的时候。我觉得是数据坏了的问题导致的。很多表中的字段都是异常信息。
这个问题我找到原因了,查表超过1万行时,写临时文件是在一个大事务中写所有记录,所以产生了很多次 page split,在对 page 标记脏页时使用了递归,递归太深,所以就堆栈溢出了。
2023-10-26T18:34:10.351+08:00 ERROR 16245 --- [nio-5220-exec-7] c.c.i.c.advice.GlobalResponseHandler : Throwable
java.lang.IllegalStateException: Position 0 [5.2.0/6] at org.lealone.common.util.DataUtils.newIllegalStateException(DataUtils.java:616) ~[lealone-common-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.BTreeStorage.readPage(BTreeStorage.java:165) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.PageReference.readPage(PageReference.java:199) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.PageReference.getOrReadPage(PageReference.java:175) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.NodePage.getChildPage(NodePage.java:51) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.Page.gotoLeafPage(Page.java:267) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.BTreeMap.getObjects(BTreeMap.java:178) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.transaction.aote.AOTransactionMap.getObjects(AOTransactionMap.java:563) ~[lealone-aote-5.2.0.jar!/:na] at org.lealone.db.index.standard.StandardPrimaryIndex.getRow(StandardPrimaryIndex.java:325) ~[lealone-db-5.2.0.jar!/:na] at org.lealone.db.table.StandardTable.getRow(StandardTable.java:230) ~[lealone-db-5.2.0.jar!/:na] at org.lealone.db.index.standard.StandardSecondaryIndex$StandardSecondaryIndexCursor.get(StandardSecondaryIndex.java:305) ~[lealone-db-5.2.0.jar!/:na] at org.lealone.db.index.standard.StandardSecondaryIndex$StandardSecondaryIndexCursor.get(StandardSecondaryIndex.java:299) ~[lealone-db-5.2.0.jar!/:na]