unicloud 不支持传统数据库事务,add() 无原子性:校验失败整批回滚,但超时或中断会导致部分写入;需用分批校验、顺序写入、补偿机制模拟事务,并对大批量数据分片(如每批200条)避免超时。

uniCloud 不支持传统意义上的数据库事务(如 begin/commit/rollback),所谓“复杂事务批量插入”必须拆解为可验证、可回滚的业务逻辑,而不是依赖底层 ACID 保证。
uniCloud 的 add() 方法本身不提供原子性事务
你调用 db.collection('users').add([{...}, {...}]) 时,UniCloud 会尽力一次性写入这批文档,但:
- 如果中途某条数据校验失败(比如 Schema 规则不通过、唯一索引冲突),整批会失败,返回错误;
- 如果写入过程中发生服务端超时或网络中断,部分数据可能已落库、部分未落库——add() 不会自动回滚已成功写入的文档;
- 没有 transaction API,也无法像 MySQL 那样手动开启事务。
所以别指望靠 add() 实现“全成功或全失败”的强一致性。真正需要事务语义的场景(比如扣库存 + 生成订单 + 记日志),得自己模拟。
用分批 + 校验 + 补偿机制替代事务
当你要插入一批关联数据(例如:创建一个订单,同时插入订单主表、订单项、用户积分变动记录),不能直接把三类数据塞进一个 add() 调用里——它们属于不同集合,且失败影响面不同。
- 先对全部待插入数据做前端/云函数内预校验(如检查库存是否充足、用户余额是否足够),失败直接返回,不发任何写请求;
- 按依赖顺序分步写入:先写主记录(如
orders),拿到生成的_id后,再写子记录(如order_items,字段中带上order_id); - 每一步都单独 await,并捕获错误;一旦某步失败,立即执行补偿操作(如已写的
orders记录,用remove()删除,或打上status: "failed"标记供后续清理); - 避免在单次云函数中串行写太多集合——超过 3~4 步就考虑拆成多个云函数,用 client 端协调状态。
大批量插入(>1000 条)必须分片,否则必然超时
UniCloud 云函数默认超时时间是 10 秒(阿里云)或 60 秒(腾讯云),但实际能稳定跑满的时间更短。一次 add() 传入 5000 条数据,极大概率触发超时或内存溢出。
正确做法是:
- 客户端或云函数内把原始数组按 batchSize = 100 ~ 500 切片;
- 用 for...of 或 Promise.allSettled() 控制并发(别用 Promise.all(),一个失败全崩);
- 每批插入后检查 result.id 或 result.affectedDocs,确认数量匹配;
- 插入失败的批次要记录 failedBatchIndex 和原始数据,便于重试或人工介入。
示例关键片段:
const batchSize = 200; for (let i = 0; i <h3>客户端直接调用 <code>add()</code> 的权限和性能陷阱</h3><p>如果你让前端页面直接调用 <code>uniCloud.databaseForJQL().collection(...).add(...)</code>: - 必须在数据库规则中显式允许该集合的 <code>add</code> 操作,且规则要能鉴权(比如 <code>auth.uid == doc.creator_id</code>),否则任何人都能往里灌数据; - 大批量插入会卡死 H5 页面或小程序 UI 线程,尤其在低端安卓机上; - JQL 的 <code>add()</code> 无分片能力,一次最多传约 2MB 数据(实际受 JSON 序列化和 HTTP 限制,通常 500 条以内较稳)。</p><p>结论:涉及复杂逻辑或大批量数据,一律走云函数,别放前端。</p><p>真正麻烦的不是怎么写 insert 语句,而是想清楚哪部分失败能接受、哪部分必须兜底、谁来负责清理中间态——这些才是“复杂事务”在 uniCloud 里的实质成本。</p>











