使用pymongo实现MongoDB集合id字段自增逻辑异常如何解决
问题根因
你的逻辑存在两处核心错误:
- 错误修改了原有最大ID对应文档的
id字段,你只需要读取最大ID值做计算,不需要改动旧文档数据 find_one_and_update默认返回更新前的文档内容,所以你拿到的last_pokemon['id']是自增前的旧值,旧文档的ID反而被改为了自增后的值,和预期完全相反
快速修正方案(低并发场景)
如果你的插入操作是单线程/低并发,不需要考虑重复ID问题,直接读取最大ID加1后插入新文档即可,不需要修改旧文档:
# 查询当前最大id,集合为空时默认从1开始 max_id_doc = pokemons.find_one({}, sort=[('id', -1)], projection={'id': 1}) new_id = max_id_doc['id'] + 1 if max_id_doc else 1 new_pokemon = { "name" : name, "avg_spawns" : avg_spawns, "candy" : candy, "img" : img_link, "weaknesses" : [], "type" : [], "candy_count" : candy_count, "egg" : egg, "height" : height, "multipliers" : [], "next_evolution" : [], "prev_evolution" : [], "spawn_chance" : spawn_chance, "spawn_time" : spawn_time, "weight" : weight, "id" : new_id, "num" : new_id, } pokemons.insert_one(new_pokemon)
生产级方案(高并发无重复)
MongoDB官方推荐使用独立的计数器集合实现原子自增ID,单文档操作自带原子性,完全避免并发下的ID重复问题:
第一步:初始化计数器(仅执行一次)
# 计算现有集合的最大id作为计数器初始值,空集合初始值为0 max_id_doc = pokemons.find_one({}, sort=[('id', -1)], projection={'id': 1}) init_val = max_id_doc['id'] if max_id_doc else 0 # 创建计数器集合,存储pokemon的id自增序列 db.counters.insert_one({'_id': 'pokemon_id', 'sequence_value': init_val})
第二步:插入新文档时获取原子自增ID
# 原子更新计数器,返回更新后的数值 counter = db.counters.find_one_and_update( {'_id': 'pokemon_id'}, {'$inc': {'sequence_value': 1}}, return_document=True ) new_id = counter['sequence_value'] new_pokemon = { "name" : name, "avg_spawns" : avg_spawns, "candy" : candy, "img" : img_link, "weaknesses" : [], "type" : [], "candy_count" : candy_count, "egg" : egg, "height" : height, "multipliers" : [], "next_evolution" : [], "prev_evolution" : [], "spawn_chance" : spawn_chance, "spawn_time" : spawn_time, "weight" : weight, "id" : new_id, "num" : new_id, } pokemons.insert_one(new_pokemon)
内容的提问来源于stack exchange,提问作者Darren_D19
相关产品推荐
相关产品推荐

