# 短链系统设计方案对比与融合总结 ## 一、核心目标 短链系统本质只做两件事: 1. **压缩**:把长网址映射成短字符串。 2. **跳转**:用户访问短链时,准确、快速地返回长链。 围绕这两件事,不同场景会衍生出容量、并发、成本、安全、业务规则等差异。 --- ## 二、方案 A:Cloudflare Sproutlink(当前项目) ### 2.1 架构组成 | 组件 | 说明 | |------|------| | 边缘运行时 | Cloudflare Worker | | 数据库 | Cloudflare D1(SQLite) | | 静态前端 | Vite React SPA,通过 `ASSETS` 绑定由 Worker 直接服务 | | 短码生成 | D1 单表原子自增 + 线性同余混洗 + Base62 编码 | | 部署 | Serverless,静态资源不计入 Worker Bundle | ### 2.2 短码生成原理 1. **原子自增序列**:`link_seq` 表保存 `next_id`,每次创建时 `UPDATE ... SET next_id = next_id + 1 RETURNING next_id`。 2. **线性同余混洗**:把顺序的 `seq` 映射到 `[0, 62^4)` 空间,公式为 `index = (A × seq + B) mod 62^4`。 - `A`:与 `62^4` 互质的乘数(`LINK_MULTIPLIER` secret,默认内置大质数)。 - `B`:偏移量(`LINK_OFFSET` secret)。 - 双射特性保证唯一,同时让短码看起来“不连续”。 3. **Base62 编码**:字符集 `0-9A-Za-z`,固定 **4 位**,容量 `62^4 ≈ 1477 万`。 4. **自定义短码**:管理员可指定 1–32 位 `a-zA-Z0-9_-`,但不可为恰好 4 位纯英数字,避免与自动码池冲突。 ### 2.3 跳转与规则校验 - 使用 **HTTP 302 临时重定向**,确保每次点击都经过 Worker,便于统计。 - 访问流程:查 D1 → 校验过期/点击上限 → 校验 Geo/设备/浏览器/IP/密码 → 302 跳转并原子递增 `clicks`。 ### 2.4 业务规则能力 | 规则 | 实现 | |------|------| | 有效期 | `expires_at` 字段 | | 点击上限 | `max_clicks` 字段 | | 密码保护 | PBKDF2 哈希,密码页表单验证 | | Geo 限制 | `request.cf.country` | | 设备/浏览器 | `User-Agent` + `Sec-CH-UA*` | | IP 白/黑名单 | `CF-Connecting-IP`,支持 IPv4 CIDR | | 软删除 | `deleted` 字段 | ### 2.5 优势特点 - **成本极低**:Serverless + D1 + 静态托管,免费/低价套餐即可运行。 - **部署极简**:单人可维护,无需维护服务器、Redis、MySQL 集群。 - **短码最短**:4 位 Base62,对二维码、短信字数敏感场景友好。 - **访问规则丰富**:密码、Geo、设备、浏览器、IP 规则开箱即用,适合私域/营销场景。 - **短码防扫号**:线性同余混洗让相邻创建的短码不是连续递增,提高被遍历的难度。 ### 2.6 局限 - **容量有限**:4 位码仅 1477 万,公开大规模服务很快耗尽。 - **读并发依赖 D1**:无独立缓存层,热点短链会直接把压力打到数据库。 - **无缓存穿透防护**:缺少布隆过滤器或空值缓存,面对恶意遍历存在一定风险。 - **Cloudflare 生态绑定**:迁移或私有化部署成本较高。 --- ## 三、方案 B:大规模分布式短链设计(经典大厂方案) ### 3.1 架构组成 | 组件 | 说明 | |------|------| | 发号器 | 分布式 ID 发号器(MySQL 号段模式 / Redis 自增) | | 数据库 | MySQL 持久化存储 `code → target_url` | | 缓存 | Redis 读缓存 | | 防护 | 布隆过滤器 + 空值缓存 + 网关限流 | | 跳转状态码 | HTTP 302 | ### 3.2 短码生成原理 1. **分布式 ID 发号器**:为每个长链接分配一个全局唯一、单调递增的数字 ID。 2. **Base62 编码**:把十进制 ID 转换成 62 进制,固定 **6 位**。 - 字符集 `0-9a-zA-Z`。 - 容量 `62^6 ≈ 568 亿`,足够公开服务长期运行。 3. **唯一索引兜底**:MySQL 对 `code` 加唯一索引,防止并发冲突。 ### 3.3 读链路(高并发重点) ``` 用户点击 /abc123 ↓ 网关限流 + WAF ↓ Redis 查 sl:abc123 ├─ 命中 → 直接返回 302 └─ 未命中 → 布隆过滤器判断 ├─ 一定不存在 → 直接 404 └─ 可能存在 → 查 MySQL ├─ 存在 → 写入 Redis → 302 └─ 不存在 → 写短时空值到 Redis → 404 ``` ### 3.4 写链路 ``` 用户提交长链 ↓ 参数校验、黑名单域名过滤 ↓ 分布式发号器取号 ↓ Base62 编码 ↓ MySQL 插入(唯一索引防冲突) ↓ 写入 Redis + 布隆过滤器 ↓ 返回短链 ``` ### 3.5 优势特点 - **容量巨大**:6 位码 568 亿,远超 4 位方案。 - **读性能强**:Redis 缓存扛住读多写少特性,适合热点营销短信/微博场景。 - **生产级健壮**:布隆过滤器防缓存穿透、空值缓存防击穿、网关限流防刷。 - **去 Cloudflare 依赖**:技术栈通用,易于私有化或多云部署。 ### 3.6 局限 - **成本高**:需要维护 MySQL、Redis、发号器、网关等多套组件。 - **短码更长**:6 位比 4 位在短信/二维码场景略逊。 - **业务规则薄弱**:标准方案通常只讲“跳转”,缺少密码、Geo、设备等访问控制能力。 --- ## 四、两套方案核心差异对比 | 维度 | Sproutlink(Cloudflare) | 大规模分布式方案 | |------|--------------------------|------------------| | **短码长度** | 4 位 | 6 位 | | **容量** | 62⁴ ≈ 1477 万 | 62⁶ ≈ 568 亿 | | **发号方式** | D1 原子自增 + 线性同余混洗 | 分布式 ID 发号器 | | **存储** | Cloudflare D1 | MySQL + Redis | | **读缓存** | 无 | Redis | | **防缓存穿透** | 无 | 布隆过滤器 + 空值缓存 | | **并发能力** | 中等,依赖 D1 | 高,依赖 Redis | | **访问规则** | 丰富(Geo/设备/IP/密码) | 通常无或简单 | | **部署成本** | 极低 | 较高 | | **适用场景** | 私域、小团队、个人、轻量营销 | 公开互联网、大厂、海量用户 | --- ## 五、融合后的推荐设计 如果让我从零设计一个**可大可小、可进化的短链系统**,我会把两套方案取长补短: ### 5.1 用大规模方案做底座 - **短码长度**:默认 6 位 Base62,容量 568 亿。 - **发号器**:采用 **MySQL 号段模式**(Segment),每个实例本地批量领号,减少 DB 压力。 - **存储**:MySQL 持久化 + Redis 读缓存。 - **防护**:布隆过滤器 + 空值缓存 + 网关限流。 - **状态码**:统一使用 **302**,保证点击量可统计。 ### 5.2 吸收 Sproutlink 的规则引擎 | 规则 | 实现位置 | |------|----------| | 有效期 | MySQL `expires_at` + 定时清理任务 | | 点击上限 | MySQL 原子递增 `clicks` / `max_clicks` | | 密码保护 | Worker / API 网关,密码页验证后 302 | | Geo 限制 | CDN 边缘(Cloudflare、阿里云 CDN 等)或网关解析 `CF-IPCountry` | | 设备/浏览器 | 解析 `User-Agent` + Client Hints | | IP 白/黑名单 | 网关层,支持 IPv4 / IPv6 / CIDR | | 自定义短码 | 管理员接口,与自动 6 位码池隔离 | ### 5.3 可选:保留短码混洗能力 在 6 位方案中,可以**可选地**对发号器拿到的顺序 ID 做一次可逆混洗(如线性同余),让短码看起来不连续,提升防扫号能力。注意混洗函数必须保证双射。 ### 5.4 最终数据流 **写(生成短链)**: ``` 提交长链 → 校验 + 黑名单过滤 → 号段发号器取唯一 ID → 可选混洗 → Base62 编码 → MySQL 写入 → 写入 Redis + 布隆过滤器 → 返回 6 位短链 ``` **读(访问短链)**: ``` 用户访问 /abc123 → 网关限流/WAF → Redis 查缓存 ├─ 命中 → 规则校验 → 302 └─ 未命中 → 布隆过滤器 ├─ 不存在 → 404 └─ 可能存在 → MySQL 查询 ├─ 存在 → 写 Redis → 规则校验 → 302 └─ 不存在 → 写空值缓存 → 404 ``` --- ## 六、选型建议 | 场景 | 推荐方案 | |------|----------| | 个人博客、小团队、私域运营、预算敏感 | **Sproutlink(Cloudflare)** | | 公开互联网、大厂、营销短信、微博 | **大规模分布式方案(6 位 + 发号器 + Redis)** | | 希望初期低成本、后期可平滑扩展 | **融合方案**:Worker/D1 起步,用户量增长后迁移到 MySQL + Redis | --- ## 七、一句话总结 > **Sproutlink 是“小而美、功能全、低成本”的私域短链方案;大规模分布式方案是“高容量、高并发、抗攻击”的公开短链标准架构。最优实践是把后者的容量与并发能力,与前者的访问规则引擎结合起来,按需分层演进。**