---
title: "見つけられないものは UPDATE できない:ClickHouse vs PostgreSQL"
date: "2026-09-30T05:04:09.283Z"
author: "Al Brown and Tom Schreiber"
category: "Engineering"
excerpt: "同じデータとハードウェア環境で、ClickHouse の更新速度が PostgreSQL と比較してどのような性能を発揮するのかを検証します。"
---

# 見つけられないものは UPDATE できない:ClickHouse vs PostgreSQL

> **TL;DR**<br/><br/>· 同一のハードウェアとデータにおいて、ClickHouse は一括 UPDATE で**最大 4,000 倍高速**であり、**単一行の更新では同等**の水準に達し、**キャッシュされたポイント更新でも競争力**を維持しています。<br/><br/>· 重要である理由: 一括更新は OLTP ワークロードで一般的であり、ClickHouse の列指向設計と並列処理によりはるかに高速に処理されます。<br/><br/>· 注意点: PostgreSQL はデフォルトで完全なトランザクション性を備えていますが、ClickHouse はそうではありません。結果は同一の保証ではなく、それぞれのネイティブな実行モデルを反映したものです。

PostgreSQL は、世界で最も人気のあるオープンソースの [OLTP データベース](https://clickhouse.com/resources/engineering/oltp-vs-olap)です。

開発者が UPDATE の性能を考えるとき、おそらく PostgreSQL が示す基準を思い浮かべるのが自然でしょう。

対照的に、ClickHouse は世界で最も人気のあるオープンソースの OLAP データベースです。

ClickHouse は OLAP 性能の期待値を確立している一方で、その OLTP のポテンシャルは過小評価されがちです。

2025年7月、[ClickHouse v25.7 により、高性能な SQL 標準の UPDATE が ClickHouse に導入されました](https://clickhouse.com/ja/blog/updates-in-clickhouse-2-sql-style-updates-jp)。

当然ながら、ClickHouse が最高峰の製品と比べてどうなのかが気になるところです。**ClickHouse の UPDATE は Postgres の UPDATE に対抗できるのでしょうか？**

本記事では、まずデータがキャッシュされていないストレステストのシナリオであるコールド実行を取り上げ、その後にインデックスとキャッシュのヒット率が高い日常のワークロードをよりよく反映したホット実行で補足します。

## PostgreSQL と ClickHouse の比較は単純な比較ではない

前回の記事では、[新しい高性能な SQL 標準 UPDATE と他の ClickHouse 更新手法のベンチマークを実施しました](https://clickhouse.com/ja/blog/updates-in-clickhouse-3-benchmarks-jp)。今回は関心から、同一のハードウェアとデータ上で PostgreSQL を相手に同じベンチマークを実行しています。

ここでは、古典的な注文データセットにおける単一行の更新と一括変更という、2 つの代表的な OLTP 操作に焦点を当てます。これらのテストにより、更新速度が対象行をいかに効率的に特定できるかにどれほど依存しているかが明らかになります。これは、ClickHouse が分析用途のルーツから受け継いだ強みです。

数値を見ていく前に、ひとつ確認しておきたい点があります。PostgreSQL と ClickHouse の比較は、完全に同一条件の比較にはなりません。

どちらも UPDATE を実行できますが、結果を解釈するうえで重要な実行モデルの違いがあります。

> **TL;DR:** PostgreSQL はデフォルトですべてのステートメントを完全なトランザクションコンテキストでラップし、コミット時に WAL をフラッシュしますが、ClickHouse はそうしません。つまり、これらの結果はトランザクション性能や永続性性能を完璧に測定したものではありません。それでも、それぞれのシステムがネイティブな実行モデルのもとで更新ワークロードをどう処理するかを見るうえで、非常に興味深く有益な比較です。

PostgreSQL と ClickHouse はどちらも UPDATE ステートメントを実行できますが、その方式は異なります。両者は分離性に対するアプローチが似ており、どちらも**マルチバージョン同時実行制御 (MVCC)** を採用していますが、トランザクションの扱い、コミットのセマンティクス、永続性の保証において異なります。

この違いは、ベンチマーク結果を読み解くうえで重要です。

### マルチバージョン同時実行制御 (MVCC)

PostgreSQL と ClickHouse はどちらも [MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) を使用して、読み取り処理と書き込み処理が互いをブロックすることなく並列に動作できるようにしています。すべてのクエリは開始時点に存在していたデータの**一貫したスナップショット**を参照するため、次の特徴があります。

* 読み取り処理が、並行する更新による中途半端な変更を目にすることはありません。

* 読み取り処理と書き込み処理が互いをブロックしません。

* データがその場で上書きされることはなく、新しいバージョンが作成され、どのクエリも必要としなくなるまで古いバージョンが保持されます。

実際には次のように動作します。

**PostgreSQL**

1. 更新後のデータを含む**新しい行バージョン**を作成します。

2. 古いバージョンを参照するすべての読み取り処理が完了するまで、古いバージョンを保持します。

3. 後から **[VACUUM](https://www.postgresql.org/docs/current/sql-vacuum.html)** を介して古いバージョンを削除します。

**ClickHouse**

1. 更新された値を含む**新しい[データパート](https://clickhouse.com/docs/ja/concepts/core-concepts/parts)([パッチ](https://clickhouse.com/ja/blog/updates-in-clickhouse-2-sql-style-updates-jp))**を作成します。

2. [古いデータパートを参照するすべての読み取り処理が完了するまで、古いデータパートを保持します](https://clickhouse.com/blog/clickhouse-release-25-06#single-snapshot-for-select)。

3. [バックグラウンドマージ](https://clickhouse.com/docs/ja/concepts/core-concepts/merges)の際に更新をソースデータパートへマージし、古いバージョンを破棄します。


    > 分離性だけで見れば、PostgreSQL と ClickHouse の挙動は非常によく似ています。どちらも更新の処理中であっても読み取り側が常に一貫したスナップショットを参照できるように保証します。



### トランザクション、コミット、ロールバック

ここから両者の方式が分かれます。

**PostgreSQL**

* 単一の UPDATE であっても、*すべての*ステートメントを**[トランザクションコンテキスト](https://www.postgresql.org/docs/current/tutorial-transactions.html)**内で実行します。

* 明示的に開始しない場合でも、ステートメントは**暗黙的なトランザクション**(BEGIN → 実行 → COMMIT)でラップされます。

* 永続性とクラッシュ復旧のため、テーブルに触れる前にすべての変更を **Write-Ahead Log([WAL](https://www.postgresql.org/docs/current/wal-intro.html))**に書き込みます。

* **COMMIT** によって変更が可視化され、**ROLLBACK** はコミット前に変更を破棄して、後のクリーンアップ用に古い行バージョンを無効としてマークします。

**ClickHouse**

* MergeTree テーブルに対する単一の挿入および更新は、単一のブロックとしてパックおよび挿入される場合、**アトミック、一貫性、分離性、耐久性(ACID)を[備えています](https://clickhouse.com/docs/ja/concepts/features/operations/insert/transactions#case-1-insert-into-one-partition-of-one-table-of-the-mergetree-family)**。

* デフォルトでは、すべてのステートメントをトランザクションでラップすることはありません。

* 明示的な BEGIN、COMMIT、ROLLBACK を伴う**実験的な**[複数ステートメントトランザクション](https://clickhouse.com/docs/ja/concepts/features/operations/insert/transactions#transactions-commit-and-rollback)をサポートしています(ClickHouse Cloud では未提供)。

> PostgreSQL はデフォルトで完全なトランザクション性を備えているため、**更新ステートメントごとのオーバーヘッド**が生じます。ClickHouse は、明示的にトランザクションを開始しない限り余分な管理処理を回避します(実験的機能であり、ClickHouse Cloud では未提供)。

### 永続性とフラッシュポリシー

両システムが再び異なるのが、永続性を保証する方法とディスクへのフラッシュの処理方法です。

PostgreSQL:

* デフォルトで `fsync` = `on`、`synchronous_commit` = `on` → コミットごとに WAL がディスクにフラッシュされます。

* レイテンシは増加しますが、OS がクラッシュしても永続性が保証されます。

ClickHouse:

* デフォルトで `fsync_after_insert` = `false` → OS のページキャッシュに依存し、パートはアトミックですが即座にはフラッシュされません。

* 挿入/更新は高速になりますが、即時の永続性は弱くなります。より強力な保証が必要な場合は `fsync_after_insert = 1` を有効にできます。

> ベンチマークへの影響: 結果にはデフォルトの永続性/フラッシュ設定が反映されています。これらの設定値を変更すれば絶対的な所要時間は変わりますが、相対的な傾向(行更新とカラム更新のコスト差)が支配的です。


実行モデルの主な違いを念頭に置いたうえで、両システムをどのようにテストしたかを見ていきましょう。

## データセットとベンチマーク環境

ClickHouse における高速更新に関する[前回のブログ](https://clickhouse.com/ja/blog/updates-in-clickhouse-3-benchmarks-jp)では、データセット、スキーマ、セットアップについて詳しく説明しました。ここでは、新たな ClickHouse と PostgreSQL の比較に集中できるよう、要点のみを簡単におさらいします。

### データセット

顧客の注文品目をモデル化した [TPC-H](https://clickhouse.com/docs/ja/get-started/sample-datasets/tpch) の lineitem テーブルを使用します。初期挿入後に数量、価格、割引などが変更される可能性があるため、これは更新における古典的な [OLTP](https://clickhouse.com/resources/engineering/oltp-vs-olap) スタイルのシナリオです。

#### ClickHouse

<pre>
<code type='click-ui' language='sql' show_line_numbers='false'>
CREATE TABLE lineitem (
    l_orderkey       Int32,
    l_partkey        Int32,
    l_suppkey        Int32,
    l_linenumber     Int32,
    l_quantity       Decimal(15,2),
    l_extendedprice  Decimal(15,2),
    l_discount       Decimal(15,2),
    l_tax            Decimal(15,2),
    l_returnflag     String,
    l_linestatus     String,
    l_shipdate       Date,
    l_commitdate     Date,
    l_receiptdate    Date,
    l_shipinstruct   String,
    l_shipmode       String,
    l_comment        String)
ORDER BY (l_orderkey, l_linenumber);
</code>
</pre>

#### PostgreSQL

<pre>
<code type='click-ui' language='sql' show_line_numbers='false'>
CREATE TABLE lineitem
(
  l_orderkey          INT,
  l_partkey           INT,
  l_suppkey           INT,
  l_linenumber        INT,
  l_quantity          DECIMAL(15,2),
  l_extendedprice     DECIMAL(15,2),
  l_discount          DECIMAL(15,2),
  l_tax               DECIMAL(15,2),
  l_returnflag        CHAR(1),
  l_linestatus        CHAR(1),
  l_shipdate          DATE,
  l_commitdate        DATE,
  l_receiptdate       DATE,
  l_shipinstruct      CHAR(25),
  l_shipmode          CHAR(10),
  l_comment           VARCHAR(44),
  PRIMARY KEY (l_orderkey, l_linenumber)
);
</code>
</pre>

#### 主キーとインデックス作成

両データベースとも [TPC-H 公式の複合主キー](https://www.tpc.org/TPC_Documents_Current_Versions/pdf/TPC-H_v3.0.1.pdf)である `(l_orderkey, l_linenumber)` を使用します。

* **PostgreSQL** では、これによりデフォルトで **[B-tree インデックス](https://www.postgresql.org/docs/current/indexes-types.html#INDEXES-TYPES-BTREE)**が作成されます。

* **ClickHouse** では、同じカラムに対して**[スパースプライマリインデックス](https://clickhouse.com/docs/ja/concepts/core-concepts/primary-indexes)**が作成されます。

どちらの場合も、インデックスによりこれらのカラムを条件に持つすべてのクエリが一致する行を素早く見つけ出せるため、更新と読み取りの双方が高速化されます。

#### データサイズ

このテーブルの**スケールファクター 100** バージョンを使用します。これにはおよそ **6 億行**(約 1 億 5,000 万件の注文からの約 6 億件の明細)が含まれ、ディスク上の専有サイズは **ClickHouse で約 30 GiB**、**PostgreSQL で約 85 GiB** となります。

### テスト環境

#### ハードウェアおよび OS

**Ubuntu 24.04 LTS** を実行する **AWS m6i.8xlarge** EC2 インスタンス(32 コア、128 GB RAM)と **gp3 EBS ボリューム**(16,000 IOPS、最大スループット 1,000 MiB/s)を使用しました。

#### ClickHouse バージョン

すべてのテストは、いかなる最適化も適用していないデフォルトインストールの **ClickHouse 25.7** で実施しました。

#### PostgreSQL バージョン

**PostgreSQL 17.5** を使用し、[最適化された設定でインストール](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/benchmark.sh#L15)しました。

### ベンチマーク手法

各更新の前に OS レベルのページキャッシュを破棄することで、**コールド状態での更新時間**を測定しました。これにより、データベースはキャッシュされたデータや事前の読み取りの恩恵を受けることなく、影響を受ける行をストレージから読み込んで変更をゼロから適用することを余儀なくされます。その結果得られるのは、**上限(ワーストケース)のレイテンシ数値**です。ウォームキャッシュでの測定は、PostgreSQL と ClickHouse でデータのキャッシュおよび再利用の仕組みが異なるため、公平に比較することが難しくなります。

**追記**: より全体像を把握できるように、現在は**コールド実行**と**ホット実行**の両方を報告しています。コールド実行は I/O に負荷をかけてワーストケースのレイテンシを際立たせ、ホット実行はデータがすでにメモリにキャッシュされている典型的なワークロードを再現します。

### ご自身での再現手順

GitHub 上の[ベンチマークスクリプト](https://github.com/ClickHouse/examples/tree/main/blog-examples/UPDATEBench)を使用して、すべての結果を再現できます。

まずは小規模なものとして一度に 1 行のみを変更するポイント更新を取り上げ、その後に複数行の一括変更へと進みます。

## 単一行(ポイント)更新

まず、[主キー](/ja/blog/update-performance-clickhouse-vs-postgresql-jp)で特定される単一行をそれぞれ対象とする [10 個の標準 SQL UPDATE ステートメント](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/updates-point.sql)から始めます。これらの更新は、ClickHouse と PostgreSQL の両方で順次実行されます。

### 更新レイテンシ

各更新について、グラフは UPDATE の発行からその変更がクエリから参照可能になるまでの[コールド](/ja/blog/update-performance-clickhouse-vs-postgresql-jp)時間を示しています。`Cols upd` カラムは、テーブルの 16 カラムのうち該当ステートメントでいくつのカラムが更新されたかを示します。

![Blog-updates Part 4.001.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_001_1ee0bc6501.png)

*(上記のグラフの元となった [PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/cold_point.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_COLD_POINT.json) の生のベンチマーク結果をご覧ください。)*

これらのポイント更新では ClickHouse が一貫してより速く完了しており、非常に満足のいく結果となっています。とはいえ、Postgres はすべてのステートメントをトランザクションコンテキストでラップするオーバーヘッドを負っているのに対し、ClickHouse はそうではない点を改めて強調しておきます。

それを踏まえたうえで、ここでの主なポイントは以下のとおりです。

1. Postgres は実に高速です。
2. ClickHouse の UPDATE は幸先の良いスタートを切り、トランザクションが不要なケースにおいて優れた性能を発揮しています。
3. 現在の性能は十分に高く、仮に ClickHouse がトランザクションの本番サポートを導入したとしても、より対等なテストにおいて Postgres と同等の水準に落ち着く可能性が十分にあります。

また、各システムが個々の行を見つけ出す方法が異なる点も興味深いところです。PostgreSQL は 1 行につき 1 エントリを持つ B-tree インデックスを使用して対象行を直接特定します。ClickHouse のスパースプライマリインデックスは、対象が含まれている可能性のある[行のブロック](https://clickhouse.com/docs/ja/guides/clickhouse/data-modelling/sparse-primary-indexes#data-is-organized-into-granules-for-parallel-data-processing)(デフォルトで 8,192 行)を特定し、その後[すべての行を並列で検索](https://clickhouse.com/docs/ja/concepts/core-concepts/query-parallelism)します。


大半のワークロードにおいて、更新して終わりではありません。その後に何が起こるかが重要です。実際のダッシュボードやレポートでは、更新された値が後続のクエリに即座に反映される必要があります。そこで次は、これらの更新の直後に実行される分析クエリを各データベースがどのように処理するかを確認します。

### 更新後のクエリレイテンシ

更新されたデータの恩恵を即座に受ける **[10 個の典型的な分析クエリ](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/analytical_queries.sql)**を使用します。これらは、以下のようなデータウェアハウス特有の一般的なパターンをカバーしています。

* **売上およびマージンの再計算** – 例: 特定の配送モードにおける純売上の計算。

* **フィルタリングされたサブセットに対する集計** – 例: 特定のコメントやステータスを持つ注文の平均割引率と税額。

* **業務上の件数集計** – 例: 特定のモードで配送された注文や特定の指示を伴う注文のカウント。

* **アドホックな調査** – 例: コメントの部分文字列に一致する行のサンプリング。

これらのクエリは、変更後に更新されたファクトが即座に反映されることが期待される、**リアルタイムダッシュボードや分析レポート**に見られる処理を表しています。

これらのクエリは 10 個の更新と 1 対 1 で対応付けられています。各更新の後に、それとペアになったクエリが実行されます。各クエリは少なくとも更新された行とカラムにアクセスしますが、現実的な分析を模倣するために多くはより広範なデータをスキャンします。

以下のグラフは、単一行 UPDATE ごとの全 10 クエリにおける更新後の[コールド](/ja/blog/update-performance-clickhouse-vs-postgresql-jp)クエリ時間を示しています。

![Blog-updates Part 4.002.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_002_d6353471d4.png)

*(上記のグラフの元となった [PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/cold_point.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_COLD_POINT.json) の生のベンチマーク結果をご覧ください。)*

ClickHouse については **Before materialization** を示しています。クエリはバックグラウンドマージを待つことなく、*[読み取り時パッチ(patch-on-read)](https://clickhouse.com/ja/blog/updates-in-clickhouse-2-sql-style-updates-jp)*を介して更新された値を即座に参照します。

ここで ClickHouse が一貫して圧倒的に高速であることは、それほど驚くことではありません。これらは*大規模データセットに対する分析クエリ*であり、ClickHouse の得意分野だからです。重要なポイントは、更新フェーズによる速度低下がほとんどないことです。更新が適用された直後であっても、完全にマージされた後であっても、クエリ性能は高く予測可能な状態を保ちます。

単一行の更新は OLTP のベースラインであり、各エンジンが個々のレコードをどれだけ素早く見つけて変更できるかを測定します。しかし、多くの実業務での変更ははるかに大きなデータ範囲に及ぶため、次は一括更新を見てみましょう。

## 一括更新(複数行の変更)

単一行更新のテストで使用したのと同じ [10 個の標準 SQL UPDATE ステートメント](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/updates-bulk.sql)を用いましたが、主キーで個々の行を特定しないように述語を変更しました。代わりに、注文テーブルにおいて非常に一般的な OLTP パターンである、**数十、数百、あるいは数十万行**に一致するようにしています。

このような一括更新が発生する典型的な理由には、次のようなものがあります。

* **データ入力エラーの修正** – 特定の日付や数量に一致するすべての注文について、割引率、税、または価格を修正する。

* **注文ステータスの調整** – ビジネスイベントに応じて、返品フラグ、配送モード、または明細ステータスを変更する。

* **ポリシーや価格改定の適用** – 特定のサプライヤー、契約、またはキャンペーンを対象に品目の価格を改定する。

* **特殊な一括処理への対応** – 確定日や数量などのフィルターに基づいて、注文に「優先」「保証延長」「返品フラグ付き」などのマークを付与する。

これらのシナリオは本番環境の OLTP ワークロードで頻繁に発生し、一度に 1 行ずつではなく、フィルターで絞り込まれた大規模なデータサブセットに対して業務上の変更を適用する必要があります。

### 更新レイテンシ

ClickHouse と PostgreSQL の両方で順次実行した 10 個の一括 UPDATE それぞれについて、グラフは UPDATE の発行からその変更がクエリに反映されるまでの[コールド](/ja/blog/update-performance-clickhouse-vs-postgresql-jp)時間を測定しています。`Rows upd` と `Cols upd` のカラムは、各ステートメントで更新された行数とテーブルの 16 カラム中いくつのカラムが更新されたかを示しています。

![Blog-updates Part 4.003.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_003_7338308ff0.png)

*(上記のグラフの元となった [PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/cold_bulk.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_COLD_BULK.json) の生のベンチマーク結果をご覧ください。)*


当初、これらの結果は少し不自然に見えましたが、実際には理にかなっています。更新⑤を除き、[これらの一括更新](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/updates-bulk.sql)は WHERE 句で主キー以外のカラムをフィルターしているため、[主キーインデックス](/ja/blog/update-performance-clickhouse-vs-postgresql-jp)を使用できません。(両システムとも追加のインデックスを使用することは可能ですが、16 カラムもあるとすべての組み合わせを網羅するのは非現実的です。特にアドホックな一括更新では、インデックスの作成自体が更新処理よりも長くかかる場合があります。)

そうした場合、実際のコストは変更の書き込みではなく、該当行を見つけることにあります。つまり、テーブルを端から端までスキャンすることになります。

このデータセットにおいて、PostgreSQL では変更が可視化されるまでに 380 〜 390 秒かかります。ClickHouse は同じデータを 0.09 〜 5.23 秒でスキャンし、最大 4,000 倍高速です。この優位性は、[高度な並列処理](https://clickhouse.com/docs/ja/concepts/core-concepts/query-parallelism)と[列指向レイアウト](https://clickhouse.com/docs/ja/get-started/about/intro#row-oriented-vs-column-oriented-storage)から生まれており、UPDATE の WHERE 句で使用されるカラムのみを読み取り、更新対象のカラムの値のみを書き込みます。

[更新⑤](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/updates-bulk.sql#L5)が唯一の例外です。両システムともインデックスを使用して一致する行へ直接ジャンプするため、差は「わずか」26 倍に縮まります。しかし、それ以外の全データをスキャンしなければならないあらゆるケースにおいて、ClickHouse の圧倒的なスキャン速度により、大規模な変更であっても更新が反映されるまでのレイテンシが低く抑えられます。

> OLTP ワークロードでは、特定できていないものを更新することはできません。そして膨大な数の行を素早く見つけ出すには、分析エンジンの馬力が必要になります。

それでも、更新を適用することは第一段階にすぎません。第二段階は、更新されたデータに対してどれだけ素早く分析を実行できるかであり、そもそも変更を行った目的はそこにある場合がほとんどです。これらの一括更新の直後に代表的な分析クエリを実行した場合に、各システムがどのように機能するかを見てみましょう。

### 更新後のクエリレイテンシ

先ほどと同じ **[10 件の代表的な分析クエリ](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/scripts/analytical_queries.sql)** を使用し、10 件の更新と再び 1:1 でペアにしています。各更新の後に、ペアとなるクエリが実行されます。各クエリは少なくとも更新された行とカラムにアクセスしますが、より現実的な分析を模倣するために、多くのクエリはさらに広範なデータをスキャンします。

次のグラフは、各一括 UPDATE の後に実行された全 10 クエリの更新後の[コールド状態](/ja/blog/update-performance-clickhouse-vs-postgresql-jp)クエリ時間を示しています。

![Blog-updates Part 4.004.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_004_da3420b9b0.png)

*(上のグラフの背後にある生のベンチマーク結果は、[PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/cold_bulk.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_COLD_BULK.json) をご覧ください。)*


予想どおり、同一ハードウェアおよび同一データセットにおいて、更新されたデータから直接恩恵を受ける典型的な分析クエリでは、ClickHouse のほうが一貫して PostgreSQL よりはるかに高速です。

しかし、コールド状態の実行結果は全体像の半分にすぎません。全体像を把握するために、ホット状態での実行結果を見てみましょう。データがすでにキャッシュされている状態での更新では、また違ったトレードオフが浮かび上がってきます。


## ホット状態の結果:もうひとつの側面

本記事の初回バージョンでは、キャッシュされていないデータに対してクエリを実行する**コールド状態の実行**に焦点を当てていました。コールド状態の結果は、システムが I/O や大規模なバッチ更新をどのように処理するかという違いを際立たせるため、ストレステストとして有用です。

とはいえ、貴重なフィードバックもいただきました。一般的な **OLTP ワークロード**において、コールド状態での実行は稀です。インデックスとキャッシュのヒット率は 99% を超えることが多く、これは大半の更新が、すでにメモリ上にデータが存在する**ホット状態**で動作することを意味します。

より完全な全体像を提示するために、ベンチマークを拡張して**ホット状態の結果**も追加しました。これにより、診断的な「ワーストケース」と、日々のワークロードにおける「現実的なベースライン」の両方を比較できます。

比較しやすくするために、先ほどと同じグラフの順序で、まずは単一行(ポイント)更新、続いて一括更新を、今回はホット状態で見ていきます。


### 単一行(ポイント)更新 - 更新レイテンシと更新後のクエリレイテンシ

このグラフは各更新について、すべてのデータがすでにメモリにキャッシュされている状態で、UPDATE を発行してからその変更がクエリから可視化されるまでの*ホット状態*のレイテンシを示しています。先ほどと同様に、**Cols upd** カラムはそのステートメントでテーブルの 16 カラム中いくつのカラムが更新されたかを示しています。

![Blog-updates Part 4.005.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_005_4a7fed56c6.png)

*(上のグラフの背後にある生のベンチマーク結果は、[PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/hot_point.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/main/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_HOT_POINT.json) をご覧ください。)*

データがすでにメモリにキャッシュされているホット状態では、PostgreSQL が優位に立ちます。すべてのステートメントが完全なトランザクションにラップされるコストを支払っているにもかかわらず、行レベルの B-tree インデックスによって決定的なアドバンテージを得ています。ブロックをスキャンすることなく、対象の行を直接特定して更新できるためです。

ClickHouse も依然として高速で、一貫して 1 桁ミリ秒台で更新を完了していますが、こうしたインメモリのケースでは PostgreSQL にわずかに及びません。この差は実行モデルの違いを反映しています。ClickHouse のスパースプライマリインデックスでは、データがすでにメモリ上にホットな状態であっても、常に 1 つの行ブロック(デフォルトで 8,192 行)全体をスキャンする必要があるためです。

主なポイントは以下のとおりです。



* PostgreSQL は、トランザクションのオーバーヘッドがあるにもかかわらず、ホット状態のポイント更新で勝利します。そのインデックス構造により、データがキャッシュされた後の行ルックアップが極めて効率的になります。

* ClickHouse はわずか数ミリ秒遅れるのみで、依然として優れた OLTP スタイルのレイテンシを提供します。

* 両システムともここでは非常に高い性能を発揮していますが、アーキテクチャ上のトレードオフが明確に現れています。PostgreSQL はトランザクション的なポイント更新向けに最適化されているのに対し、ClickHouse の設計は分析ワークロード向けであり、それでもなお肉薄した性能を維持しています。


次のグラフは、各単一行 UPDATE の後に実行された全 10 クエリの更新後のホット状態クエリ時間を示しています。

![Blog-updates Part 4.006.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_006_fa62020526.png)

*(上のグラフの背後にある生のベンチマーク結果は、[PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/hot_point.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/main/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_HOT_POINT.json) をご覧ください。)*

PostgreSQL と ClickHouse の双方において、データがすでにキャッシュされているため、ホット状態のクエリ実行時間はコールド状態よりも短くなっています。しかし相対的な挙動は変わりません。コールド状態の結果と同様に、同一ハードウェアおよびデータセット上で、更新データを即座に使用する典型的な分析クエリを実行した場合、ClickHouse のほうが一貫して PostgreSQL よりはるかに高速です。


### 一括更新 - 更新レイテンシと更新後のクエリレイテンシ

10 件の一括 UPDATE それぞれについて、このグラフは UPDATE を発行してからその変更がクエリから可視化されるまでのホット状態の時間を示しています。Rows upd カラムと Cols upd カラムは、各ステートメントで何行、そしてテーブルの 16 カラム中いくつのカラムが更新されたかを示しています。

![Blog-updates Part 4.007.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_007_8910a82d40.png)

*(上のグラフの背後にある生のベンチマーク結果は、[PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/hot_bulk.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_HOT_BULK.json) をご覧ください。)*

データがすでにキャッシュされているホット状態では、PostgreSQL と ClickHouse の双方がコールド状態よりも高速に一括更新を完了します。しかし、相対的な構図は変わりません。PostgreSQL が依然として行の特定に数十秒を費やしているのに対し、ClickHouse の並列スキャンは更新を 1 秒未満に抑え、このデータセットでは最大 700 倍高速です。

次のグラフは、各一括 UPDATE の後に実行された全 10 クエリの更新後のホット状態クエリ時間を示しています。

![Blog-updates Part 4.008.png](https://cms.clickhouse-dev.com:1337/uploads/Blog_updates_Part_4_008_951839272d.png)

*(上のグラフの背後にある生のベンチマーク結果は、[PostgreSQL](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/postgresql/results/hot_bulk.json) および [ClickHouse](https://github.com/ClickHouse/examples/blob/ec031e87ec602da81fb844d81757ee9f1e912b57/blog-examples/UPDATEBench/clickhouse/results/update_timings_sequential_lightweight_p20_HOT_BULK.json) をご覧ください。)*

データがキャッシュされている状態では、両システムともにコールド状態よりもクエリ時間が短縮されます。しかし傾向は維持されています。同一ハードウェア上で更新データを即座に使用する分析クエリにおいて、ClickHouse は一貫して PostgreSQL よりはるかに高速であり続けます。

総合すると、これらのテストは ClickHouse が変更を素早く適用するだけでなく、それを分析クエリですぐに活用できるようにすることを示しています。これは OLTP 中心のシナリオでは稀有な組み合わせです。

## まとめ

SQL 標準に準拠した高性能な UPDATE は、列指向ストアや分析データベースにおいては珍しい機能です。これらは分析ワークロードにとって極めて有益であり、OLTP スタイルのワークロードをサポートするうえで最も難易度の高い足がかりの 1 つでもあります。

ClickHouse 向けの UPDATE 開発に着手した際、目標は「Postgres に勝つこと」ではありませんでした。今でもそれは目標ではありません。しかし、単一行の更新では PostgreSQL と同等になり、一括更新では最大 4,000 倍の高速化を達成するなど、この取り組みがこれほど説得力のある結果として結実したことを非常に誇らしく思います。

コールド状態での実行は、2 つのシステムにおける大規模スキャンの処理方法の違いを浮き彫りにしました。ClickHouse は一括更新において最大 4,000 倍高速です。ホット状態での実行はトレードオフを裏付けています。PostgreSQL は B-tree インデックスのおかげでキャッシュされたポイント更新で勝利しますが、ClickHouse は分析クエリにおいて圧倒的な速さを維持し、単一行の OLTP スタイルの更新でも十分に渡り合っています。

Postgres が ClickHouse ではまだ実現できない完全なトランザクションセマンティクスを提供していることは認識していますが、可能性を閉ざすつもりはありません。将来的に ClickHouse がトランザクションをサポートできない理由はないと考えています。

そして私たちは、おそらく明白であるはずの事実を学びました。
見つけられないものは更新できない。そして ClickHouse は、見つけるのが速いのです。