Mnesia分表场景下mnesia:read返回空列表但存在对应记录的问题
Mnesia分片表中
mnesia:read返回空但dirty_read正常的问题排查与解决 嘿,这个问题我之前在做Mnesia分片开发的时候也踩过坑!结合你的代码场景,咱们一步步拆解问题出在哪,以及怎么解决:
问题重现
先还原你的场景,你创建分片表的代码如下:
PrimProps = [{n_fragments, 64}, {n_disc_only_copies, 1}, {node_pool, [node()]}], mnesia:create_table(offline_msg, [{disc_only_copies, [node()]},{type, bag},{attributes, record_info(fields, offline_msg)}, {frag_properties, PrimProps}])
然后出现了诡异的差异:
- 事务内调用
mnesia:dirty_read能正常拿到记录:F = fun() -> mnesia:dirty_read({offline_msg, <<0,0,0,0,0,0,0,11>>}) end. Result = mnesia:activity(transaction, F, [],mnesia_frag). % 返回结果: [#offline_msg{userid = <<0,0,0,0,0,0,0,11>>,timestamp =1547039796317984,from = 123}] - 但同样在事务内调用
mnesia:read却返回空列表:F = fun() -> mnesia:read({offline_msg, <<0,0,0,0,0,0,0,11>>}) end. Result = mnesia:activity(transaction, F, [],mnesia_frag). % 返回结果: []
问题根源分析
出现这种差异的核心原因有两个:
- 表配置冲突:你创建表时同时在外层指定了
disc_only_copies,又在frag_properties里设置了n_disc_only_copies。Mnesia分片表的副本配置应该完全由frag_properties管理,重复设置会导致分片的元数据混乱,事务内的mnesia:read无法正确定位到分片的存储位置。 - API使用场景不匹配:
mnesia:read是针对普通表的事务读取API,在分片表场景下,它无法自动完成分片路由;而mnesia:dirty_read是直接跳过事务的一致性校验,直接读取磁盘数据,所以能绕过路由问题拿到记录。
解决方案
方案1:修正表创建的配置冲突
去掉外层的disc_only_copies配置,让分片属性单独管理副本类型:
PrimProps = [{n_fragments, 64}, {n_disc_only_copies, 1}, {node_pool, [node()]}], mnesia:create_table(offline_msg, [{type, bag}, {attributes, record_info(fields, offline_msg)}, {frag_properties, PrimProps}])
注意:如果表已经创建,需要先删除表再重新创建,或者用mnesia:change_table_frag/2修正配置。
方案2:使用分片表专用的读取API
在事务内,改用mnesia_frag模块提供的专用读取函数,它会自动处理分片路由:
F = fun() -> mnesia_frag:read(offline_msg, <<0,0,0,0,0,0,0,11>>) end. Result = mnesia:activity(transaction, F, [], mnesia_frag).
方案3:手动指定分片路由(应急方案)
如果暂时无法修改表配置,可以手动计算分片ID,直接读取对应的分片表:
F = fun() -> TargetKey = <<0,0,0,0,0,0,0,11>>, % 计算目标分片ID FragId = mnesia_frag:fragment_id(offline_msg, TargetKey), % 获取分片表的真实名称 FragTableName = mnesia_frag:fragment_name(offline_msg, FragId), % 直接读取分片表 mnesia:read({FragTableName, TargetKey}) end. Result = mnesia:activity(transaction, F, [], mnesia_frag).
总结
本质上,dirty_read是“暴力”直接读磁盘,而事务内的read需要依赖正确的元数据做分片路由。解决这个问题的核心是保证分片表的配置正确,并且使用对应场景的API。
内容的提问来源于stack exchange,提问作者Chelin Tsien
相关产品推荐
相关产品推荐

