微信小程序云开发环境采用按量付费时,成本主要由云函数资源使用量、数据库存储与读写次数、云存储容量和下行流量构成。很多项目上线初期费用很低,但运行几个月后,账单里总会多出一些说不清楚的开销。其实很大一部分来自已经不再使用、却没有被释放的资源。清理这些资源不需要改动业务代码,也不用迁移架构,只要把云函数、数据库索引和存储文件这三类对象理清楚,通常就能看到明显变化。

先盘点云函数:删除没有调用量的函数和触发器
云函数的成本主要由调用次数、运行时间和内存规格决定。一个函数即使不再对外提供服务,只要绑定了定时触发器,或者仍被某些隐藏入口调用,就会持续产生费用。因此清理云函数的第一件事不是直接删除,而是确认函数的真实调用情况。打开云开发控制台的云函数列表,可以查看每个函数近期的调用次数、错误率和平均耗时。重点排查调用次数为零、更新时间很久且代码库中已无引用的函数。
如果函数数量较多,逐个点击效率太低。可以使用 CloudBase CLI 的 fn list 命令先把当前环境下的函数名称和状态导出来,再结合代码仓库里的调用位置做交叉比对。下面这条命令可以列出指定环境的所有函数:
# 列出当前环境全部云函数 tcb fn list -e your-env-id
拿到函数列表后,先确认哪些函数在代码中已经没有任何调用入口,再检查定时触发器是否还在运行。定时触发器在云函数目录下的 config.json 中声明,也可以在控制台查看。如果一个函数只是偶尔被客户端直接调用,但入口已经下线,或者相关页面已经改版,就应该纳入删除清单。删除函数可以使用控制台,也可以执行 CLI 命令:
# 删除已确认无用的云函数 tcb fn delete old-pay-notify -e your-env-id
删除前建议先备份函数代码。对于暂时无法确认是否还会使用的函数,可以保留代码但关闭定时触发器,观察一到两个完整业务周期。若期间没有任何异常告警或用户反馈,再执行删除。这样可以在成本优化和业务稳定性之间取得平衡。
清理数据库索引:冗余索引不只是占空间
数据库索引的维护成本经常被低估。云开发数据库基于文档模型,集合上每多一个索引,写入文档时就要同步更新索引结构。无效索引不仅占用存储空间,还会拖慢写入性能,间接增加数据库操作耗时。要判断索引是否冗余,先要梳理当前业务的所有查询条件。比如订单列表通常按照用户 ID 和订单状态筛选,那么 userId + status 的组合索引是核心索引。
很多项目在开发阶段为了调试方便,会给 userId 单独建一个索引,再给 userId + status 建一个组合索引。前者在组合索引存在时就是冗余的,因为组合索引的前缀已经能高效服务只按 userId 查询的场景。保留冗余索引会让每次写入多维护一棵索引树,不如尽早删除。控制台中进入对应集合的索引管理页,可以看到每个索引涉及的字段和创建时间,建议先截图或记录索引定义,再删除冗余项。
优化索引后,可以在云函数中跑一段查询来验证业务请求是否仍然正常。下面这个例子模拟按 userId 和 status 查询订单,命中组合索引后,读取数据的速度和稳定性通常都会更好:
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event) => {
const { userId, status } = event
const res = await db.collection('orders')
.where({ userId, status })
.limit(20)
.get()
return res.data
}
清理索引时还要注意唯一索引和系统默认的 _id 索引不能删除。对于组合索引已经覆盖的前缀索引,可以根据写入量和查询量决定是否保留。如果写入量远大于查询量,删除冗余索引的收益会更加明显。
回收云存储空间:把过期文件纳入自动清理流程
云存储中的图片、音视频、压缩包等文件,如果没有生命周期管理,就会一直累积。小程序用户上传的头像、评论图片、聊天附件,很多只在短期内有用,一旦过期就不再被读取,但仍会按月计费。解决这个问题的前提是,上传文件时要把文件信息记录到数据库,包括 fileID、存储路径、上传时间和业务类型。否则后续想要批量清理时,根本不知道哪些文件已经过期。
常见的做法是在上传成功的回调里,向一个专门的 file_records 集合写入一条记录。清理任务运行在云函数定时触发器中,每天或每周扫描超过保留期限的记录,再调用 cloud.deleteFile 批量删除文件。下面是一个清理超过 30 天未删除文件的云函数示例:
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async () => {
const deadline = Date.now() - 30 * 24 * 60 * 60 * 1000
const _ = db.command
const expiredFiles = await db.collection('file_records')
.where({
createdAt: _.lt(deadline),
deleted: false
})
.limit(100)
.get()
const fileList = expiredFiles.data.map(item => item.fileID)
if (fileList.length > 0) {
await cloud.deleteFile({ fileList })
await db.collection('file_records')
.where({ fileID: _.in(fileList) })
.update({ data: { deleted: true } })
}
return { cleaned: fileList.length }
}
为了让这段逻辑自动执行,需要在云函数目录下配置定时触发器。下面是一个每天凌晨三点执行清理任务的 config.json 示例:
{
"triggers": [
{
"name": "daily-clean-expired-files",
"type": "timer",
"config": "0 0 3 * * * *"
}
]
}
对于历史遗留文件,如果从未在数据库中记录过,可以先做一次人工盘点。云开发控制台的存储管理支持按目录查看文件大小和修改时间,可以按时间范围筛选出长期未访问的大文件。确认无业务关联后,使用控制台批量删除,或导出文件列表后用云函数调用 cloud.deleteFile。删除前建议先备份关键文件,尤其是用户生成的原创内容。
建立月度巡检机制,防止无用资源再次堆积
一次性的清理可以快速降低成本,但如果不形成固定机制,几个月后可能又会出现新的无用云函数、冗余索引和过期文件。建议每月固定做一次云开发环境巡检,按照下面的表格记录检查结果,并跟进处理状态。
| 检查对象 | 检查指标 | 处理动作 |
|---|---|---|
| 云函数 | 最近 30 天调用次数、触发器状态 | 确认无用后关闭触发器或删除函数 |
| 数据库索引 | 索引数量、查询模型是否变化 | 删除组合索引已覆盖的前缀索引 |
| 云存储文件 | 文件数量、目录占用、过期记录 | 执行批量清理并更新数据库标记 |
除了人工巡检,还可以把资源变化情况接入监控。云开发控制台提供资源使用量统计,如果某类资源的费用或存储量连续增长,可以配合日志和业务迭代记录判断是否属于正常增长。对于已经确认无用的资源,尽量在下一次发版前完成清理,避免影响新功能上线。
成本优化的核心不是一味地降低配置,而是让每一份资源都在为业务服务。把云函数、数据库索引和存储文件这三类最容易堆积的资源管好,云开发环境通常能够回落到一个比较健康的使用状态。相比直接升级资源包或重构后端,这种清理方式风险更小、见效更快,也更适合中小团队长期维护。