简介:微信小程序开发者若想理清云数据库与云存储的配合用法,这份PDF教程值得参考。它围绕实际开发中前端界面数据的存取需求,从打开云开发、新建集合入手,以videos集合为例,逐步演示如何将云存储中视频数据的tempFileURL下载地址复制到数据库字段,完成数据关联与入库,整个过程无需复杂配置。内容同时讲解云数据库在用户数据、应用配置、商品详情等场景中的应用,提及增删改查、过滤排序、分页以及实时数据同步等能力,帮助读者理解其在大数据量下的扩展性与安全性,为后续项目实现动态数据展示打下基础。资源为单份PDF文档,体积仅56KB,篇幅精炼但操作步骤完整,目前已有3171人学习下载,适合初学小程序云开发或需要快速复习数据库用法的开发者。
1. 先想清楚:小程序写业务,云数据库替你把后端省掉,这套方案到底解决了什么
微信小程序云开发之使用云数据库,这句话对刚入门的开发者来说,重点往往不在“数据库”,而在“云开发”这三个字。简单说,云开发把服务器、鉴权、存储、数据库一起打包成了“环境”,你不需要自己买域名、配 HTTPS、维护接口服务,在小程序里直接拿官方 SDK 操作数据库。云数据库则是一个 JSON 文档型数据库,每条记录就是一个对象,集合里的数据结构和前端 JavaScript 对象几乎一一对应,学习成本比传统 MySQL 低一大截。
我见过太多小程序项目死在半路上:后端没人写、接口联调拖了两周、数据库表结构改一次崩一次。云数据库的价值恰恰是让你在需求还没完全清晰的时候就能先把前端页面跑起来,后续加字段、改数据结构都像改对象一样随意。它适合个人开发者做工具类小程序、小团队做原型验证、以及那些不想把精力耗在运维上的项目。当然它也有限制,比如并发上限、事务能力、权限模型,这些后面会一条条讲到。搞清楚这几点,你才知道它到底是不是你的菜。
2. 先立住模型:集合、记录、权限,这三件事搞不明白,后面全是玄学
2.1 集合和记录是文档型,不是你熟悉的表结构
云数据库的基础单位是集合,集合里是一条条 JSON 记录。很多从 MySQL 过来的同学第一反应是“集合就是表,记录就是行”,这个类比能帮你快速上手,但千万别把它当成完整的对应关系。传统表结构要先定字段、定类型,之后加个字段得改表;云数据库的记录完全可以各不相同,A 记录里有nickName字段,B 记录里没有,这条插入照样成功。
这样设计有好处,比如你要做一个用户表,早期只存openid和createTime,后来要加phone、avatar,直接往记录里塞字段就行,不用跑迁移脚本。坏处也很明显,字段拼写错了不会报错,查询结果不符合预期时才追悔莫及。我一般会在代码里维护一份字段命名规范文档,或者在集合里放一条“样本记录”当模板。别嫌麻烦,等到线上数据攒了几万条再返工,重构成本远比你想象的高。
集合名称建议用有意义的单数或复数名词,比如user、order、goods,不要用list1、test2这类名字。云数据库的集合名和记录里的_id一旦创建就不能改,改名的唯一方式是新建集合再迁移数据,这个坑后文会专门说。
2.2 权限设置是安全底线,不是功能开关
权限是云数据库最容易被忽略的地方。你创建集合的时候,微信开发者工具会让你选权限:仅创建者可读写、所有用户可读仅创建者可写、所有用户可读、所有用户不可读写。很多教程为了演示方便,直接选“所有用户可读”,结果数据完全暴露在公网,任何人都能通过小程序反编译或者直接调 HTTP API 把数据拖走。
常见的做法是:基础数据(如商品列表、文章内容)用“所有用户可读,仅创建者可写”,用户私有数据(如个人资料、订单记录)用“仅创建者可读写”。云数据库权限判断依据是记录里的_openid字段,这条字段在从小程序端插入数据时自动带上,不需要你在代码里手动赋值。这也意味着,如果你在云函数里插入数据,_openid不会自动生成,需要你显式写入。这个细节后面还会遇到。
提示:权限设置只在小程序端生效,云函数端拥有完全的管理员权限,可以读写所有记录。所以千万别把敏感逻辑全押在小程序端权限上,真正的校验要放在云函数里做。
2.3 小程序端直接读写 vs 云函数端操作:两条路子怎么选
云数据库有两条操作路径。第一条是小程序端直接用wx.cloud.database()获取引用,适合简单查询和当前用户自己的数据增删改。第二条是在云函数里用wx-server-sdk的cloud.database()拿引用,适合批量操作、复杂查询、跨用户数据操作,或者需要保证数据正确性的业务逻辑。
这两条路径能做的事差别很大。小程序端默认单次查询最多返回 20 条,skip值默认最大 10000,条件查询里or操作符的限制也很多;云函数端虽然没有这些限制,但要付函数调用时长和数据库操作次数的资源消耗。实际项目里,我的习惯是“读走小程序端,写走云函数”,查询尽可能在端上做,减轻服务器压力;写入操作哪怕简单如更新用户昵称,也包一层云函数,方便以后加校验、加日志、加风控逻辑。
3. 用小程序端 SDK 跑通最小读写:从初始化到增删改查
3.1 初始化环境与第一个集合
新建一个云开发小程序时,开发者工具会让你创建环境,环境 ID 是一串类似cloud1-xxxxxx的字符串。初始化必须在app.js的onLaunch里完成,否则后续所有数据库操作都会报Cloud API isn't enabled之类的错误。
// app.js App({ onLaunch() { if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else { wx.cloud.init({ // 环境 ID 在云开发控制台可见 env: 'cloud1-xxxxxx', // 默认为 true,表示用户身份由微信自动识别 traceUser: true }) } } })初始化里的env参数指向你的云环境,一个账号下可以创建多个环境,比如开发环境、测试环境、生产环境。traceUser建议保持默认的true,这样数据库会自动记录每条记录的创建者_openid,你才能用权限里的“仅创建者可读写”。初始化完成后,在云开发控制台手动创建一个集合,比如叫todo,就可以开始写业务了。
提示:我在本地调试时习惯把
env写成动态获取,比如从config文件里读取,避免测试数据和线上数据混在一起。开发者工具里的云环境切换按钮只影响工具界面,代码里是哪个环境就是哪个环境。
3.2 增删改查四条核心命令
先跑通最基础的插入和查询,代码如下:
const db = wx.cloud.database() const todos = db.collection('todo') // 插入一条待办事项 todos.add({ data: { title: '学习云数据库', done: false, createTime: Date.now() } }).then(res => { console.log('新增成功,记录 ID:', res._id) }).catch(err => { console.error('新增失败:', err) }) // 查询当前用户的所有待办事项 todos.where({ _openid: '{openid}' // 这个占位符会被自动替换为当前用户 }).get().then(res => { console.log('查询结果:', res.data) })add方法返回的res._id是自动生成的记录主键,后续的更新和删除都要用到它。where条件里的'{openid}'是微信官方的特殊占位符,不需要你自己去wx.getUserInfo拿 openid,云数据库会自动把当前用户替换进去。查询结果默认按创建时间倒序排列,这个顺序在大多数场景下都够用。
更新和删除的代码如下,注意doc方法需要传入记录 ID:
// 更新一条记录 todos.doc('记录ID字符串').update({ data: { done: true } }).then(res => { console.log('更新条数:', res.stats.updated) }) // 删除一条记录 todos.doc('记录ID字符串').remove().then(res => { console.log('删除条数:', res.stats.removed) })这里有个容易踩的细节:update只会更新你传入的字段,不会影响其他字段。如果你想把整个记录覆盖掉,要用set方法。doc操作默认带有权限检查,如果当前用户不是这条记录的创建者,操作会直接失败,这是云数据库帮你做的一道基础防线。
3.3 where 查询与模糊搜索:能用正则,但别大意
日常业务里最常用的是条件查询,云数据库的 where 支持等于、不等于、大于、小于、in、exists等操作符。模糊搜索用正则表达式,但这里有一个性能隐患。
// 精确匹配 todos.where({ done: false }).get() // 范围查询 todos.where({ createTime: db.command.gt(Date.now() - 7 * 24 * 3600 * 1000) }).get() // 正则模糊查询 todos.where({ title: db.RegExp({ regexp: '云开发', options: 'i' }) }).get()db.command.gt是云数据库的查询指令,类似的还有lt、gte、lte、neq、in。正则查询看着方便,但它没有走索引能力,数据量超过一万条时响应会明显变慢。如果业务确实需要模糊搜索,并且集合里数据量很大,我的建议是换一种方案:单独维护一个专门做搜索的字段,存关键词数组,用in操作符匹配,性能能快一个数量级。
4. 用云函数做批量操作和复杂查询:绕开小程序端三个硬限制
4.1 为什么云函数里写数据库操作更稳
小程序端的数据库操作有一些硬限制,最让人头疼的三个是:单次get最多返回 20 条、or操作符使用条件苛刻、数据操作次数按小程序端调用算消耗。当你需要把几十条记录一次读出来,或者按多个条件组合筛选时,小程序端就不够用了。
云函数里使用的是wx-server-sdk,它运行在服务端环境,没有 20 条限制,skip、limit、orderBy、or的规则也宽松很多。另一个重要的点:云函数里操作数据库不经过权限判断,你需要自己控制哪些数据能读、能写。这既是自由,也是责任。我在云函数里一定会先做一次入参校验,再执行数据库操作,防止有人直接调用云函数接口绕过前端做坏事。
4.2 批量写入与更新:单条循环 vs 批量接口
批量写入最容易犯的错是用for循环一条条add。小程序端这样做并发一多就超限,云函数里这样做浪费调用时长。云数据库没有像 MySQL 那种insert into ... values (...), (...)的写法,批量操作的正确姿势是使用db.collection('xxx).add()配合 Promise.all,或者用云函数批量插入:
const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() exports.main = async (event) => { const list = event.list || [] if (list.length === 0) { return { success: false, message: '没有数据' } } // 拆成每批 100 条,避免单次请求体过大 const batch = [] for (let i = 0; i < list.length; i += 100) { batch.push(list.slice(i, i + 100)) } let inserted = 0 for (const group of batch) { // 批量 add,Promise.all 并发执行 const tasks = group.map(item => db.collection('todo').add({ data: item })) const results = await Promise.all(tasks) inserted += results.length } return { success: true, inserted } }这里没有用官方提供的batchAdd,因为实际项目里云函数批量写入的瓶颈通常不在数据库而在网络和内存。分批 100 条是为了避免一次传入上百条记录导致请求体超过限制。Promise.all并发执行时注意别把整个批次几千条一次性并发,否则数据库写入连接会被打满,返回write limit exceeded。如果你要插入的数据有依赖关系,比如订单头要和订单明细一起写,那就得改串行,或者用事务。
4.3 聚合查询:连表、分组、count 怎么用
云数据库的聚合操作和 MongoDB 的聚合框架几乎一样,用aggregate方法。我最常用的三个场景是:统计数量、分组求和、连表查询。
const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() exports.main = async (event) => { const _ = db.command.aggregate // 统计未完成待办数量 const countRes = await db.collection('todo') .where({ done: false }) .count() // 按创建日期分组统计 const groupRes = await db.collection('todo') .aggregate() .group({ _id: _.dateToString({ date: '$createTime', format: '%Y-%m-%d' }), total: _.sum(1) }) .limit(10) .end() return { count: countRes.total, group: groupRes.list } }count方法返回的是total字段,不是data,很多人第一次用会取错。聚合操作里的$createTime指向字段名,dateToString能把时间戳转成指定格式的字符串。连表查询用lookup,把当前集合的某字段和目标集合的_id关联起来。注意聚合操作不能在where里用_openid: '{openid}'占位符,云函数里拿不到当前用户身份,你需要显式传入 openid 或自己解析cloud.getWXContext()。
5. 云数据库避坑与排查:五个高频翻车现场
5.1 报错 collection not exists,但集合明明创建了
现象:第一次请求数据库,控制台报collection not exists,去云开发控制台一看,集合确实在那里。
原因:云开发的集合创建后,索引和路由信息需要一点时间生效。另一个常见原因是环境搞错了,代码初始化的是 A 环境,集合建在 B 环境,自然找不到。
解决:先确认env参数指向的环境 ID 和控制台一致;如果环境没问题,等几秒重试一次,或者直接在初始化时调用一次db.createCollection兜底。注意createCollection只在小程序端或云函数端可用,而且重复创建同名集合会报错,建议只在特殊场景下用它。
5.2 开发工具里能查到记录,真机上查不到
现象:数据明明插入成功了,开发者工具里也能查到,但手机预览时页面是空的。
原因:大概率是权限问题。开发者工具默认跳过部分权限校验,但真机不行。插入数据时记录的_openid是开发者的微信号 openid,真机上当前用户是测试微信号的 openid,两者不一致,权限一过滤,数据就“消失”了。
解决:用'{openid}'占位符而不是硬编码用户身份;如果是自己测试,把集合权限临时调到“所有用户可读”,确认数据能查到后再收回去。线上环境千万别用“所有用户可读”放用户私有数据。
5.3 循环里逐条 update,请求超时或报错 setInterval
现象:批量更新 1000 条记录,代码写在for循环里逐条update,跑到一半报超时。
原因:小程序端逐条调用数据库 API,每条请求都要经过微信网关,批量请求数一多就触发频率限制。云函数里逐条串行更新虽然不会触发频率限制,但执行时间被放大,白白扣资源。
解决:能用where加update批量更新的,别用doc逐条更新。比如把所有done=false的记录改成done=true,一条命令搞定。如果更新内容每条不一样,把数据整理好后在云函数里用Promise.all并发更新,单次并发数控制在 20 以内。
5.4 慢查询:没有索引 + or 操作符翻车
现象:集合几千条数据,查询响应要两三秒,索引优化后毫秒级返回。
原因:where条件里的字段没有建立索引,或者用了or组合条件,数据库没法高效定位记录。云数据库控制台里可以看到慢查询日志,响应时间超过 500ms 的操作会被记录。
解决:在云开发控制台的数据库页面,打开集合的“索引管理”,给查询里高频出现的字段建索引。比如按done过滤、按createTime排序,就建一个done + createTime的复合索引。or查询尽量改成db.command.in,后者能走索引。
提示:建立索引要支付额外的存储和写入成本,字段很多的时候别盲目全建。按实际查询场景来,先看日志再建索引。
5.5 并发写入覆盖:update 不是事务,注意这个陷阱
现象:两个用户同时给同一个待办事项点赞,结果点赞数只加了 1,而不是 2。
原因:云数据库的单条update操作不具备原子性,A 请求读到数量是 10,B 请求也读到 10,A 更新成 11,B 也更新成 11,最终变成 11。
解决:云数据库没有提供inc这种原子自增操作符,它是_.inc,在db.command里。对这个需求,正确的写法是todo.doc(id).update({ data: { likes: _.inc(1) } }),数据库会在服务器端自动完成自增,不会出现读改写导致的丢失更新。凡是对数值做增减的场景,一律用inc,不要“读出旧值、算好新值、写回”。如果业务逻辑更复杂,需要保证多步骤的一致性,云开发目前支持事务,但只支持单文档事务,跨集合事务得靠云函数加锁自行处理,这是云数据库的一个硬边界。
6. 收尾的进阶用法:把数据库操作封装成通用模块,省掉重复代码
开发几个小程序之后,你会发现所有页面的数据库逻辑几乎都是增删改查四件套。我习惯在项目utils目录下维护一个db.js,把基础操作统一封装,页面里只管传参数,不用关心 API 细节。
// utils/db.js const db = wx.cloud.database() // 通用查询封装 function query(collection, where = {}, { pageSize = 20, page = 1 } = {}) { return db.collection(collection) .where(where) .skip((page - 1) * pageSize) .limit(pageSize) .get() .then(res => res.data) } // 通用更新封装 function update(collection, id, data) { return db.collection(collection).doc(id).update({ data }) } module.exports = { query, update }这里把分页参数pageSize和page收敛到一处,页面代码里只需要调用query('todo', { done: false }, { page: 2 })。注意云数据库的skip最大只能到 10000,超过这个值会有问题,所以真正的深分页场景要用游标或者基于时间戳的翻页。这个封装只适合简单场景,带or、聚合、事务的操作直接写云函数,不必为了形式上的统一牺牲灵活性。
另外建议在云开发控制台给每个集合加好索引,这比写任何代码都重要。我见过性能翻车的小程序,排查到最后不是代码问题,而是索引缺失。数据库操作频繁出现慢日志的时候,按“先看慢查询日志,再建索引,最后优化查询语句”的顺序处理,不要上来就重构业务代码。归根结底云数据库只是一个工具,真正的功夫在设计集合结构和合理用索引上。落过的坑多了,你自然会养成这个习惯。希望帮到你。
本文还有配套的精品资源,点击获取