NewSQL 設計の意思決定
NewSQL を採用すると決めた後の、実際の設計の 5 つの意思決定を扱う。
判断 1:Region 配置の決定
地理分散の最初の判断は「どこのリージョンに何を置くか」。
主要パターン
| パターン | 配置 | 用途 |
|---|---|---|
| Region-restricted | 1 region のみ | 単一国向けサービス |
| Multi-region active-passive | 主 1 region + 従 1-2 region | 災害復旧(DR) |
| Multi-region active-active | 全 region で R/W | グローバル SaaS |
| Tenant-pinned | tenant ごとに region を固定 | データ主権要件 |
| Geo-partitioned | データを地理に応じて partition | EU/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 / 監査 / クォータ。それぞれにある “外さない作法” を集中して扱う。