云数据库使用模型

云数据库本质是托管在云端的文档型数据库,集合里存文档,每个文档就是一条 JSON 记录。客户端 SDK 和云函数都能读写,但权限模型不一样,这点要先分清。

客户端直接读写适合数据量小、规则简单的场景,安全规则写在数据库权限里;云函数读写适合要校验、要聚合、要跨集合处理的场景,绕过客户端权限限制,逻辑更可控。

很多团队把读写都收到云函数里,客户端只调接口。代价是多一层开发量,换来的是权限、日志、限流都统一在一处。小型项目可以先客户端直连,业务复杂了再收口。

本文核心知识点
  • 批量批量写入与大数据导入。
  • 事务多文档一致性与回滚。
  • 聚合分组统计与复杂查询。
  • 实时watch 监听数据变化。
小程序云数据库深入实战配图:蓝调渐变背景上数据库圆柱体连接批量导入、事务锁与实时推送节点的扁平科技插画
批量、事务、实时,数据层的三件硬功夫。

批量写入与导入

一次性要写入成百上千条数据,一条条 add 会又慢又费配额。云数据库提供批量写入接口,一次调用能写多条,适合初始化数据、迁移数据这类场景。

const cloud = require('wx-server-sdk');
cloud.init();

exports.main = async function (event) {
  const db = cloud.database();
  const rows = event.rows || [];
  const batch = [];
  for (let i = 0; i < rows.length; i += 100) {
    batch.push(db.collection('orders').add({
      data: rows.slice(i, i + 100)
    }));
  }
  const results = await Promise.all(batch);
  return { inserted: results.length };
};

批量写入有单次数量上限,超过要分批。分批的时候记得处理部分失败:哪批失败了、成功了几批,都写进日志,方便重试,别整批静默丢弃。

导入还要考虑幂等:给数据带上唯一键,写入前先查重,重复导入才不会把库存、订单这类数据弄花。

事务与数据一致性

扣库存、转账、改订单状态,这类操作要求多文档一起成功或一起失败,云函数里可以用事务接口保证一致性。

const db = cloud.database();
const transaction = await db.startTransaction();
try {
  await transaction.collection('stock').doc('sku-001').update({
    data: { count: db.command.inc(-1) }
  });
  await transaction.collection('orders').add({
    data: { sku: 'sku-001', amount: 99 }
  });
  await transaction.commit();
} catch (err) {
  await transaction.rollback();
  throw err;
}

事务有超时和重试的限制,事务里不要做耗时的外部请求,比如调第三方支付、等用户输入。能放事务里的操作就尽量精简,事务外做准备工作,事务内只做关键更新。

并发扣减要注意:库存这种高频字段,用 db.command.inc 原子自增比读出来再写回去安全得多,事务里也一样。先想清楚并发模型,再决定要不要上事务,别所有接口都套一层。

聚合与复杂查询

统计订单总额、按分类分组、算用户留存,这些不能靠客户端拉全量数据再自己算,要用聚合管道在服务端算好,只把结果返回给客户端。

const result = await db.collection('orders').aggregate()
  .match({ status: 'paid' })
  .group({
    _id: '$category',
    total: db.command.aggregate.sum('$amount'),
    count: db.command.aggregate.count()
  })
  .sort({ total: -1 })
  .end();

聚合操作放在云函数里跑,客户端直连的聚合权限和性能都不好控制。聚合结果如果变化不频繁,缓存到另一个集合,查询接口直接读缓存,能省大量配额和延迟。

复杂查询的另一个关键是索引。查询字段没建索引,数据一多就慢,控制台会提示慢查询。按查询模式把组合索引建好,查询速度能差出数量级。

实时数据推送

聊天消息、协作文档、订单状态变更,这类要实时刷新的数据,用 watch 监听集合或文档的变化,变化推给前端,比轮询省流量也更及时。

const db = wx.cloud.database();
const watcher = db.collection('messages')
  .where({ to: openid })
  .watch({
    onChange: function (snapshot) {
      // snapshot.docs 是最新数据,snapshot.docChanges 是变更明细
      renderMessages(snapshot.docs);
    },
    onError: function (err) {
      console.log('watch error', err);
    }
  });

// 页面卸载时关闭监听
function onUnload() {
  watcher.close();
}

watch 有监听数量上限,也要记得在页面卸载时 close,不然会一直占用连接。实时数据要在服务端配合权限规则,只推用户有权限看的数据,别把全表数据推给任何客户端。

实时推送不适合大数据量场景。几十条消息没问题,几万条文档全量推就不现实了,前端要做增量合并、节流渲染,否则页面直接卡死。