OLAPとOLTPの違い
分析基盤の設計を任されたとき、なぜ本番DBをそのまま集計に使うと詰まるのかがわかります。取引系と分析系のワークロード特性の根本差、スタースキーマとディメンションの狙い、列指向がなぜ分析に効くのかを原理から解説します。
- 1.OLTP(取引系)は多数の短いトランザクションで少数行を更新する『書き込み・点アクセス』主体、OLAP(分析系)は少数の巨大クエリで多数行を集約する『読み取り・スキャン』主体。アクセスパターンが正反対なので、同じテーブル設計・同じ物理レイアウトでは両立できず、分析基盤は別系統に切り出す。
- 2.分析基盤の標準形はスタースキーマで、数値の事実を持つ巨大なファクト表を、属性を持つ小さなディメンション表が囲む。正規化を意図的に崩してディメンションを非正規化することで、分析クエリの結合数を減らし、フィルタ・グループ化を人が理解できる属性軸に沿わせる。
- 3.分析クエリは『全行の一部の列を舐めて集約する』ため、行ごとに全列を連続配置する行指向より、列ごとに値を連続配置する列指向が圧倒的に有利。必要な列だけを読んでI/Oを削り、同種の値が並ぶことで高い圧縮率とベクトル化実行が効く。集約中心の設計と列指向は相互に噛み合う。
二つのワークロードは正反対
同じ「データベースを使う処理」でも、OLTP(Online Transaction Processing、取引系) と OLAP(Online Analytical Processing、分析系) はアクセスパターンが真逆です。ここを取り違えると、本番の取引DBに重い集計クエリを投げてサービスごと詰まらせる、という典型的な事故になります。
OLTPは、注文の確定・在庫の減算・残高の更新のような多数の短いトランザクションです。1件あたりが触るのは主キーで特定した少数の行で、読み書きが混在し、応答は数ミリ秒が期待されます。「今この1件を正しく更新する」が主役なので、点アクセス(point access)と整合性が最優先です。
OLAPは、「先月の地域別・商品カテゴリ別の売上」のような少数の巨大なクエリです。1本が数百万〜数十億行を走査(スキャン)して集約し、更新はほぼ行わず一括ロードで積む。応答は秒〜分でも許容され、代わりにスループット(一度に舐める量)が主役です。
| 観点 | OLTP(取引系) | OLAP(分析系) |
|---|---|---|
| アクセス単位 | 主キーで少数行を点アクセス | 多数行を範囲スキャンして集約 |
| 読み書き比率 | 読み書き混在・更新が中心 | ほぼ読み取り専用・一括ロードで追加 |
| 1クエリが触る列 | その行の全列(幅狭・全項目) | 全行のうち少数の列(幅広だが列は限定) |
| 同時実行数 | 多数(多ユーザーの短い処理) | 少数(重いバッチ・BI) |
| 最適化の主眼 | 低レイテンシと整合性 | 高スループットとスキャン効率 |
| 典型的な鮮度 | 最新(コミット即反映) | 分〜時間単位の遅延を許容 |
この差は好みではなく物理です。点アクセスに最適な物理レイアウトと、全走査集約に最適なレイアウトは両立しないため、分析系は取引系から別基盤(データウェアハウス/レイクハウス)へ切り出すのが定石になります。DB内部のインデックス構造やトランザクション制御は取引系の関心で、ここでは扱いません(内部原理は/database/側の主題です)。本稿は分析基盤としての設計、すなわちデータのモデリングと物理配置に絞ります。
なぜ取引系スキーマを分析にそのまま使えないのか
OLTPのスキーマは正規化されています。同じ事実を一箇所だけに持ち、更新時の不整合(更新異常)を防ぐためです。顧客・注文・注文明細・商品・カテゴリ…と表が細かく分かれ、外部キーで結ばれる。1件の更新はごく一部の表に閉じるので、正規化は取引系にとって正しい選択です。
ところが分析クエリでは、この正規化が牙をむきます。「地域別・カテゴリ別の売上」を出すだけで、注文明細から商品、商品からカテゴリ、注文から顧客、顧客から地域…と多数の表を結合する羽目になる。結合の数と中間結果の膨張がクエリを重くし、しかも分析者は業務の正規化構造を暗記しないと問い合わせを書けません。
正規化はデータの重複を消して書き込み時の整合性を守る技術で、OLTPの世界観そのものです。一方の分析基盤は、いったんロードしたら基本的に更新しません。更新異常のリスクが小さいなら、重複を許して読み取り時の結合を減らす(非正規化する)ほうが合理的です。OLAPスキーマ設計は、この「読み取り最適化への振り切り」を前提に組み立てます。
スタースキーマ:ファクトとディメンション
分析基盤の標準形がスタースキーマ(星型スキーマ) です。中心にファクト表(fact table)、その周囲にディメンション表(dimension table) を放射状に配置するのでこの名がつきます。
- ファクト表は、測定したい数値の事実(メジャー) を1行1イベントで持ちます。売上なら「いつ・どの店で・どの商品を・いくつ・いくらで売れたか」の金額や数量です。行数は膨大(億単位もざら)だが、列は「各ディメンションを指す外部キー+数個のメジャー」と幅は狭い。
- ディメンション表は、事実を切る軸となる属性を持ちます。商品ディメンションなら商品名・カテゴリ・ブランド・仕入先、日付ディメンションなら年・四半期・月・曜日・祝日フラグ。行数は小さい(商品数、日数)が、列は多く記述的です。
┌─────────────┐
│ dim_date │ ← 年/四半期/月/曜日/祝日…
└──────┬──────┘
┌─────────────┐ ┌──▼──────────────┐ ┌─────────────┐
│ dim_product ├──►│ fact_sales │◄──┤ dim_store │
│ 商品/分類 │ │ (数量, 金額, │ │ 店舗/地域 │
└─────────────┘ │ 各dimへのFK) │ └─────────────┘
┌───┤ ├───┐
┌────────▼─┐ └─────────────────┘ ┌─▼──────────┐
│dim_customer│ │dim_promo │
└────────────┘ └────────────┘
分析クエリはいつも同じ形になります。ファクトを対象に、いくつかのディメンションを結合し、ディメンションの属性で絞り込み(WHERE)、別の属性でまとめて(GROUP BY)、メジャーを集約(SUM/COUNT/AVG)する。スタースキーマはこの定型を最短にします。
-- 「2026年Q1・地域別・カテゴリ別の売上合計」の骨格
SELECT s.region, p.category, SUM(f.amount) AS sales
FROM fact_sales f
JOIN dim_store s ON f.store_id = s.store_id
JOIN dim_product p ON f.product_id = p.product_id
JOIN dim_date d ON f.date_id = d.date_id
WHERE d.year = 2026 AND d.quarter = 1
GROUP BY s.region, p.category
結合はすべて巨大なファクト表 対 小さなディメンション表の形になり、結合の相手が小さいので実行計画が単純で速い。ディメンションを非正規化(カテゴリを別表に切り出さず商品ディメンションに畳み込む)しておけば結合数はさらに減ります。これがスノーフレークスキーマ(ディメンションをさらに正規化した形)よりスタースキーマが好まれる理由です。結合を1段でも減らすほうが、正規化の綺麗さより分析では価値が高い。
ディメンションは「その事実が起きた時点の属性」を表せる必要があります。顧客が引っ越して地域が変われば、過去の売上は当時の地域で集計したい。属性変更の履歴をどう保持するかを整理したのが緩やかに変化するディメンション(SCD, Slowly Changing Dimension) で、上書きするType 1、変更のたびに行を足して履歴を残すType 2などがあります。分析の正しさは、ファクトの数値だけでなくディメンションの時間的正しさにも依存します。
集約中心の設計とグレイン
スタースキーマ設計で最初に決めるべきはグレイン(grain、ファクト1行が表す粒度) です。「注文明細1行=1グレイン」なのか「1日1店舗1商品の集計=1グレイン」なのか。グレインを細かく取るほど後から自由に集計できるが行数は増える。粗く取れば軽いが、それより細かい問いには答えられません。分析基盤の設計は**「どの粒度まで事実を残すか」の意思決定**であり、集約の自由度とコストのトレードオフです。
よく効くのが事前集約(pre-aggregation) です。日次・月次のようによく使う粒度をあらかじめ集計してサマリー表(マテリアライズドビュー) に持てば、BIの応答が桁で速くなる。素朴なOLAPキューブは、ディメンションの組み合わせごとに合計を先に計算しておく発想の極端形です。ただし事前集約はストレージと再計算コストと引き換えで、元データが更新されたらサマリーも作り直しが要る。「どの集約を実体化し、どれを都度計算するか」は分析基盤の中心的なチューニングになります。
列指向がなぜ分析に効くのか
ここまでの設計(少数列を全行スキャンして集約する)は、物理的な行の並べ方と深く噛み合います。分析基盤が列指向(columnar)ストレージを採るのはこのためです。
- 行指向(row-oriented):1行の全列を連続してディスクに置く。
[行1の全列][行2の全列]…。1件の全項目をまとめて読み書きする点アクセス、つまりOLTPに向く。 - 列指向(column-oriented):同じ列の値を全行ぶん連続して置く。
[全行のamount][全行のqty][全行のdate_id]…。特定の列だけを舐める集約、つまりOLAPに向く。
分析クエリが SUM(amount) を求めるとき、行指向だと各行の全列をディスクから読み込んで amount 以外を捨てることになります。ファクト表が数十列あって使うのが2〜3列なら、大半のI/Oが無駄です。列指向なら必要な列のブロックだけを読むので、I/O量が「使う列の割合」まで落ちます。これが列プルーニング(column pruning)で、スキャン主体のワークロードに直接効きます。
列指向のもう一つの威力は、同じ列=同じ型・似た分布の値が隣接することです。ここに辞書エンコード(頻出値を整数IDに置換)、ランレングス(連続する同値をまとめる)、ビットパッキングなどが強烈に効き、圧縮率が上がってI/Oがさらに減ります。加えて、同型の値が連続するのでCPUはベクトル化(SIMDで1命令が複数値を処理) しやすく、集約が速い。列指向は「読む量を減らす」と「読んだ後の処理を速くする」を同時に満たします。圧縮方式や物理フォーマットの詳細は/database/側で個別に掘れます。
逆に、なぜOLTPで列指向を使わないかも同じ理屈です。1行を挿入するのに全列のブロックへ書き分ける必要があり、1行更新も列ごとに分散した位置を触る。点書き込みの多い取引系では行指向が有利で、列指向は「一括ロード+読み取り主体」を前提にして初めて勝てる。近年のHTAP(取引と分析の融合)は、書き込みは行指向で受け、バックグラウンドで列指向へ変換する二層構成でこの矛盾を回避しますが、根っこにあるのは本稿のOLTP/OLAP物理差そのものです。
まとめ
- OLTPは点アクセス・書き込み・低レイテンシ、OLAPはスキャン・集約・高スループットでアクセスパターンが正反対。物理レイアウトが両立しないため、分析系は取引系DBから別基盤に切り出すのが原則。
- 取引系は正規化で書き込み整合性を守るが、分析ではその構造が結合地獄を生む。分析基盤は非正規化して読み取りを最適化する。
- 標準形はスタースキーマ。数値の事実を持つ巨大なファクト表を、属性軸を持つ小さなディメンション表が囲み、分析クエリを「フィルタ→グループ化→集約」の定型に落とす。
- 設計の起点はグレインの決定で、集約の自由度とコストのトレードオフ。よく使う粒度は事前集約(サマリー表) で応答を稼ぐ。
- 「少数列を全行スキャン」する分析には列指向が本質的に合う。列プルーニングでI/Oを削り、同種の値の隣接で高圧縮とベクトル化が効く。集約中心の設計と列指向は相互に噛み合う。
データ工学 Article
OLAPとOLTPの違いを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
OLAP
比較で見る軸
難易度: advanced / カテゴリ: データ工学 / タグ数: 6
導入後に効く点
分析基盤の標準形はスタースキーマで、数値の事実を持つ巨大なファクト表を、属性を持つ小さなディメンション表が囲む。正規化を意図的に崩してディメンションを非正規化することで、分析クエリの結合数を減らし、フィルタ・グループ化を人が理解できる属性軸に沿わせる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データ工学
- タグ数
- 6
判断チェックリスト
- 自社の用途が「OLAP / OLTP」に近いか確認する。
- 強みである「OLTP(取引系)は多数の短いトランザクションで少数行を更新する『書き込み・点アクセス』主体、OLAP(分析系)は少数の巨大クエリで多数行を集約する『読み取り・スキャン』主体。アクセスパターンが正反対なので、同じテーブル設計・同じ物理レイアウトでは両立できず、分析基盤は別系統に切り出す。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。