如何突破WriteBatch单次500次写入限制批量导入27000条Firestore文档
批量写入Firestore的调整实现方案
核心调整思路
你原来的单条循环写入相当于发起27000次独立网络请求,不仅网络开销极高,还容易触发Firestore的单客户端请求频控,速度慢且失败率高。结合WriteBatch单批次最多500个写操作的硬限制,调整逻辑非常直接:
- 在遍历数据的过程中动态攒写操作,每攒够500个就立刻提交当前批次
- 提交完成后重置计数,创建新的空批次接着处理剩下的数据
- 遍历结束后检查剩余不足500条的内容,单独提交收尾批次
整个流程下来27000条数据仅需拆分为54个批次提交,请求量较原方案下降98%,写入效率和可靠性都会有量级提升。
具体可落地的代码实现
// Firestore官方硬限制:单批次最多支持500个写操作,不要随意调高 private static final int MAX_BATCH_OPERATION_LIMIT = 500; private void addData2DB(ArrayList<String> titles, ArrayList<String> authors, ArrayList<String> publishers, ArrayList<String> genres, ArrayList<String> pages) { // 前置校验:确保所有数据列长度一致,避免遍历过程中数组越界 int totalDataCount = titles.size(); if (authors.size() != totalDataCount || publishers.size() != totalDataCount || genres.size() != totalDataCount || pages.size() != totalDataCount) { throw new IllegalArgumentException("读取的Excel各列数据长度不一致,请检查解析逻辑"); } // 初始化第一个批次和计数器 WriteBatch currentBatch = db.batch(); int currentBatchOperationCount = 0; CollectionReference userCollection = db.collection("users"); for (int i = 0; i < totalDataCount; i++) { // 构建单条文档数据结构 Map<String, Object> docData = new HashMap<>(); docData.put("title", titles.get(i)); docData.put("author", authors.get(i)); docData.put("publisher", publishers.get(i)); docData.put("genre", genres.get(i)); // 建议将pages字段转为数值类型存储,方便后续做范围查询、排序 docData.put("pages", pages.get(i)); // 生成随机ID的文档引用,和原方案中collection.add()的写入行为完全一致,不会覆盖已有文档 DocumentReference targetDocRef = userCollection.document(); currentBatch.set(targetDocRef, docData); currentBatchOperationCount++; // 攒够500个操作就提交当前批次 if (currentBatchOperationCount >= MAX_BATCH_OPERATION_LIMIT) { currentBatch.commit() .addOnSuccessListener(unused -> { // 可在此处打印写入进度,方便追踪任务执行情况 }) .addOnFailureListener(e -> { // 必须保留失败监听,避免异常被静默吞掉 Log.e("FirestoreBatchUpload", "单批次写入失败", e); }); // 重置批次和计数,处理下一批数据 currentBatch = db.batch(); currentBatchOperationCount = 0; } } // 遍历完成后,提交最后剩余的不足500条的收尾批次 if (currentBatchOperationCount > 0) { currentBatch.commit() .addOnSuccessListener(unused -> { Log.d("FirestoreBatchUpload", "全部数据写入完成"); }) .addOnFailureListener(e -> { Log.e("FirestoreBatchUpload", "收尾批次写入失败", e); }); } }
实际运行的注意事项
- 不需要严格串行等待上一个批次提交完成再处理下一批,Firestore客户端会自动做请求调度,54个批次的并发量远低于单客户端的请求频率限制,不会触发限流
- 同个批次内的写操作是原子性的,要么全部写入成功,要么全部失败,相比单条写入的碎片状态,数据一致性更好
- 如果遇到极个别批次提交失败,可以在失败回调中增加简单的重试逻辑,因为请求量本身很小,重试的成本极低
- 27000条字符串数据的内存占用仅十几MB,完全不需要担心加载到ArrayList中会触发OOM
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

