重新打开Chronicle Map时出现Checksum不匹配错误问题咨询
问题环境
- 使用Chronicle Map版本为3.20.84
问题现象
重新打开持久化的Chronicle Map实例时,输出如下报错日志:
map.ChronicleMapBuilder - Checksum doesn't match, stored: -1805860448, should be from the entry bytes: 1297789250, key: 4-US9024941034, value:...
已排查操作
- 查阅项目公开issue记录,其中存在完全一致的问题描述,记录显示此类报错的常见诱因是Map实例关闭后仍存在写入操作。
- Chronicle Map 3.x版本内置自动关闭Map的shutdown hook(关闭钩子)机制,由于JVM关闭钩子的执行顺序无明确保证,可能出现钩子触发关闭Map后仍有写入逻辑执行的情况,因此已手动关闭该自动关闭特性,改为在业务逻辑中主动显式关闭Map实例。
- 调整后重新打开此前正常关闭的Map实例时,仍然出现上述Checksum不匹配报错。
根因分析
除了shutdown hook触发顺序问题外,该版本Checksum校验失败还有三个非常常见的触发原因:
- 手动关闭的流程顺序错误:业务代码调用
close()的位置没有放在所有写入逻辑的绝对下游,多线程异步写入任务未完全终止时,主线程就触发Map关闭,本质和shutdown hook顺序问题是一回事,只是把JVM自动触发的顺序问题变成了业务代码手动触发的顺序问题。最典型的场景是用线程池做异步写入时,没有先停线程池、等所有写入任务跑完,就直接关Map,照样会出现关完还有写入的情况,把持久化文件写坏。 - 3.20.84版本刷盘机制缺陷:这个版本默认的持久化刷盘策略不会在调用
close()时强制把所有页缓存的修改同步到磁盘,操作系统页缓存还没把数据落盘的时候进程就退出了,会导致文件里部分写入内容缺失,校验和自然对不上。 - 多进程并发写入冲突:如果没做排他访问控制,同一时间有多个JVM进程拿着同一个持久化文件的写权限,哪怕只有很短时间的并发写入,也会直接把内存映射页的内容写乱,触发校验和错误。
修复方案
- 严格调整关闭流程的执行顺序:所有持有Chronicle Map写入引用的业务线程、异步任务必须先完全终止,确认没有任何正在跑、排队等执行的写入操作后,再调用Map的
close()方法。如果用线程池处理写入,必须严格按「停止接新的写入请求→触发线程池shutdown→等所有已提交的任务跑完(设好合理的超时时间)→关Chronicle Map实例」的顺序走,不能跳步。注意:绝对禁止在写入任务没全部停完的时候调用Map的close()方法,这是3.x版本持久化文件损坏的最高频诱因。 - 开启关闭时强制刷盘配置:构建Map实例的时候,加上
builder.forceFlushOnClose(true)配置,开启关闭时强制刷盘,调用close的时候会主动把内存映射里的内容强制同步到磁盘,避免页缓存没来得及落盘导致的文件损坏。 - 打开文件排他锁校验:构建Map的时候加上
builder.tryLockFile(true)配置,启动的时候自动给持久化文件加排他锁,从机制上堵死多进程同时写同一个文件的可能。 - 损坏文件的兜底处理:3.x版本没有自带的损坏文件修复工具,已经出现校验和损坏的持久化文件,优先从备份恢复;如果业务能接受丢这部分数据,可以直接删掉损坏的持久化文件,重启的时候重新构建Map实例,也可以在启动逻辑里加校验和错误检测,触发自动重建,避免进程直接起不来。
内容的提问来源于stack exchange,提问作者Stuart Goldberg
相关产品推荐
相关产品推荐

