Orbitra 负载实测:480 请求/秒,零错误,p99 182 毫秒
追踪器生来就处在负载之下:每个点击都是一次写入、一次唯一性检查和一次重定向。因此在给 v1.6.3 打标签之前,我们在真实规模数据库上重构了唯一性管线,并在测试台上用 k6 分步负载进行了压测。数字如下。
为什么要做这次测试
v1.6.3 让去重过滤器对移动 NAT 更公平:唯一性键从单独的 IP 变为 IP + User-Agent。在运营商 NAT 后面,两个不同的人曾共用一个 IP——第二位访客拿到的 subid 在数据库里没有对应的点击行,他的转化无从挂靠。
这一改动需要在已经承载真实流量的数据库上新增 ua_hash 列和一个复合索引。这是最吓人的一类迁移——所以在打标签之前,我们按用户安装会经历的完整路径,在真实规模的数据上做了演练。
测试台
- 云端容器:2 vCPU / 7 GB 内存,Ubuntu 24.04。nginx 1.24 + PHP-FPM 8.3,保持
install.sh安装后的原厂配置——基础跑分不做任何手动调优。 - SQLite WAL 模式。k6 跑在同一台机器上(约 0.3 核开销),所以数字只可能偏低。
- 分步恒定到达率加压:每档 60 秒,超时 5 秒,不跟随重定向。
- 广告系列:一个常规流 → offer 重定向,唯一性窗口为 24 小时 IP + User-Agent。唯一访客使用随机公网 IP 和唯一 UA;重复访客使用 50 组固定 IP/UA。
- 三个数据库:约 5 万点击(健全性检查)、320 万(主测),以及用于迁移演练的 390 万点击升级前快照(约 100 万来自最近 24 小时,每个 NAT IP 约 8 500 次点击)。
- 成对运行:v1.6.2 在同一数据库上与新版本并行服务;某次跑挂之后会等 FPM 积压清空再测。
结果
| 场景(除注明外均为 320 万数据库) | req/s | 错误 | p99 | CPU |
|---|---|---|---|---|
| 唯一访客,原厂 FPM(约 5 万数据库) | 480 | 0 | 46 毫秒 | 76% |
| 唯一访客 | 480 | 0 | 75 毫秒 | 77% |
| 重复访客(50 组 NAT) | 480 | 0,约 1% 经 spool | 14 毫秒 | 61% |
| 唯一访客 + 面板打开,报表每 2 秒刷新 | 320 | 0 | 587 毫秒 | 81% |
| 唯一访客,调优 FPM/nginx,开启 geo 库 | 560 | 0 | 187 毫秒 | 94% |
| 唯一访客,刻意过载 | 800 | 27% 超时 | — | 97% |
以及 390 万快照上的迁移演练:「升级」后的第一个请求在 0.04 秒内完成 migration 55,一次 cron 在 4.8 秒内建好索引、29.6 秒回填 1 008 085 个 UA 哈希——NAT 流量以 480 请求/秒通过,零错误,p99 182 毫秒,subid 全部入库。同一场景下旧版本丢失 27 775 个 subid,新版本为零。
两个值得点名的细节。即使在 800 请求/秒——越过崩溃点——每一个已完成的重定向仍然写入了数据库:过载损害的是延迟,而不是持久性。哈希正确性也单独校验:随机抽 2 000 行与 PHP crc32 比对——零差异。
算一笔账:一次点击约消耗 2.5 毫秒 CPU,2 vCPU 的天花板约在 550–600 请求/秒(CPU 瓶颈)。同样的算法外推到 4 vCPU 约为 1 000 请求/秒——目前只是纸面推算,我们也如实标注。
压测在发布前抓到了什么
这次压测一半的价值在于揪出的 bug——全部在打标签前修复:
- 点击路径上未关闭的游标会立即触发 SQLITE_BUSY_SNAPSHOT——重复访客的点击进了 spool 而不是数据库。
- 去重只按 IP 作键——NAT 邻居会丢点击,拿到不存在的 subid。
- 报表日期过滤使用 date(created_at, tz),走不到索引——大数据库上报表慢如蜗牛。
- 像素 INSERT 有 23 列却只有 22 个值——像素点击被静默丢弃。
- 带 NULL 哈希的旧行让 NAT 崩溃在升级后的一个唯一性窗口内继续存在。
为什么撑得住
- SQLite WAL 模式,单文件,零额外服务。没有吃内存的 ClickHouse、Redis 或 MySQL——瓶颈是磁盘 I/O,它随 VPS 一起扩展。
- 写入缓冲队列(spool):SQLite 短暂锁定时,点击落入文件队列,由每分钟的 cron 补写——访客的重定向不用等,点击也不丢失。
- 唯一性检查走索引:点击携带紧凑的 crc32 UA 哈希,每次点击的四项唯一性检查都按 (ip, ua_hash, created_at) 索引查找,而不是扫描该 IP 的全部点击。
坦率的说明
- 测试在 v1.6.3 候选版本上进行。存储架构(SQLite、spool、索引)在当前版本中没有变化。
- 我们的测试台不是你的 VPS:绝对数字随硬件走。能迁移的是比例关系和失败模式。
- 4 vCPU 的数字是外推,不是实测——我们直说,而不是向上取整。
- 这是合成压力测试加上真实规模的迁移演练——不是真实流量下与其他追踪器的 A/B 对比。
对你意味着什么
一台普通的 VPS——多数指南推荐的入门档——已经有应对可观日常流量的余量,而且上面没有按事件计费的账单等着你。如果你因为「SQLite 撑不住」而留在 Keitaro,这份数据值得一看。
Orbitra 在干净的 Ubuntu 服务器上约 15 分钟即可装好——一条命令;在线演示随时开放,可以先点点面板。
常见问题
测试跑在什么硬件上?
一台 2 vCPU / 7 GB 内存的云容器,Ubuntu 24.04,nginx 和 PHP-FPM 保持 install.sh 的原厂配置,k6 也跑在同一台机器上——所以数字只可能偏低。绝对数字始终取决于你的 VPS:你扩展的是服务器,而不是套餐。
结果适用于当前版本吗?
测试在 v1.6.3 候选版本上进行。存储架构(SQLite + 写入缓冲 + 唯一性索引)与当前版本一致;此后的变化是功能和界面。
为什么用 SQLite 而不是 ClickHouse?
单文件、WAL 模式、无需独立的数据库服务器。在中小流量下瓶颈是磁盘 I/O 而非内存,所以便宜的 VPS 足以走很远。对于持续每天数百万点击,列式架构确实更强——我们对此直言不讳。
升级有没有丢数据?
在重复访客场景中,旧版本丢失了 27 775 个 subid;新版本为零。320 万点击上的报表与旧版本逐行一致。