【実務・中級編】 JSON型の利用 – Cloud Spanner

Cloud SpannerにおけるJSON型:その「甘い誘惑」と「真の設計思想」

Cloud SpannerにJSON型が導入されたことで、かつての「厳格なリレーショナルモデルか、それともNoSQLの柔軟性か」という二元論的な苦悩は過去のものとなった。しかし、この強力な武器を手に、脳死で何でもかんでもJSONに突っ込む設計は、Spannerの真価を殺す愚行に他ならない。

チーフアーキテクトとして、現場の設計レビューで必ず突きつける「JSON型を正しく使いこなし、スケーラビリティを損なわないための指針」を伝授する。

—

1. JSON型の本質:静的なスキーマの「外縁」を拡張する

SpannerのJSON型は、単なるテキストのラッパーではない。内部的に効率的なバイナリ形式で保持され、クエリエンジンによってネイティブに最適化されている。

定義と挿入の基本

JSONカラムは、他のカラムと同様にテーブル定義に組み込める。

CREATE TABLE UserProfiles (
UserId INT64 NOT NULL,
— 拡張性の高い属性をJSONで保持
Metadata JSON,
) PRIMARY KEY (UserId);

— 挿入例
INSERT INTO UserProfiles (UserId, Metadata)
VALUES (1, JSON ‘{“theme”: “dark”, “preferences”: {“notifications”: true, “lang”: “ja”}}’);

設計の極意: JSONカラムは「変化が激しいが、クエリの検索条件としては限定的にしか使わない」データのためにある。頻繁にJOINやフィルタリングの条件にするなら、それはJSONではなく、正規化されたカラムとして切り出すべきだ。

—

2. JSONパス式:柔軟性とパフォーマンスのトレードオフ

JSON内の特定の要素にアクセスするには `JSON_VALUE` や `JSON_QUERY` を使用する。

— JSONパス式による値の抽出
SELECT
JSON_VALUE(Metadata, ‘$.theme’) AS theme
FROM UserProfiles
WHERE JSON_VALUE(Metadata, ‘$.preferences.notifications’) = ‘true’;

ここでエンジニアが陥る罠が、「JSONパス式をWHERE句で乱用する」ことだ。
JSONパスによる抽出は、フルスキャンが発生しやすい。インデックスを貼っていない状態でこのクエリを投げれば、大規模データセットでは即座にレイテンシの死を招く。

—

3. インデックス戦略:JSONの「深部」を高速化する

JSON内の特定の値に対して高速な検索を行いたい場合、「生成カラム(Generated Columns)」を活用し、そこにインデックスを貼るのがSpanner流の定石だ。

— 1. JSONから特定要素を抽出する生成カラムを定義
ALTER TABLE UserProfiles
ADD COLUMN Theme STRING(20) AS (JSON_VALUE(Metadata, ‘$.theme’)) STORED;

— 2. その生成カラムにインデックスを貼る
CREATE INDEX Idx_UserProfile_Theme ON UserProfiles(Theme);

アーキテクトの視点: なぜ直接JSONにインデックスを貼らないのか? Spannerの生成カラムはデータ整合性を担保したまま、特定のパスを実体化(Materialized)できるからだ。これにより、クエリエンジンはJSONパースという高コストな処理をバイパスし、通常のB-Treeインデックスと同じ速度で検索を実行できる。

—

4. 実務で守るべき「3つの鉄則」

現場でJSON型を扱う際、以下の原則を破る者はコードレビューで容赦なく差し戻す。

1. JSONを「巨大なゴミ箱」にしない
JSONサイズの上限は10MBだが、上限ギリギリまで詰め込むのは設計の敗北だ。JSONはあくまで「メインのスキーマを補完する補助的な属性」に留めること。
2. 書き込みの競合を考慮する
JSON全体を更新する場合、`JSON_SET` 等で一部更新を行っても、内部的には行レベルのロックが走る。頻繁に更新されるJSONデータは、トランザクションの競合頻度を高める原因になる。
3. バリデーションを疎かにしない
JSON型はスキーマレスゆえに、アプリケーション側で「期待する構造」が壊れていてもDBは受け入れてしまう。DB層で防げない型崩れは、必ずアプリケーションのドメイン層で厳密に型定義(TypeScriptのインターフェースやProtocol Buffersなど)を行い、シリアライズ前にバリデーションを完結させること。

—

結びに代えて

Cloud SpannerのJSON型は、リレーショナルデータベースの堅牢性と、NoSQLの自由度を接続する最強のブリッジだ。しかし、武器の切れ味は使い手の腕に依存する。

「JSONでいいか」という安易な妥協ではなく、「ここだけはJSONであるべき合理的な理由」を説明できるか。その自問自答こそが、あなたの設計を一段上のレベルへ引き上げる。

さあ、コードを開け。設計を見直せ。Spannerの性能を極限まで引き出すのは、ツールではなく、あなたの論理的な設計判断だ。

コメント

タイトルとURLをコピーしました