残高を直接削減しない—SaaS課金アーキテクチャ、元帳方式を選んだ理由

SaaSのクレジット請求で残高を直接修正せず、全ての差し引きを履歴(元帳)として記録するアーキテクチャを選択した理由。速度、透明性、顧客信頼の観点から元帳ベースの請求の利点をハッシュスクラッピング技術ブログでまとめました。

51
残高を直接削減しない—SaaS課金アーキテクチャ、元帳方式を選んだ理由

SaaS クレジット請求で残高カラムを直接修正する方法は速いが危険です。ハッシュスクラッパーはすべての差し引きを履歴(元帳)として記録し、残高は履歴の合計で算出する元帳ベースの請求アーキテクチャを使用しています。この方法は請求の透明性、デバッグの容易さ、大量同時処理のパフォーマンスのすべてで有利です。

要約
- 残高を直接修正(UPDATE)すると速いが、誤差が出ても原因を追跡できない
- すべての差し引きを履歴(元帳)として記録するとすべての取引を証明できる
- row lock(SELECT FOR UPDATE)は同時リクエストでボトルネックになる。元帳のINSERTにはボトルネックがない
- ハッシュスクラッパーは1日数万件の処理基準、元帳方式が速度と正確性の両方で優位
- 請求アーキテクチャはエンジニアリングではなく、顧客との約束である


なぜほとんどの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)
─────────────────
잔액 = 총 크레딧 - 이력 합산

ボールペンで書いた複式簿記です。
修正するときも線を引いて、新しい行に書きます。
どの取引も消えません。

1件の差し引きには必ず記録が付随します。
いつ、どのURLを、いくつのクレジットで、成功したか失敗したか。
失敗したリクエストは差し引かれません。
差し引いた場合、その理由が必ず残ります。


元帳方式が大量処理でも速い理由は?

ここで疑問が生じます。
"履歴を毎回INSERTすると遅くないですか?"

ハッシュスクラッパーは2018年から8年間、5,000以上のウェブサイトからデータを収集してきた会社です。
1日数万件のリクエストを処理します。
速度を犠牲にするわけにはいきません。

しかし逆説的に、
元帳方式の方が速いのです。

残高を直接修正するにはrow lock(SELECT FOR UPDATE)が必要です。
同じ顧客のリクエストが同時に10件入ってきた場合、
9件は列に並んで待たなければなりません。
交通警察が車を1台ずつ通すのと同じです。

履歴を残す方法は異なります。
INSERTは他の行をロックしません。
10件が同時に入っても、10件すぐに記録されます。
10車線の高速道路です。

残高は後で合計すればいいです。

方法 同時10件処理 ボトルネック 障害追跡
残高直接修正(row lock) 順次処理(1件ずつ) 深刻 不可能
元帳記録(INSERT) 並列処理(10件同時) なし 履歴で即座に追跡

遅く見える方法が
大量処理では逆にボトルネックがありません。


請求アーキテクチャはなぜ顧客との約束なのか?

これはエンジニアリングの好みの問題ではありません。

請求アーキテクチャは最終的にこの質問に対する答えです:

  • 顧客が「私のクレジットはどこに行ったのか?」と尋ねたとき、即座に答えられるか?
  • システムエラーで誤って差し引かれた場合、正確にどこが間違っているか見つけられるか?
  • 失敗したリクエストには請求されなかったことを証明できるか?

残高カラム1つではこれらの質問に答えることはできません。
履歴が必要です。

私たちは4人の小さな会社です。
小さいから適当にしてもいいわけではなく、
小さいからちゃんとやらなければいけないのです。

顧客のクレジット1件がどこに使われたか
いつでも取り出して見せることができること。

それが私たちが請求システムでボールペンを選んだ理由です。

残高を削らない。履歴が残高を作るようにする。


FAQ

Q. 元帳方式だと残高照会が遅くないですか?

残高は履歴の合計で計算しますが、リアルタイムで毎回集計するわけではありません。定期的にキャッシュしたり、事後精算すれば照会パフォーマンスに影響しません。履歴が数十万件でもインデックスベースの合計は数ミリ秒以内です。

Q. 失敗したリクエストにも履歴を残しますか?

はい。失敗履歴も記録しますが、差し引き金額は0です。"このリクエストは失敗し、請求されていない"という事実自体が顧客信頼の根拠となります。

Q. row lock方式が適切な場合もありますか?

同時リクエストがほとんどない小規模サービスでは、row lockがより単純で効果的です。しかしAPI請求のように同時リクエストが頻繁な構造では、元帳方式がパフォーマンスと追跡性の両方で有利です。

Q. ハッシスクラッパーの請求単位はどうなりますか?

1つのURLリクエスト = 10クレジットであり、収集と変換が成功した場合のみ差し引かれます。失敗した場合、クレジット損失はありません。

Comments

Add Comment

Your email won't be published and will only be used for reply notifications.

続きを読む

Get notified of new posts

We'll email you when 해시스크래퍼 기술 블로그 publishes new content.

Your email will only be used for new post notifications.