目次を表示する

DB 設計の軸 2026 ─ ドメイン駆動と特性駆動の二つの流派を行き来する 19 章

NewSQL 設計の意思決定 ─ Region 配置・Locality・Consistency level

NewSQL 設計の意思決定

NewSQL を採用すると決めた後の、実際の設計の 5 つの意思決定を扱う。

判断 1:Region 配置の決定

地理分散の最初の判断は「どこのリージョンに何を置くか」。

主要パターン

パターン配置用途
Region-restricted1 region のみ単一国向けサービス
Multi-region active-passive主 1 region + 従 1-2 region災害復旧(DR)
Multi-region active-active全 region で R/Wグローバル SaaS
Tenant-pinnedtenant ごとに region を固定データ主権要件
Geo-partitionedデータを地理に応じて partitionEU/US でデータを分ける

CockroachDB の Locality 設定

CockroachDB は Locality-Aware Replication を持つ:

-- 各ノード起動時
cockroach start --locality=region=us-east1,zone=a
cockroach start --locality=region=us-east1,zone=b
cockroach start --locality=region=eu-west1,zone=a

-- テーブル単位で配置を制御
ALTER TABLE users CONFIGURE ZONE USING constraints = '[+region=us-east1]';
ALTER TABLE eu_users CONFIGURE ZONE USING constraints = '[+region=eu-west1]';

これで「特定のテーブル / Range は特定の region に固定」できる。データ主権要件(GDPR で EU データを EU に置く必要がある等)に応える。

配置と Aggregate

Aggregate は 同じ region に置く のが基本。Aggregate 内の atomic 操作で region 跨ぎが起きると、レイテンシが激増する。

-- ❌ Order と OrderItem が違う region
ALTER TABLE orders CONFIGURE ZONE USING constraints = '[+region=us-east1]';
ALTER TABLE order_items CONFIGURE ZONE USING constraints = '[+region=eu-west1]';
-- → JOIN や Trans が地理跨ぎ → 数百 ms のレイテンシ

-- ✅ Aggregate を 1 region に固定
ALTER TABLE orders CONFIGURE ZONE USING constraints = '[+region=us-east1]';
ALTER TABLE order_items CONFIGURE ZONE USING constraints = '[+region=us-east1]';

判断 2:Locality の選択 ─ どの Read を許容するか

NewSQL は Read を多様にできる

Strong Read(Linearizable)

-- デフォルト:常に最新を読む
SELECT * FROM users WHERE id = 1;
-- → 全 region 跨ぎで最新を保証、数十 ms

Stale Read / Follower Read

-- フォロワーから読む(少し古い)
SELECT * FROM users AS OF SYSTEM TIME '-10s' WHERE id = 1;
-- → 最寄りの replica から読める、ms オーダー

近隣 follower から読むことで、地理的レイテンシを劇的に削減できる。代償は数秒前のデータを読む可能性。

用途別の選択

用途Locality
決済、認可Strong(最新必須)
ユーザープロファイル表示Stale 可
商品カタログStale 可 + Cache
在庫数用途によるが Strong 寄り
分析ダッシュボードStale 可(数分単位)

判断 3:Consistency Level の使い分け

CockroachDB / Spanner では transaction の isolation level も選べる:

Level特徴用途
Serializable完全な isolation、最も安全デフォルト推奨、特に金融
Snapshotスナップショット時点の一貫性読み中心
Read Committed緩いが性能が良いレポート、分析

CockroachDB は デフォルトが Serializable(Spanner も)。これは PostgreSQL のデフォルト Read Committed と異なる。

Serializable のコスト

Serializable は読み書き両方を厳密に管理するため、競合時に retry が起きる

BEGIN;
SELECT balance FROM accounts WHERE id = 1;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
-- → 別の Trans が同じ行を触っていたら、こちらが retry される

retry が頻繁に起きる workload(hot row 系)では性能が出ない。Hot row 回避(次節)が NewSQL でも重要

判断 4:Hot Row 回避(再び)

第 5 章 OLTP で扱った Hot Row 問題は、NewSQL でより深刻になる。地理跨ぎでロック競合するとレイテンシが致命的。

NewSQL での緩和策

-- ❌ 単一行の counter
UPDATE event_counts SET count = count + 1 WHERE name = 'orders';

-- ✅ Sharded counter
INSERT INTO event_counts_sharded (name, shard, count)
VALUES ('orders', 0..N, 0)
ON CONFLICT DO UPDATE SET count = count + 1
WHERE shard = (random() * N)::int;

CockroachDB には UNIQUE 制約付きの auto increment ID で Hot row が起きる問題 に対し、unique_rowid() という関数がある(時系列 + ノード ID + counter で一意)。これは UUIDv7 の発想と近い。

判断 5:Aggregate と地理の整合

最後の判断:Aggregate の境界と地理の境界を一致させる

例:マルチテナント SaaS

-- tenant ごとに region を固定
CREATE TABLE orders (
  tenant_id UUID,
  order_id UUID,
  ...
  PRIMARY KEY (tenant_id, order_id)
) LOCALITY REGIONAL BY ROW ON tenant_id;

CockroachDB の REGIONAL BY ROW は、行ごとに region を持たせる機能。tenant の region に合わせてデータが配置される。

これで:

  • EU の tenant の data は EU に(GDPR 対応)
  • 同じ tenant の Aggregate は同 region(atomic 操作が低レイテンシ)
  • tenant 横断のクエリは複数 region 跨ぎ(許容できる頻度)

Aggregate を「region 内に閉じる」設計

// Order Aggregate は常に 1 region 内
class Order {
  tenant_id: TenantId;  // region を決める
  order_id: OrderId;
  items: OrderItem[];   // 同一 region
  shipping: Shipping;   // 同一 region
}

// Tenant Aggregate(region 跨ぎではない)
class Tenant {
  tenant_id: TenantId;
  region: Region;
  // ...
}

// 異なる Tenant 間の集計は、後段の OLAP で

これは DDD の Bounded Context地理の Locality が綺麗に対応する例。Bounded Context が地理に紐づくなら、Aggregate も地理に紐づける。

NewSQL 設計の意思決定マトリクス

graph TB
  Q1[Region 配置] --> A1[Aggregate を 1 region に閉じる]
  Q2[Read Locality] --> A2[Strong / Stale を用途で使い分け]
  Q3[Consistency Level] --> A3[デフォルト Serializable]
  Q4[Hot Row 回避] --> A4[Sharded counter / UUIDv7 系 ID]
  Q5[Aggregate と地理] --> A5[REGIONAL BY ROW で行レベル制御]

  style Q1 fill:#e1f5ff
  style Q2 fill:#e1f5ff
  style Q3 fill:#e1f5ff
  style Q4 fill:#e1f5ff
  style Q5 fill:#e1f5ff

ドメインから見た NewSQL

NewSQL は OLTP の地理分散版 として、ドメイン側からは “PostgreSQL の上位互換” のように見える。SQL も Repository パターンも素直に乗る。

ただし Aggregate のサイズと地理性 が、これまでの DB より強い設計制約を持ち込む。1 つの Aggregate が複数 region のデータに依存すると、レイテンシで破綻する。

1 Aggregate = 1 region」という暗黙のルールが NewSQL には宿る。これは「1 Aggregate = 1 Trans」の地理分散版。

この章の要点

  • Region 配置: Aggregate は 1 region に閉じる、Tenant は region に固定
  • Locality: Strong Read と Stale Read を用途で使い分け、地理レイテンシを節約
  • Consistency Level: デフォルト Serializable、retry コストを意識
  • Hot Row 対策は OLTP より重要。地理跨ぎロックを避ける
  • REGIONAL BY ROW で行レベルの地理制御
  • Aggregate と地理の境界を一致させると DDD と整合する

次章への問いかけ

ここまで 6 つのワークロードを 12 章で見てきた。次に踏み込むのがドメイン側で慣れた人ほど不慣れになる領域 ── ドメインから使われる側、すなわち共通基盤としての DB 設計

Stripe Idempotency / Multi-tenant / Schema 進化 / Hot tenant / 監査 / クォータ。それぞれにある “外さない作法” を集中して扱う。