你打算做一个SaaS产品,代码写好了,但不知道该怎么上云?
这篇文章会告诉你:从选云厂商、到数据库选型、到多租户设计、到计费系统,每一个关键决策怎么做。
一、上云之前,先想清楚这三件事
很多人上云时的第一反应是去买一台服务器,装上环境,把代码部署上去。这是最常见的错误。
上云前,你需要想清楚三件事:
1. 你的SaaS产品服务谁?
– 面向企业客户(B2B):对数据隔离要求高,通常需要独立数据库,价格可以高
– 面向个人用户(B2C):对价格敏感,需要共享资源池,降低成本
– 混合模式:免费个人版 + 付费企业版,架构复杂度最高
2. 你的增长预期是什么?
– 冷启动阶段(0-1000用户):省钱第一,别过度设计
– 快速增长阶段(1000-10万用户):可扩展性第一
– 稳定运营阶段:稳定性和成本优化并重
3. 你的团队有多少运维能力?
– 无专职运维:用托管服务(数据库、缓存都用云托管版),少自建
– 有专职运维:可以自建 Kubernetes,灵活度更高
二、云厂商选择:别踩这个坑
先说结论:国内首选腾讯云或阿里云,别一上来就选AWS/GCP,除非你明确要做出海。
| 对比维度 | 腾讯云 | 阿里云 | AWS |
|---|---|---|---|
| 国内备案 | 支持,流程顺 | 支持,流程顺 | 不支持,需绕路 |
| 生态集成 | 微信生态最强 | 钉钉/淘宝生态强 | 全球服务齐全 |
| 客服支持 | 中文,响应快 | 中文,响应快 | 英文为主 |
| 计费方式 | 灵活,预付费优惠大 | 灵活,包年包月优惠 | 按量,复杂 |
| 适合场景 | 微信小程序/公众号相关 | 电商、大数据 | 出海项目 |
如果你的SaaS要做微信登录、微信支付,优先选腾讯云,省去很多跨平台问题。
三、多租户架构:SaaS最核心的设计决策
多租户设计有三种模式,选错了后面改造代价极大:
模式一:共享数据库 + 共享Schema(推荐初创阶段)
所有租户的数据在同一张表里,用 tenant_id 字段区分。
-- 用户表(共享Schema模式)
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
tenant_id VARCHAR(64) NOT NULL, -- 租户标识
username VARCHAR(100) NOT NULL,
email VARCHAR(200) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_tenant_id (tenant_id),
INDEX idx_tenant_email (tenant_id, email)
);
-- 每次查询都必须带 tenant_id
SELECT * FROM users WHERE tenant_id = 'tenant_abc' AND email = '[email protected]';
在应用层强制隔离,防止越权访问:
# Python Flask 示例:自动注入 tenant_id
from functools import wraps
from flask import g, request, abort
def require_tenant(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# 从 JWT Token 中解析 tenant_id
tenant_id = get_tenant_from_token(request.headers.get('Authorization'))
if not tenant_id:
abort(401)
g.tenant_id = tenant_id
return f(*args, **kwargs)
return decorated_function
class UserRepository:
def find_by_email(self, email: str) -> User:
# 强制使用当前租户的 tenant_id
return db.session.query(User).filter_by(
tenant_id=g.tenant_id, # 从上下文中自动注入
email=email
).first()
优点: 成本最低,运维最简单,适合冷启动阶段
缺点: 数据隔离靠代码保证,有安全风险;无法为单个租户独立扩容
模式二:共享数据库 + 独立Schema
每个租户一个独立的 Schema(数据库命名空间),表结构相同但数据完全隔离。
# 连接时动态切换 Schema
class TenantDatabaseManager:
def get_connection(self, tenant_id: str):
schema_name = f"tenant_{tenant_id}"
conn = get_db_connection()
conn.execute(f"SET search_path TO {schema_name}, public") # PostgreSQL
return conn
def create_tenant_schema(self, tenant_id: str):
schema_name = f"tenant_{tenant_id}"
with get_admin_connection() as conn:
conn.execute(f"CREATE SCHEMA IF NOT EXISTS {schema_name}")
# 运行迁移脚本,在新 Schema 中创建表
self.run_migrations(schema_name)
优点: 隔离性好,数据库层面防止越权;可以为大客户单独备份
缺点: Schema数量多时管理复杂;跨租户的统计查询困难
模式三:独立数据库(适合高端B2B)
每个客户一套独立的数据库实例,彻底隔离。
优点: 最强隔离,合规性最好,可以定制化
缺点: 成本最高,管理复杂度最高
推荐策略:
– 初创 → 共享Schema(单数据库)
– 成长 → 共享数据库 + 独立Schema
– 高端企业版 → 独立数据库(作为Premium套餐卖)
四、SaaS计费系统设计
计费系统是SaaS的命脉,设计不好会严重影响业务扩展。
计费模型设计
# 计费套餐定义
PLANS = {
"free": {
"name": "免费版",
"price": 0,
"limits": {
"users": 3, # 最多3个用户
"api_calls_per_day": 100,
"storage_gb": 1,
"features": ["basic"]
}
},
"starter": {
"name": "基础版",
"price": 99, # 元/月
"limits": {
"users": 10,
"api_calls_per_day": 1000,
"storage_gb": 10,
"features": ["basic", "export", "api"]
}
},
"pro": {
"name": "专业版",
"price": 399,
"limits": {
"users": 50,
"api_calls_per_day": 10000,
"storage_gb": 100,
"features": ["basic", "export", "api", "sso", "custom_domain"]
}
}
}
# 用量检查中间件
class UsageLimiter:
def check_api_call(self, tenant_id: str) -> bool:
plan = self.get_tenant_plan(tenant_id)
daily_limit = PLANS[plan]["limits"]["api_calls_per_day"]
today_calls = self.get_today_calls(tenant_id)
if today_calls >= daily_limit:
raise QuotaExceededError(f"已达到今日API调用上限 {daily_limit} 次")
# 记录本次调用
self.increment_usage(tenant_id, "api_calls")
return True
续费提醒与降级处理
from datetime import datetime, timedelta
import schedule
class SubscriptionManager:
def check_expiring_subscriptions(self):
"""每天检查即将到期的订阅"""
expiring_soon = self.db.query("""
SELECT tenant_id, plan, expire_at
FROM subscriptions
WHERE expire_at BETWEEN NOW() AND NOW() + INTERVAL 7 DAY
AND status = 'active'
""")
for sub in expiring_soon:
days_left = (sub.expire_at - datetime.now()).days
self.send_renewal_reminder(sub.tenant_id, days_left)
def handle_expired_subscription(self, tenant_id: str):
"""订阅到期处理:降级到免费版"""
self.update_plan(tenant_id, "free")
# 超出免费版限制的数据标记为只读,不删除
self.mark_excess_data_readonly(tenant_id)
self.send_expiry_notification(tenant_id)
五、关键基础设施选型
推荐技术栈(腾讯云版)
计算层: 云服务器 CVM 或 容器服务 TKE
数据库: 云数据库 MySQL(主从) + TDSQL(如需分布式)
缓存: 云数据库 Redis(Cluster版)
文件存储: 对象存储 COS + CDN 加速
消息队列: CMQ 或 TDMQ(RocketMQ版)
搜索: Elasticsearch Service
监控: 云监控 + 日志服务 CLS
最低可行架构(月费控制在1500元内)
1台 4核8G 云服务器(标准型,约200元/月)
└── 部署后端应用 + Nginx
1个 RDS MySQL 实例(2核4G,约400元/月)
└── 主实例,开启自动备份
1个 Redis 实例(1G,约80元/月)
└── Session存储 + 接口缓存
COS 对象存储(50G,约10元/月)
└── 用户上传文件
域名 + SSL 证书(免费版够用)
总计: 约700元/月,足够支撑前1000用户
六、上线前必检清单
| 检查项 | 重要性 | 说明 |
|---|---|---|
| 数据备份 | ⭐⭐⭐ | RDS开启自动备份,每天一次 |
| SSL证书 | ⭐⭐⭐ | 全站HTTPS,免费申请 |
| 错误监控 | ⭐⭐⭐ | 接入Sentry,异常第一时间告警 |
| 性能压测 | ⭐⭐ | 上线前用JMeter/k6压测接口 |
| 敏感数据加密 | ⭐⭐⭐ | 密码bcrypt,手机号/身份证AES加密 |
| 限流防刷 | ⭐⭐ | Nginx限流 + 接口层限流 |
| 日志记录 | ⭐⭐ | 操作日志 + 错误日志,保留30天 |
| 合规性 | ⭐⭐⭐ | 隐私政策、用户协议、ICP备案 |
七、常见陷阱:我见过的SaaS创业团队踩的坑
坑1:一开始就买太多服务器
初创阶段买了5台服务器,结果用户没起来,每月白烧2000块。应该按需扩容,先用1台验证商业模式。
坑2:忘记做多租户隔离,数据串了
某SaaS产品因为代码bug,A租户可以看到B租户的数据,导致严重事故被用户起诉。多租户隔离是红线,必须在架构层面保证。
坑3:计费系统设计太简单,后期无法扩展
只支持按月计费,用户要求按用量计费时,整个计费系统需要重写。
坑4:没做好数据库迁移方案
版本迭代时直接修改线上数据库,导致服务中断30分钟。应该使用 Flyway/Liquibase 做数据库版本管理:
# 使用 Flyway 管理数据库版本
# 文件命名规则:V{版本}_{描述}.sql
migrations/
├── V1.0.0__init_schema.sql
├── V1.1.0__add_tenant_id.sql
├── V1.2.0__add_subscription_table.sql
└── V2.0.0__add_usage_tracking.sql
总结
SaaS上云的核心原则:
1. 冷启动省钱:能用托管服务就用托管服务,别自建
2. 多租户设计:这是SaaS的根本,早想清楚,比什么都重要
3. 计费系统提前设计:后期改造代价极大
4. 监控先行:没有监控的SaaS,出问题你都不知道
记住:完成比完美更重要,先上线跑起来,然后迭代优化。
关于作者
长期关注大模型应用落地与云服务器实战,专注技术在企业场景中的落地实践。
个人博客:y1cloud.cn —— 持续更新云计算、AI大模型实战教程,欢迎访问交流。
