Python 3.6下shelve的get()与keys()返回结果不一致问题咨询
问题原因
- shelve本身是对dbm数据库的轻量封装,Python 3.6对应的旧版dbm(尤其是gdbm<1.15版本)存在已知的迭代器缺陷:新增写入的键如果落在新的数据库存储页,
keys()、items()这类迭代方法只会读取已加载到内存的旧元数据,不会主动同步磁盘上的最新写入内容,但get()方法是直接按键查询磁盘存储,所以能返回正确结果。 - 若上一次写入shelve时没有显式调用
close()或者sync()方法,最新写入的键会存在操作系统页缓存或者dbm的写入日志中,没有合并到主数据库的元数据索引里,迭代方法读取索引时就会漏掉这部分键。 - 若该shelve文件是在更高版本Python/其他平台写入的,旧版本dbm解析索引时也可能丢失高序号键的元数据。
解决方法
- 优先尝试强制同步索引:打开shelve时先调用一次
sync()方法,强制将磁盘写入内容同步到内存索引后再读取keys(),示例代码如下:
import shelve with shelve.open("../../datasets/raw_reviews_translated_helsinki", flag='r') as db: db.sync() all_keys = list(db.keys()) print(sorted(map(int, all_keys)))
- 如果是写入时未正常关闭导致的索引损坏,可以先用
flag='w'模式打开一次再关闭,触发dbm的日志合并逻辑,修复索引后后续读取就能拿到全量键。 - 若以上方法无效,可以用兜底遍历方案:由于你的键是固定步长(100)的增量有序键,可以从最大的可查询到的键开始向上遍历,直到
get()返回None为止,收集所有有效键。 - 长期使用建议升级Python到3.7及以上版本,或者换用sqlite、json等更稳定的存储方案替代shelve,避免底层dbm的实现缺陷。
内容的提问来源于stack exchange,提问作者Artur Siedlecki
相关产品推荐
相关产品推荐

