1 分

マルチテナントデータベースがセキュリティとパフォーマンスに与える影響

マルチテナントデータベースがセキュリティとパフォーマンスに与える影響、主要なリスク(分離、ノイジーネイバー)、およびテナントを安全かつ高速に保つための実践的な対策を学びます。

マルチテナントデータベースがセキュリティとパフォーマンスに与える影響

マルチテナントデータベースとは

マルチテナントデータベースとは、多数の顧客(テナント)が同じデータベースシステムを共有する構成です—同じデータベースサーバ、同じストレージ、しばしば同じスキーマを使い、アプリケーション側が各テナントが自分のデータにしかアクセスできないように保証します。

アパートの建物のように考えてください:建物の構造や設備は共有しますが、各テナントは自分の鍵のかかった部屋を持っています。

マルチテナント vs シングルテナント(概念レベル)

シングルテナントでは、各顧客に専用のデータベースリソース(例えば専用のデータベースインスタンスやサーバ)が割り当てられます。分離は単純で分かりやすいですが、顧客数が増えるとコストや運用負荷が高くなります。

マルチテナンシーではインフラを共有するため効率的ですが、境界を強制する設計が必要になります。

SaaSチームがマルチテナンシーを選ぶ理由

SaaS企業がマルチテナンシーを採用する実務的理由は:

  • 顧客あたりのコストが低い(計算・ストレージ・ライセンス・運用時間を共有)
  • 大規模運用が簡素化される(パッチやアップグレード、監視対象のデータベースが減る)
  • 新規顧客のオンボーディングが速い(完全なデータベーススタックを毎回プロビジョニングする必要がない)

重要な期待値:設計が結果を決める

マルチテナンシー自体が自動的に「安全」や「高速」になるわけではありません。結果は、テナントをどのように分離するか(スキーマ、行、あるいはデータベース単位か)、アクセス制御の実装、暗号鍵の扱い方、あるテナントの負荷が他を遅くしないようにする手段などの選択によって決まります。

以降はこれら設計上の選択に焦点を当てます—マルチテナントシステムではセキュリティとパフォーマンスは設計して作る機能です。

一般的なマルチテナントデータベースモデル

マルチテナンシーは単一の設計ではなく、どれだけ密にインフラを共有するかのスペクトラムです。選んだモデルが分離の境界を定め、それがデータベースのセキュリティ、パフォーマンス分離、日常運用に直接影響します。

テナントごとのデータベース(Database-per-tenant)

各テナントが専用のデータベースを持ちます(同じサーバやクラスタ上に配置されることもあります)。

分離境界: データベース自体。クロステナントアクセスは通常データベース境界を越える必要があり、最もクリーンな分離方法です。

運用上のトレードオフ: スケールすると運用が重くなります。アップグレードやスキーマ移行を何千回も実行する必要があるかもしれませんし、接続プーリングが複雑になることもあります。テナント単位のバックアップ/復元は分かりやすいですが、ストレージや管理オーバーヘッドが速く増えます。

セキュリティ&チューニング: 顧客ごとにセキュリティとチューニングを行いやすく、コンプライアンス要件が異なる場合に適しています。

テナントごとのスキーマ(Schema-per-tenant)

同じデータベースを共有するが、各テナントは独自のスキーマを持ちます。

分離境界: スキーマ。意味のある分離ですが、正しい権限設定とツールが前提です。

運用上のトレードオフ: マイグレーションは繰り返されますが、データベースごとに分けるより軽めです。バックアップは難しくなることがあり、多くのツールはデータベースを単位にバックアップするため、テナント単位の操作ではスキーマレベルのエクスポートが必要になります。

セキュリティ&チューニング: 共有テーブルより分離しやすいですが、権限や間違ったスキーマ参照を避けるための厳格さが求められます。

テナントごとのテーブル(Table-per-tenant)

すべてのテナントが同じデータベースとスキーマを共有するが、各テナントに対して別のテーブルを作る(例:orders_tenant123)。

分離境界: テーブルセット。少数のテナントでは機能しますがスケールしにくい:メタデータの増大、マイグレーションスクリプトの複雑化、クエリ計画の劣化などが起きます。

セキュリティ&チューニング: 権限は細かく設定できますが運用が煩雑になりやすく、新機能追加時にミスが発生しやすいです。

共有テーブル(Shared-table / Shared schema)

すべてのテナントが同じテーブルを共有し、tenant_id カラムで区別します。

分離境界: クエリ層やアクセス制御レイヤ(一般に行レベルセキュリティ)。スキーマを一つだけ管理すれば良いため運用は効率的ですが、データベースのセキュリティとパフォーマンス分離の要件は最も厳しくなります。

セキュリティ&チューニング: 全てのクエリがテナントを意識している必要があり、ノイジーネイバー問題が起きやすく、リソーススロットリングや慎重なインデックス設計が不可欠です。

一つの有用なルール:共有度が高いほどアップグレードは簡単になりますが、テナント分離とパフォーマンス分離のためにより厳密な設計が必要になります。

マルチテナンシーがセキュリティモデルをどう変えるか

マルチテナンシーは単に「複数の顧客が一つのデータベースにいる」という状態ではありません。脅威モデルが変わり、最大のリスクは外部からの突破よりも「認可されたユーザーが誤って(あるいは意図的に)別テナントのデータを見てしまうこと」へシフトします。

認証と認可:テナントコンテキストは認可の問題

認証は「あなたは誰か?」に答えます。認可は「何にアクセスできるか?」に答えます。マルチテナントデータベースではテナントコンテキスト(tenant_id, account_id, org_id など)を認可時に必ず適用し、オプションのフィルタとして扱ってはいけません。

よくある誤りは、ユーザーが認証されテナントが「分かっている」ならアプリが自動的にクエリを分離してくれるだろうと期待することです。実際には分離は明示的で一貫した制御点(データベースポリシーや必須のクエリレイヤなど)で強制する必要があります。

コアルール:すべての読み書きはテナントスコープで行うこと

最も単純で重要なルール:すべてのSELECT/UPDATE/DELETE、バックグラウンドジョブ、ETL、管理ツールの操作は正確に1つのテナントにスコープされていなければならない

テナントスコープがオプションだと、いずれどこかで抜け落ちます。

クロステナントアクセスを引き起こす一般的な失敗モード

クロステナント漏洩は小さな日常的なミスから起きることが多いです:

  • あるエンドポイントやコードパスでテナントフィルタを忘れる
  • 一方のテーブルはスコープされたが、JOIN先がスコープされていない
  • キャッシュがユーザやURLでキー化され、テナントでキー化されていない
  • 準備済みステートメントが間違った tenant_id をバインドする

「テストでは動く」が本番では漏れる理由

テストは通常、データ量が小さく前提がクリーンです。本番では並列処理、リトライ、キャッシュ、複数テナントが混ざったデータ、エッジケースが発生します。テストで一つのテナントだけが存在するために通ってしまう機能があります。最も安全な設計は「スコープされていないクエリを書けない」ようにしておくことです。レビュアに毎回頼る設計は脆弱です。

クロステナントアクセスを防ぐ分離コントロール

マルチテナント環境の核心的リスクは単純です:テナントでフィルタし忘れたクエリが別テナントのデータを露呈してしまうこと。強い分離コントロールはミスを前提とし、それを無害化します。

テナント識別子と厳格なスコーピングパターン

テナント所有のレコードには必ずテナント識別子(例:tenant_id)を持たせ、アクセス層は常にこれで読み書きをスコープするべきです。

実用的パターンは「テナントコンテキストを先に決める」こと:アプリはサブドメイン、組織ID、トークンのclaimsなどからテナントを解決し、リクエストコンテキストに保存して、データアクセスコードはそのコンテキストがないと動かないようにします。

有用なガードレール:

  • 必要に応じて主キー/一意キーに tenant_id を含める(テナント間の衝突を防ぐため)
  • 外部キーにも tenant_id を含めて、クロステナントな関係が勝手にできないようにする

行レベルセキュリティ(RLS)とポリシーベースのアクセス

PostgreSQLなどがサポートする場合、行レベルセキュリティでテナントチェックをデータベース側に移せます。ポリシーは現在のテナントに一致する行だけを可視化するよう制限できます。

これにより「開発者が毎回WHERE句を忘れないことに賭ける」必要が減り、ある種のインジェクションやORMの誤用からも保護されます。RLSは“追加の錠”として使い、唯一の防御にしないでください。

スキーマ/データベース単位での分離

テナントの感度が高い、またはコンプライアンス要件が厳しい場合、スキーマあるいはデータベースで分けることで被害範囲を小さくできますが、運用負荷は増えます。

デフォルト拒否と最小権限

デフォルトを「アクセス不可」にする設計:

  • アプリケーションロールには必要最小限のテーブルアクセスだけ付与する
  • 管理ワークフローでは別アカウントと監査(権限昇格は分離して行う)
  • アプリコードに共有の“スーパーユーザ”コネクションを避ける

これらを組み合わせると、何かが滑ったときに被害が限定されます。

共有ストアの暗号化と鍵管理

暗号化は他の分離層が失敗したときにも有効なコントロールの一つです。共有ストアではデータを「移動中」「保存時」、そして「アプリがどのテナントとして動いているかを証明する際」に保護することが目標です。

通信中および保存時の暗号化

通信中は、クライアント→API、API→データベース、内部サービス呼び出しのすべてでTLSを必須にしてください。可能ならデータベース側で非TLS接続を拒否する設定にして、一時的な例外が恒久化しないようにします。

保存時は、データベースやストレージレベルの暗号化(管理されたディスク暗号化、TDE、暗号化バックアップ)を使います。これはメディア紛失やスナップショット露出、インフラ侵害の一部リスクを軽減しますが、バグのあるクエリが別テナントの行を返すことは防げません。

共有鍵 vs テナント毎の鍵

単一の共有鍵は運用が単純ですが、鍵が漏れた場合の被害範囲が大きくなります。テナント毎の鍵は被害範囲を限定でき、顧客要件にも合うことがありますが、鍵のライフサイクル管理やローテーション、テナントが鍵を無効にした場合のサポートワークフローなど運用が複雑になります。

実用的な折衷はエンベロープ暗号化:マスター鍵でテナント毎のデータ鍵を暗号化し、ローテーションを管理しやすくする方法です。

データベース資格情報のシークレット管理

データベース資格情報はシークレットマネージャに保管し、長期間置かれた環境変数にするべきではありません。短期の資格情報や自動ローテーションを好み、サービスロールでアクセスを限定して、一つのコンポーネントの侵害が全てに繋がらないようにします。

トークンとセッション:偽造テナントコンテキストを防ぐ

テナントIDをセキュリティクリティカルに扱い、クライアントから送られる生のテナントIDをそのまま信頼しないでください。テナントコンテキストは署名トークンやサーバー側の認可チェックに紐付け、各リクエストで検証してください。

監査、モニタリング、インシデント準備

テナント対応の安全なマイグレーションを計画
プランニングモードで本番データに触れる前にマイグレーション、バックフィル、ロールアウト手順を計画します。

マルチテナンシーは「通常値」を変えます。単にデータベースを監視するのではなく、共有システムを複数のテナントが使っており、あるミスがクロステナント露出になる可能性があることを前提に監視する必要があります。優れた監査性とモニタリングはインシデントの発生確率と被害範囲を減らします。

監査ログ:出来事の全体像を記録する

少なくとも、テナントデータを読み、変更し、またはアクセス権を与える可能性のあるすべての操作をログに残してください。最も有用な監査イベントは次に答えます:

  • 誰が:ユーザー/サービスの識別、認証方法、ロール、発信IP/デバイス
  • 何を:操作(SELECT/UPDATE/DELETE)、影響を受けたオブジェクト、クエリの種類(必ずしも完全なSQLではない)、特権変更の前後
  • いつ:タイムスタンプ(タイムゾーン付き)、リクエスト/トレースID
  • テナント:テナントIDを第一級フィールドとして必ず記録する

また、テナント作成や分離ポリシー変更、RLSルールの変更、鍵のローテーション、接続文字列の変更といった管理操作もログに残します。

クロステナントや権限異常のアラート

モニタリングは通常のSaaS利用では起きにくいパターンを検出すべきです:

  • 複数のテナントIDの行を返すクエリ、または急増する「テナント不一致」拒否
  • 通常アクセスしないテナントに対するサービスアカウントのアクセス
  • 急速なロール/権限変更、新規管理者、セキュリティポリシー無効化、RLS迂回の試み

アラートには実行可能なランブックを紐付けてください:確認手順、封じ込め方法、連絡すべき担当者。

管理コントロールとブレイクガラス手順

特権アクセスは本番変更として扱います。最小権限ロール、短期資格情報、重要操作の承認フローを利用してください。緊急時用のブレイクガラスアカウントは別管理の資格情報、必須のチケット/承認、時間制限されたアクセス、強化されたログを設けます。

保持期間とテナントスコープのログアクセス

保持期間はコンプライアンスと調査ニーズに基づいて設定しますが、ログへのアクセスはテナント単位で制限し、サポートスタッフが自分のテナントのログだけを見られるようにします。顧客が監査エクスポートを要求する場合は生ログではなく、テナントでフィルタしたレポートを提供します。

パフォーマンス基礎とノイジーネイバー問題

マルチテナンシーは多くの顧客に同じデータベースインフラを共有させることで効率を高めますが、トレードオフとしてパフォーマンスは共有体験になります:あるテナントの振る舞いが他のテナントに影響を与える可能性があります。

ノイジーネイバー問題(平易な説明)

「ノイジーネイバー」は、あるテナントの活動が非常に重かったりピーキーだったりして、共有リソースを必要以上に消費してしまうテナントです。データベースが壊れているわけではなく、そのテナントの仕事を処理している間に他のテナントの待ち時間が増えるのです。

アパートの水圧に例えると分かりやすい:ある部屋が同時にシャワーと洗濯機を回すと水圧が下がり、他の住人が影響を受ける、という状況です。

実際に共有されているもの

たとえ各テナントが別の行やスキーマを持っていても、多くのパフォーマンスに重要な資源は共有されます:

  • CPU: クエリ実行、ソート、結合、暗号/復号、バックグラウンドメンテ
  • メモリ: バッファ/キャッシュページ、クエリ作業領域、内部キュー
  • ディスク/I/O: データファイルの読み書き、ログ書き込み、チェックポイント、コンパクション/バキューム
  • 接続: データベース接続上限やスレッドプール
  • キャッシュ: プランキャッシュ、バッファキャッシュ、アプリ側キャッシュ

これらの共有プールが飽和すると全体のレイテンシが上がります。

バーストワークロードが他テナントを傷つける理由

多くのSaaSワークロードはバーストで来ます:インポート、月次レポート、マーケティングキャンペーン、毎時のcronなど。

バーストはデータベース内で渋滞を生みます:

  • 単一テナントが多数の高コストクエリを同時に投げてCPUを100%にする
  • 大量の書き込みがI/Oを引き起こし、他の読み取りを遅くする
  • 接続の急増でプールが埋まり、他のテナントが接続できなくなる

バーストが数分でも続けばキューが枯渇するまで影響が続きます。

ユーザーが通常気づくこと

顧客から見た症状はランダムで不公平に感じられます。一般的な症状:

  • ログイン、検索、チェックアウト、レポート作成でのタイムアウト
  • リストビューやダッシュボードなどページの遅延
  • 一貫性のない速度(10:05は速いが10:10に遅くなるなど)
  • バックグラウンドジョブの遅延(エクスポートが遅い、Webhookが遅延する)

これらは「もっとハードウェアを増やす」だけでは根本解決にならないことが多く、パフォーマンス分離技術が必要な早期警告です。

リソース分離とスロットリング技術

マルチテナントSaaSをプロトタイプ
チャットから動作するマルチテナントアプリを作り、本番対応する前に安全に反復できます。

マルチテナンシーは「一つの顧客がデータベース容量を他から借りすぎない」ことが重要です。リソース分離は重いテナントが他を遅らせないためのガードレール群です。

接続プール制限とテナントごとのクォータ

一般的な失敗モードは無制限な接続です:あるテナントのスパイクで何百ものセッションが開き、データベースを枯渇させることがあります。

二箇所でハードキャップを設定します:

  • アプリ側のプール:サービスインスタンスごとの最大接続を制限し、バックグラウンドジョブ用に最低限を確保する
  • テナントごと:プランに応じた「N 同時リクエスト」や「M 同時DBセッション」などのクォータを適用する

データベース側で直接「テナントごとの接続」を強制できなくても、各テナントを専用プールやプールパーティション経由でルーティングすることで近似できます。

レート制限とワークロードシェーピング(アプリ+DB)

レート制限は時間を通じた公平性をもたらします。エッジ(APIゲートウェイ/アプリ)近くで適用し、可能な場合はデータベース側(リソースグループやワークロード管理)でも行います。

例:

  • 高コストエンドポイントに対するテナント別のトークンバケット制限(エクスポート、検索など)
  • インタラクティブリクエストを優先し、バッチを低優先度にする優先度層
  • バーストを平滑化するキュー方式

クエリタイムアウト、ステートメント制限、サーキットブレーカー

データベースを「暴走クエリ」から守る:

  • クエリ/ステートメントタイムアウトで長時間のスキャンを停止する
  • エンドポイントで返す最大行数/バイト数を設定する
  • エラー率やレイテンシが閾値を超えたときに一時的にテナントの高負荷機能をブロックするサーキットブレーカー

これらはフェイル時に明確なエラーと再試行/バックオフの指示を返すべきです。

リードレプリカとキャッシュで競合を減らす

読み取り負荷の多いトラフィックをプライマリから切り離します:

  • リードレプリカをダッシュボードやレポート、分析クエリに使用する
  • キャッシュ(テナントごとのキー、短いTTL)を頻繁なルックアップや設定データに使う

目的は単なる速度向上だけでなく、ロックやCPU競合を減らし、ノイジーネイバーが他に影響を与える経路を減らすことです。

パフォーマンスに影響するデータモデリングの選択

マルチテナントのパフォーマンス問題は「データベースが遅い」という見え方をしますが、原因の多くはデータモデル(テナントデータのキー、フィルタ、インデックス、物理レイアウト)にあります。良いモデリングはテナントスコープのクエリを自然に速くし、悪い設計はデータベースに余計な仕事をさせます。

テナントスコープクエリのためのインデックス設計

多くのSaaSクエリはテナント識別子を含みます。これを明示的にモデル化し(例:tenant_id)、インデックスはそれを先頭に置くように設計します。実務上、(tenant_id, created_at)(tenant_id, status) のような複合インデックスは、単独で created_atstatus にインデックスを張るよりはるかに有用です。

一意性も同様です:メールアドレスがテナント内でのみユニークなら (tenant_id, email) で制約を設けるべきで、グローバルな email 制約は適切ではありません。

フルテーブルスキャンを避ける(テナントフィルタの欠如)

遅いクエリの一般的なパターンは、テナントフィルタを忘れて大きなテーブルにまたがってスキャンすることです。

安全な経路を簡単にする方法:

  • クエリレイヤ(ORMのスコープ、リポジトリメソッド)でテナントフィルタを必須にする
  • データベース側で保護できる場合はデフォルトビューやポリシーを使い、スコープされていないアクセスは速やかに失敗させる

パーティショニングとシャーディング:テナントまたは時間で分割する

パーティショニングは各クエリが見るデータ量を減らせます。テナントが大きく不均一な場合はテナントでパーティションを切ることを検討してください。アクセスが主に最近のデータである場合は時間でパーティションを切り、各パーティション内で tenant_id を先頭にしたインデックスを使います。

単一データベースでピークトラフィックを捌けない、あるいは1つのテナントの負荷が他全員を脅かす場合はシャーディングを検討します。

ホットテナントの管理

“ホットテナント”は読/書のボリューム、ロック競合、大きなインデックスで不均衡を生みます。

テナントごとのクエリ時間、読み取り行数、書き込み率を追跡して発見します。1つのテナントが支配的であれば分離する:別シャード/データベースへ移す、大きなテーブルをテナント別に分割する、専用キャッシュやレート制限を導入して他のテナントの速度を守る、などです。

セキュリティとパフォーマンスを守る運用慣行

マルチテナンシーはデータベース自体が原因で失敗することはまれで、多くは日々の運用での小さな不整合が積み重なってセキュリティギャップやパフォーマンス劣化を招きます。目標は「安全な道」をすべての変更、ジョブ、デプロイでデフォルトにすることです。

テナントキーを標準化し、どこでも強制する

単一の正規テナント識別子(例:tenant_id)を選び、テーブル、インデックス、ログ、APIで一貫して使ってください。整合性があるほどセキュリティミスやパフォーマンスの驚きが減ります。

実用的な対策:

  • すべての主要アクセス経路で tenant_id を要求する(クエリ、リポジトリ、ORMスコープ)
  • 一般的な検索用に tenant_id を先頭にした複合インデックスを追加する
  • 可能ならデータベース制約(tenant_id を含む外部キーやチェック制約)を使って不正な書き込みを早期に検出する

バックグラウンド処理でのテナント混同を防ぐ

非同期ワーカーはしばしばテナントコンテキストを失い、クロステナント事故の原因になります。

有効な運用パターン:

  • ジョブペイロードに tenant_id を明示的に渡す(環境コンテキストに頼らない)
  • idempotencyキーやキャッシュキーにテナントキーを含める
  • ジョブの開始/終了やリトライ時に tenant_id をログに残す

マイグレーションをテナントセーフにする

スキーマやデータマイグレーションは完璧な同時ロールアウトを前提にせずに進められるべきです。

ローリングでの変更を使う:

  • 拡張/縮小戦略(新列/インデックス追加、デュアルライト/デュアルリード、古い経路削除)
  • 長時間ブロッキングする操作を避け、テナントごとにバッチでバックフィルして負荷をコントロールする
  • すべてのバックフィルクエリにテナントスコープとレート制限を設けて自己発生的なノイジーネイバーを防ぐ

分離失敗をテストする(ハッピーパスだけでなく)

意図的に別テナントのデータにアクセスしようとするネガティブテストを追加し、これらをリリースブロッカーにしてください。

例:

  • テナントAの既知レコードをテナントBとしてフェッチしようとして失敗することを確認
  • 不一致の tenant_id を持つバックグラウンドジョブがハードに失敗することをテスト
  • すべてのクエリヘルパーに対してテナントスコープが常に適用される回帰テストを入れる

バックアップ、復元、テナント単位のデータ操作

RLSのガードレールを追加
PostgreSQLのRLSポリシーを設定し、無スコープのクエリをデフォルトで失敗させます。

バックアップは「データベースをコピーする」と説明するのは簡単ですが、マルチテナント環境では安全に実行するのが意外に難しいです。多くの顧客がテーブルを共有している瞬間、1つのテナントだけを復元する方法や他テナントを露出させずに復元する計画が必要になります。

バックアップ/復元戦略:1テナント対全体

フルデータベースバックアップは災害復旧の基礎ですが、日常のサポートケースには不十分なことが多いです。一般的なアプローチ:

  • フルバックアップ + 時点復旧:全体的な障害やリージョン障害に対応
  • テナントスコープのエクスポートtenant_idでフィルタした論理ダンプ):単一テナントの復元用
  • テナントごとの別ストレージ(可能な場合):復元が自然にテナント境界内に収まる

論理エクスポートに頼る場合、エクスポートジョブ自体を本番コードと同等に扱い、WHERE句を一度書いて終わりにするのではなく、RLS等で分離を強制するなどして安全性を確保してください。

テナントレベルのエクスポート/削除(プライバシー要求)

エクスポートや削除はセキュリティとパフォーマンスに関わるテナント単位の操作です。以下を持つ再現可能で監査可能なワークフローを構築してください:

  • テナントデータを一貫したスナップショットとしてエクスポートする方法
  • 孤立行を残さずにテナントデータを削除する手順
  • ログやチェックサムで完了を証明する手順

誤ったクロステナント復元を防ぐ

最大のリスクはハッカーではなく急いだオペレータです。人的ミスを減らすガードレール:

  • テナント識別子に加えて二次確認(テナント名や請求ID)を要求する
  • インポート前に行数と tenant_id の分布を検証する
  • まず隔離環境に復元して確認してから本番へ昇格する

DR演習と事後の境界検証

災害復旧訓練後は「アプリが立ち上がった」で終わらせないでください。テナント分離を確認する自動化チェックを実行:テナント横断サンプルクエリ、監査ログのレビュー、暗号鍵やアクセスロールが正しくスコープされているかのスポット検証などを行います。

マルチテナンシーが適さなくなるとき

マルチテナンシーは多くのSaaSでデフォルトとして有効ですが、永続的な決定ではありません。プロダクトや顧客構成が進化すると、「1つの共有データストア」アプローチがビジネスリスクや開発の足かせになることがあります。

分離を強めるべきシグナル

以下が継続的に見られる場合、より分離を進める検討を:

  • 成長とスケールの影響:一部のテナントがトラフィックやストレージ、バックグラウンドジョブの大半を占め、全体のチューニングが難しくなる
  • コンプライアンスや契約要件:専用環境、居住要件、鍵の所有など共有モデルで満たせない要件が増える
  • 特定テナントの重いワークロード:大規模インポートやレポート負荷、カスタム統合が頻繁に発生し、スロットリングやチューニングでは抑えられない

コストと複雑さをステークホルダに説明する

より分離すると一般にインフラコストが上がり、**運用オーバーヘッド(マイグレーション、監視、オンコール)**が増え、リリース調整が必要になります。対価として、パフォーマンス保証が明確になり、コンプライアンス上の議論がしやすくなります。

次のステップ

分離オプションを評価するなら、/blog の関連ガイドを確認するか、/pricing でプランとデプロイオプションを比較してください。

すぐにSaaSをプロトタイプしてマルチテナンシーの仮定(テナントスコーピング、RLSに優しいスキーマ、スロットリング、運用ワークフロー)を早期に試したい場合は、Koder.ai のようなvibe-codingプラットフォームを使うと、チャットから React + Go + PostgreSQL の動くアプリを立ち上げ、計画モードで反復し、スナップショットとロールバックでデプロイし、準備が整ったらソースコードをエクスポートして本番アーキテクチャを強化できます。

よくある質問

What is a multi-tenant database in plain terms?

マルチテナントデータベースは、複数の顧客が同じデータベース基盤(しばしば同じスキーマ)を共有しつつ、アプリケーションやデータベース側でそれぞれのテナントが自分のデータにのみアクセスできるように強制する仕組みです。核心は、すべての読み書きで厳密なテナントスコープを適用することです。

Why do SaaS teams choose multi-tenancy?

マルチテナンシーが選ばれる主な理由は次の通りです:

  • 顧客あたりのコスト削減(計算・ストレージ・ライセンス・運用工数の共有)
  • 大規模運用が簡素化されること(パッチ適用や監視を行うデータベースが少ない)
  • オンボーディングの高速化(顧客ごとに完全なデータベーススタックをプロビジョニングする必要がない)

トレードオフとして、分離とパフォーマンスのガードレールを意図的に設計する必要があります。

What are the main multi-tenant database models?

一般的なモデル(分離が強い順に):

  • Database-per-tenant: 最も強い分離境界だが運用負荷が大きい。
  • Schema-per-tenant: 十分な分離を提供するがマイグレーションは繰り返し必要。
  • Table-per-tenant: 少数のテナントなら機能するがスケールしにくい。
  • Shared-table(tenant_idカラム): 運用は最も簡単だがセキュリティやチューニングは難しい。

選択は分離境界と運用負担を決めます。

How does multi-tenancy change the security threat model?

リスクは外部攻撃だけではなく、日常的なミスで別テナントのデータにアクセスしてしまうことに移ります。tenant_id のようなテナントコンテキストは、オーセンティケーションの結果ではなく、常に認可の要件として扱う必要があります。実運用では並行処理、キャッシュ、リトライ、バッチ処理などがあり、これらを前提に設計しなければなりません。

What typically causes cross-tenant data leaks?

典型的な原因は次のとおりです:

  • あるエンドポイントやコードパスでテナントフィルタが抜けている
  • 片方のテーブルはスコープされているが、JOIN先がされていない
  • URLやユーザーでキャッシュしていてテナントでキー化していない
  • 準備済みステートメントで間違った tenant_id をバインドしている
  • バックグラウンドジョブでテナントコンテキストを失っている

設計上、スコープされていないクエリが実行されにくい仕組みにすることが重要です。

When should you use row-level security (RLS), and what does it protect against?

RLS(行レベルセキュリティ)は、データベース側にテナントチェックを移せる強力な手段です。SELECT/UPDATE/DELETEを現在のテナントに一致する行だけに制限するポリシーを定義できます。ただし、RLSは“もう一つの鍵”と考え、アプリ層でのスコーピング、最小権限、十分なテストと組み合わせて使うべきです。単独に頼らないでください。

What are the most important isolation controls to prevent cross-tenant access?

実用的なベースライン例:

  • テナント所有テーブルに正規の tenant_id を持たせる
  • tenant_id を含む複合ユニーク制約や外部キーを使う
  • デフォルト拒否の権限設計と最小権限のDBロール
  • 監査された管理アクセス(アプリコードにスーパーユーザを使わない)
  • クロステナントの読み書きを試すネガティブテスト

ミスが起きても安全に失敗することを目指します。

How does encryption and key management work in a shared datastore?

暗号化は失敗したときのダメージを限定する手段です:

  • 通信中(TLS): サービス間のすべてのホップでTLSを必須にする
  • 保存時: データベースやストレージの暗号化(TDEや暗号化されたバックアップ)を使う
  • テナントごとの鍵は影響範囲を小さくするが運用は複雑になる

また、クライアントから送られる生のテナントIDを信頼せず、署名トークンやサーバー側での検証と紐付けを行ってください。

What is the noisy neighbor problem, and how do you mitigate it?

ノイジーネイバーは、あるテナントの負荷(CPU、メモリ、I/O、接続など)が共有リソースを使い切り、他のテナントのレイテンシを上げる現象です。対策の例:

  • ハードな接続プール上限とテナントごとのクォータ
  • 高負荷エンドポイントのレート制限やワークロードシェーピング
  • クエリタイムアウト、最大行数/バイト数制限、サーキットブレーカー
  • 読み取りをレプリカやキャッシュに振る

目標はスループットだけでなく“公平性”です。

When should you move away from full multi-tenancy, and what hybrid options exist?

以下の状況が継続して起きるなら分離を強める検討を:

  • 一部のテナントがトラフィックやストレージの大半を占め、他を阻害する
  • 専有環境やキー管理、居住要件などコンプライアンス上の要求がある
  • 大量インポートやカスタム統合などのパターンで頻繁に競合が起きる

ハイブリッドの選択肢:トップティアだけ専用データベースに切り出す、共有と専用のプランを用意する、トランザクションは共有で分析だけ別にする等です。

Related posts