如何在Firestore集合中为每个文档执行事务并避免重复读取
解决Firestore异步事务重复读取的问题
你遇到的重复读取问题,根源在于stream()方法会直接拉取文档内容生成DocumentSnapshot,而事务内的get()又会再读一次文档。可以通过两种方式解决:
方式一:如果更新不依赖文档当前值,直接移除事务内的get()
如果你的更新逻辑不需要用到文档的现有数据(比如直接把a设为3),Firestore允许在事务内直接执行update,不需要先get。修改后的代码如下:
async def example(): @google.cloud.firestore.async_transactional async def update_in_transaction(transaction: AsyncTransaction, city_ref, data): # 移除多余的get(),直接执行update transaction.update(city_ref, data) db: google.cloud.firestore.AsyncClient = AsyncClient() await db.collection("1").document("1").set({"a": 1}) await db.collection("1").document("2").set({"a": 2}) docs = db.collection("1").stream() async for doc in docs: transaction = db.transaction() # 传入doc.reference而不是doc本身,逻辑更清晰 await update_in_transaction(transaction, doc.reference, {"a": 3}) asyncio.run(example())
注意:单个文档的更新本身就是原子操作,如果只是单文档的简单更新,其实不需要用事务——事务更多用于跨文档的原子操作,或者需要基于文档当前值做计算的场景。
方式二:只获取文档引用,避免预读取内容
如果必须保留事务内的get()(比如需要验证文档存在、或基于当前值更新),可以用list_documents()代替stream(),这个方法只会返回文档的引用(DocumentReference),不会拉取文档内容,这样就只有事务内的一次读取:
async def example(): @google.cloud.firestore.async_transactional async def update_in_transaction(transaction: AsyncTransaction, city_ref, data): doc_snap = await transaction.get(city_ref) if not doc_snap.exists: # 可在此添加文档存在性校验逻辑 raise ValueError("文档不存在") transaction.update(city_ref, data) db: google.cloud.firestore.AsyncClient = AsyncClient() await db.collection("1").document("1").set({"a": 1}) await db.collection("1").document("2").set({"a": 2}) # 使用list_documents()获取文档引用,不预读内容 doc_refs = db.collection("1").list_documents() async for doc_ref in doc_refs: transaction = db.transaction() await update_in_transaction(transaction, doc_ref, {"a": 3}) asyncio.run(example())
这种方式完全避免了预读取,所有读取都在事务内完成,符合事务的原子性要求。
内容的提问来源于stack exchange,提问作者Andrey
相关产品推荐
相关产品推荐

