SaaS信用计费中直接修改余额列的方法虽然快速,但存在风险。哈希刮削器记录所有扣款作为历史记录,并使用历史记录的累加来计算余额,采用基于历史记录的计费架构。这种方法在计费透明性、调试易用性和大规模并发处理性能方面都有优势。
TL;DR
- 直接修改余额(UPDATE)虽然快速,但无法追踪错误原因
- 记录所有扣款为历史记录可以证明所有交易
- 行锁(SELECT FOR UPDATE)在并发请求中成为瓶颈。历史记录的插入没有瓶颈
- 哈希刮削器以每天数万笔处理为标准,历史记录方式在速度和准确性上均优于其它方式
- 计费架构是与客户的约定,而非工程设计
为什么大多数SaaS直接修改余额?
在扣除信用时,大多数SaaS都这样做。
打开余额列,减去数字,保存。
UPDATE service_tickets SET use_credit = use_credit + 10 WHERE id = ?
这样做很快。
很直观。
几乎所有计费教程都教授这种方法。
我们刚开始也是这样的。
只看余额会失去什么?
在生产环境中出现问题时,
找不到答案。
"为什么这个客户的信用少了3个?"
"昨天失败的请求是否扣款了?"
"这个数字是否正确,如何验证?"
余额只显示结果。
过程已经消失。
就像用铅笔写的账簿一样。
擦掉重新写,就不知道原来写了什么。
什么是基于历史记录的计费?
因此,我们改变了方法。
不直接修改余额。
将所有扣款记录为单独的历史记录。
余额由历史记录的累加计算得出。
이력 #1: -10 크레딧 (https://example.com, 성공, 2026-03-07 14:32)
이력 #2: -10 크레딧 (https://news.site.com, 성공, 2026-03-07 14:33)
이력 #3: 0 크레딧 (https://blocked.com, 실패, 2026-03-07 14:35)
─────────────────
잔액 = 총 크레딧 - 이력 합산
就像用钢笔写的复式记账一样。
修改时划掉,写在新行上。
任何交易都不会消失。
每笔扣款必须有记录。
何时、哪个URL、扣多少信用、成功还是失败。
失败的请求不会扣款。
如果扣款了,一定会留下原因。
基于历史记录的方式在大规模处理中为什么更快?
这里会有疑问。
"每次都插入历史记录不会很慢吗?"
哈希刮削器从2018年开始,在8年内从5000多个网站收集数据。
每天处理数万个请求。
速度不能放弃。
然而,讽刺的是,
基于历史记录的方式更快。
要直接修改余额需要行锁(SELECT FOR UPDATE)。
如果同一个客户的请求同时有10个进来,
9个必须排队等待。
就像交通警察一辆一辆放行车辆一样。
记录历史的方式不同。
插入不会锁定其他行。
10个请求同时进来,10个都会立即记录。
就像有10条车道的高速公路。
余额稍后再累加即可。
| 方法 | 同时处理10个请求 | 瓶颈 | 故障追踪 |
|---|---|---|---|
| 直接修改余额(行锁) | 顺序处理(逐个) | 严重 | 不可能 |
| 记录历史(插入) | 并行处理(同时10个) | 无 | 立即通过历史追踪 |
看似慢的方式
在大规模处理中却没有瓶颈。
为什么计费架构是与客户的约定?
这不是工程偏好的问题。
计费架构最终是对以下问题的回答:
- 当客户问“我的信用去哪了?”时,是否能立即回答?
- 当系统错误导致错误扣款时,是否能准确找到错误来源?
- 对于失败请求,是否能证明未进行扣款?
仅凭一个余额列无法回答这些问题。
需要有历史记录才能回答。
我们是一家四口之家的小公司。
正因为小,不能马虎,而是要做得更好。
能随时拿出一笔客户信用的使用记录
这就是我们选择笔记本方式的原因。
不要直接减少余额。让历史记录生成余额。
常见问题
Q. 使用基于历史记录的方式会导致余额查询变慢吗?
虽然余额是通过历史记录的累加计算得出,但并不会实时每次计算。定期缓存或事后结算不会影响查询性能。即使历史记录有数十万条,基于索引的累加也在几毫秒内完成。
Q. 失败请求也会记录历史吗?
是的。会记录失败历史,但扣款金额为0。“此请求失败,未进行扣款”这一事实本身就是客户信任的依据。
Q. 行锁方式在哪些情况下适用?
在几乎没有并发请求的小型服务中,行锁更简单有效。但像API计费这样频繁并发请求的结构中,基于历史记录的方式在性能和追踪性上更有优势。
Q. 哈希刮削器的计费单位是多少?
1个URL请求 = 10信用,仅在收集和转换成功时扣除。失败时不会有信用损失。




