当前位置: 首页 > news >正文

Node 后端实战 · 为什么用 Cloudflare Workers + D1 扛起了整个多租户 SaaS 后端?架构决策全景复盘

Node 后端实战 · 为什么用 Cloudflare Workers + D1 扛起了整个多租户 SaaS 后端?架构决策全景复盘

各位看官,这篇聊聊我最近做的一个多租户 SaaS 后端。技术栈没有走"买服务器、装 Postgres、再配 Nginx"的传统路线,而是选了Cloudflare Workers + D1(边缘 SQLite)+ KV + R2 + Hono + Drizzle的全 serverless 边缘栈。

选型阶段我其实也犹豫过——SQLite 跑 SaaS,还是边缘的,不少人第一反应是质疑。但项目上线跑了一段时间,回过头看,当时那几个架构决策基本站得住。今天把拍板的逻辑和踩过的坑一并讲清楚,供同样想轻运维上云的同学参考。

一、为什么是 serverless 边缘栈,而不是传统服务器

最朴素的原因是:我不想长期养一台服务器。租 ECS 要一直付钱,哪怕半夜没流量;扩容得自己盯监控;数据库主从、备份、打补丁样样都要管。而这个 SaaS 的体量也就几十个租户、日活几千,犯不着为这点规模专门投入运维精力。

Cloudflare Workers 的思路正好相反:

  • 按量计费、闲置不花钱:没有请求就不产生计算费用,对小项目很友好。
  • 全球边缘、就近执行:Worker 跑在 Cloudflare 几百个边缘节点上,请求落到离用户最近的节点,延迟天然低。
  • 冷启动几乎为零:毫秒级启动,体验上和常驻服务没有明显差别。
  • 数据库跟着 Worker 走:D1 是 Cloudflare 托管的 SQLite,和 Worker 同区域部署,读写延迟极低,不需要自己维护连接池。

对中小体量、流量平稳的项目来说,"轻量、省钱、免运维"这三个诉求,一套 Workers + D1 基本都能满足。

二、多租户怎么隔离:单库 tenant_id 行级隔离

多租户 SaaS 第一个绕不开的问题是租户数据怎么隔开。我一开始也权衡过是否每个租户独立一个库,最后定了单库 + 每表tenant_id行级隔离

核心做法:每个业务表都加一个tenant_id列,所有查询在中间件层被强制注入tenant_id过滤条件,应用层无法绕开。二三十个租户这个量级,独立库没有必要,单库反而让备份、迁移、跨租户统计都简单得多。

隔离方案做法优点缺点适用场景
独立数据库每租户一个 D1 / 实例物理隔离最彻底备份/迁移/统计爆炸,运维重大客户、强合规
Schema 隔离同库不同 schema中等隔离SQLite 不支持 schema,劝退Postgres 多租户
单库 tenant_id 行级每表加 tenant_id,查询强制过滤轻量、易备份、易统计靠应用层纪律保证隔离中小体量 SaaS

三、D1 没有"真事务":零物理外键 + db.batch 原子写

这是用 SQLite 系数据库最反直觉的一点。D1 不支持交互式BEGIN / COMMIT,没法开一个事务再中途查询、最后决定回滚。每条语句自带自动事务,批量写要靠别的方式。

所以我定了两条原则:

  1. 表之间零物理外键。Drizzle 的extra回调里不能塞原始sql\FOREIGN KEY``(会把 schema 解析拖垮),完整性靠应用层保证。
  2. 需要批量原子写时,用db.batch([...])。它把多条写打包成一个事务边界一起提交,相当于"伪事务"。比如一条线索转化时同时写leads状态变更和customers新建,就放进同一个 batch。

把传统关系型数据库"外键 + 长事务"的习惯,换成"应用层约束 + 批次提交"的 edge-native 写法,是用 D1 必须过的关。

四、架构决策全景表

把当时拍板的几个关键决策列出来,方便各位对照复盘:

决策做法为什么
多租户隔离单 D1 + 每表tenant_id行级几十租户级不需要独立库,备份统计都省事
原子写db.batch([...])替代事务D1 不支持交互式BEGIN/COMMIT
预聚合看板lead_stats_daily宽表 + Cron 每日写避免实时多表 GROUP BY 突破 30s 超时
禁拨合规手动 / Cron 对账刷新is_blocked+ 联动取消日程合规优先,标记即移出活跃池
废弃双源leads.nextFollowupAt仅来自 schedules避免两表数据不一致
审计归档D1 热存 365 天 → R2 NDJSON 冷归档单 D1 10GB 上限控制
Token 吊销JWT +tv版本号(KV 存)改密 / 全设备登出无需扫描删 RefreshToken
密码存储PBKDF2(SHA-256, 10万迭代) + 每用户随机盐符合行业密码存储规范
鉴权签名JWT(jose, HS256)双密钥密钥轮转不中断服务
接口校验Zod运行时类型安全,挡住脏数据

五、最反直觉的一个决策:统计看板必须预聚合

这条值得单独说。Cloudflare Workers 单次请求有30 秒超时(实际可用 CPU 时间更短)。如果做一个实时统计接口,跨 leads / call_records / schedules 几张表 JOIN 再 GROUP BY,数据量稍大就直接超时。

我的解法是空间换时间:建一张预聚合宽表lead_stats_daily,用 Cron 每天凌晨把前一天的统计数据算好写进去,看板接口直接SELECT这张表。代价是数据有"一天延迟",但看板本来就不是实时交易场景,T+1 完全够用。这一招把看板响应从"可能超时"变成"毫秒返回"。

六、Token 吊销不扫库:tv 版本号 + KV

传统做法里,用户改密码要去数据库删掉所有 RefreshToken,才能实现"全设备登出"。在 serverless 环境里这等于一次扫库,又慢又费钱。

我改成:JWT 里带一个tv(token version)字段,版本号存在 KV 里。用户改密或管理员踢人时,只把 KV 里的tv加一,所有旧 token 因为携带的tv对不上,验证时直接拒绝——全设备登出就是一次 KV 写入,秒级生效,零扫描。配合 JWT 双密钥(JWT_SECRET/JWT_SECRET_PREV),密钥轮转也能做到不中断服务。

七、踩坑与权衡:这趟没那么顺

这套栈有几个坑是实打实绊过我的,先在这里点一下,后面会单开文章细讲:

  • D1 单查询 100 绑定参数硬上限:批量写入得按"变量数"分块,宽表一张 INSERT 可能只能塞 3 行。
  • 本地 D1 复位要rm -rf .wrangler:不能用 better-sqlite3 直接开 D1 文件,否则内部状态污染,dev 各种诡异。
  • workerd 端口幽灵占用wrangler dev退出了,真正跑 worker 的子进程还占着端口,下次启动改动"不生效",得pkill -9 workerd
  • PBKDF2 在 Workers 上限 10 万迭代:超了直接NotSupportedError,而且 seed 脚本和运行时迭代次数必须对得上,否则登录永远失败。
  • KV 写入配额反模式:限流如果每请求kv.put一次,超过 16 QPS 就爆配额,得改成本地内存固定窗口。

这几个坑每一个都能单独成篇,今天先列在这里。

八、什么时候这套栈不适合你

这套栈有清楚的能力边界,选错场景一样会出问题。我见过有人把它硬塞进高并发事务系统,最后不得不迁回 Postgres。划条线:

场景这套栈的短板该用什么
超大量级 / 强一致事务D1 单库、无交互式事务,跨表强一致难保证老老实实上 Postgres / MySQL
长耗时计算Worker 30s 超时摆在那丢给队列 + 独立算力(如 Cloudflare Queues + 外部 worker)
复杂实时分析预聚合只解决固定看板专门 OLAP / 数仓

一句话:中小体量、请求短平快、能接受最终一致性的 CRUD 型 SaaS,这套栈合适;超出这个框,传统数据库该上还是得上。技术选型服务于项目的体量和团队的人力,不要为了 serverless 而 serverless。

小结

回头复盘:serverless 边缘栈做中小体量的 SaaS 完全够用,关键是把"传统数据库那套事务 / 实时聚合 / 扫库吊销"的习惯换掉,改成"批次 / 预聚合 / 版本号"的 edge-native 思路。

各位看官记住一点:选 Workers + D1 不是因为它比 Postgres 强,而是因为它让小团队 zero-ops 也能把多租户 SaaS 跑起来。技术选型永远服务于体量和人力,别被"最佳实践"绑架——能用最简单的栈 deliver,就是合适的架构。

如果这篇文章对你有帮助,发财的小手点个小赞。也欢迎翻翻我前面几篇实战记录:

相关阅读

  • NodeJS Koa 后端用户会话管理,JWT, Session,长短Token,本文一次性讲明白
  • node 后端和浏览器前端,有关 RSA 非对称加密的完整实践,前后端匹配的代码演示
  • Nodejs 实现 Mysql 数据库的全量备份的代码演示
  • 安装和配置 Nginx 和 Mysql —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录6
  • PVE 虚拟机安装 Ubuntu Server V24 系统 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录1

本文由 FungLeo 主导,Deepseek 优化校阅,转发请注明首发地址,谢谢大家!

http://www.cnnetsun.cn/news/3989252.html

相关文章:

  • 栈数据结构:顺序与链式存储实现及应用解析
  • 机器人仿真软件选型指南:从物理引擎到AI训练平台全解析
  • GTAIV.EFLC.FusionFix:技术修复方案深度解析与部署指南
  • 从东莞网站建设到化工材料的技术支持:打造精准营销的数字引擎,为传统行业注入新活力
  • 南昌网站建设q479185700惠:企业数字化转型的必经之路与避坑指南
  • 脚本文件执行原理与常见“无法识别”错误排查指南
  • Ubuntu 20.04下构建稳定可维护的ESP-IDF开发环境全攻略
  • Unity GIF加载全解析:从LZW解码到跨平台高性能播放器实现
  • API额度周期管理实战:从监控预警到智能优化策略
  • 嵌入式面试总结(八)——大小端
  • OpenCode双模式AI编程工具解析与实战
  • 避坑指南!专业长春网站建设哪家好?揭秘2024年长春互联网营销核心竞争力
  • 兴宁电子商务网站建设指南如何助力本土企业抓住数字化机遇
  • 解决Visual Studio编译错误:CL.exe退出代码-1073741515的全面指南
  • 高速数字电路设计:阻抗匹配与端接技术解决信号反射问题
  • 告别Suno订阅费!3步本地部署ACE-Step UI,开启你的免费AI音乐创作之旅
  • 创业资源丰富的香港EMBA对实体创业者有什么帮助
  • 免费招聘网站建设指南:零基础企业如何用最低成本搭建高效人才获取平台并解决招聘难痛点
  • AI驱动文档开发:从自然语言到可执行代码的范式转变
  • 烟台网站建设哪家服务好?揭秘2024年企业官网选择避坑指南与深度评测
  • 我踩过的去AI痕迹在线生成的三个无效坑
  • 从经典到现代:自控原理核心思想与工程实践深度解析
  • 开发者指南:如何为gh_mirrors/co/completion贡献代码与提交PR
  • 5步快速上手kiui:打造轻量级跨平台UI界面的终极指南
  • 如何用开源音频编辑器Audacity:从噪音消除到专业混音的5个步骤
  • 免费开源OCR终极指南:Umi-OCR让扫描件文字提取如此简单
  • Apifox CLI与Skill:构建稳定AI Agent工作流的API集成方案
  • 3个Python脚本彻底解决微信管理难题:微信工具箱完全指南
  • ComfyUI工作流中文版:20类50项专业AI创作工具集
  • 做网站别被坑:邢台企业网站建设咨询避坑指南,老板们必看