💡

TiFlashとMySQL、クエリはどっちが速い?

に公開

はじめに

TiDB は分散型の HTAP(Hybrid Transactional/Analytical Processing)データベースで、トランザクション処理向けの行ストレージ(TiKV)と、分析処理向けの列ストレージ(TiFlash)を併用できます。一方、MySQL(InnoDB)は行志向ストレージを採用した OLTP 向けの RDBMS です。

本記事では、同一データセットを用いて TiFlashMySQL(InnoDB) のクエリ処理性能を比較し、どのようなケースでどちらを選ぶと効果的かを整理します。特に「少ない列を対象にした集計・グルーピング(例:COUNT(*)GROUP BY)」に焦点を当て、行志向と列志向の得手不得手が実測でどの程度の差として現れるかを確認します。

なお、計測結果はデータ量・分布、インデックス設計、ハードウェア構成、設定(並列度やバッファサイズ等)によって変動します。本検証はシンプルな構成での一例として捉えてください。

前準備

TiFlashと比較するためMySQLコンテナを作成します。

docker-compose.yml
version: "3.8"

services:
  mysql:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mysql-test
    environment:
      - DB_NAME=test
      - DB_USER=root
      - DB_HOST=127.0.0.1
      - DB_PORT=3306
      - DB_PASSWORD=password
    ports:
      - "3306:3306"
    networks:
      - mysql-net
    volumes:
      - ./data:/docker-entrypoint-initdb.d:ro

networks:
  mysql-net:
    driver: bridge


Dockerfile
FROM mysql:8.0

ARG TZ=Asia/Tokyo
ENV TZ=${TZ}

ENV MYSQL_DATABASE=test \
    MYSQL_ROOT_PASSWORD=password \
    MYSQL_USER=app \
    MYSQL_PASSWORD=app_password

COPY ./data/ /docker-entrypoint-initdb.d/

COPY ./conf.d/ /etc/mysql/conf.d/

# ヘルスチェック(起動完了を検知)
HEALTHCHECK --interval=10s --timeout=5s --retries=10 \
  CMD mysqladmin ping -h 127.0.0.1 -uroot -p$MYSQL_ROOT_PASSWORD || exit 1

EXPOSE 3306
.conf.d/my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server     = utf8mb4_0900_ai_ci
default-authentication-plugin = mysql_native_password

それぞれの特徴

  • MySQL(行ストア / TiKV)

    • データを行単位で格納
    • PKやインデックスでのポイント検索、更新が高速
    • 少量データの取得に適している
    • 集計・分析など全件スキャンは苦手
  • TiFlash(列ストア)

    • データを列単位で格納
    • 特定の列だけを使う集計処理が高速
    • MPP(Massively Parallel Processing)で分散並列処理可能
    • 複雑なGROUP BYやORDER BY、ウィンドウ関数などに強い
    • 書き込みはTiKVから非同期で反映(数秒の遅延がある場合あり)

速度差が出やすいケース

クエリの種類 MySQL(行ストア) TiFlash(列ストア)
PK等価検索 (id=...) ◎ とても速い △ オーバーヘッドあり
インデックス検索(少量行) ◎ 速い
全件集計 (COUNT(*), SUM(...)) △ 遅い ◎ 非常に速い
大規模GROUP BY / 集計 △ 遅い
複数列JOIN + 集計 △ 遅い
小規模データセット処理 ◎ 速い

データ作成

速度計測の前に、100万行のテーブルを作成します。
以下のDDLをTiDBとMySQLに適用します。

-- 新しいデータベースを作成
CREATE DATABASE IF NOT EXISTS test_db;

-- データベースを使用
USE test_db;

-- テーブルを作成
CREATE TABLE IF NOT EXISTS my_table (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 100万行のレコードの流し込み
INSERT INTO my_table (name, created_at)
WITH RECURSIVE d AS (
  SELECT 0 AS n
  UNION ALL
  SELECT n + 1 FROM d WHERE n < 999
),
seq AS (
  SELECT a.n * 1000 + b.n + 1 AS n
  FROM d AS a
  CROSS JOIN d AS b
)
SELECT
  CONCAT('name_', n),
  DATE_SUB(NOW(), INTERVAL MOD(n, 365) DAY)
FROM seq
WHERE n <= 1000000;

ANALYZE TABLE my_table;

TiFlashを有効にする手順

  1. 対象テーブルにTiFlashレプリカを作成します。
ALTER TABLE my_table SET TIFLASH REPLICA 1;
  1. セッションでTiFlash利用を許可します。
SET @@tidb_allow_mpp = 1;
SET @@tidb_isolation_read_engines='tiflash,tikv';

COUNTの測定

TiFlashの測定

  1. 以下のクエリを3回実行します。
SELECT /*+ READ_FROM_STORAGE(TIFLASH[my_table]) */ COUNT(*) FROM my_table;

/*+ READ_FROM_STORAGE(TIFLASH[my_table]) */のクエリは、「このクエリは指定したテーブルを TiFlash から読んで実行しろ」とプランナーに指示するものになります。

  1. 計測結果
    計測結果は以下になります。
1回目:
1 row in set (0.016 sec)
2回目:
1 row in set (0.019 sec)
3回目:
1 row in set (0.012 sec)

平均時間は0.0015 secになりました。

MySQLの測定

  1. 以下のクエリを3回実行します。
SELECT COUNT(*) FROM my_table;
  1. 計測結果
    計測結果は以下になります。
1回目:
1 row in set (0.007 sec)
2回目:
1 row in set (0.002 sec)
3回目:
1 row in set (0.003 sec)

平均時間は0.004 secになりました。

あれ?TiFlashの方が遅いけど、ってこの時点では思いました。

TiFlashは、全行・全列を返すSELECTはTiFlashの見せ場じゃないので、速さはあまり変わらないのが通常となっています。
集計・グルーピング・少列投影・範囲抽出で比較すると、TiFlashとの差が出やすいとのことで今度はその内容で比較します。

Group Byでの比較

TiFlashの測定

  1. TiflashのGROUP BYの実行を行います。
SELECT /*+ READ_FROM_STORAGE(TIFLASH[my_table]) */
       LEFT(name, 2) AS prefix, COUNT(*)
FROM my_table
GROUP BY prefix
ORDER BY COUNT(*) DESC
LIMIT 10;



計測結果は以下になります。

1回目:
1 row in set (0.047 sec)
2回目:
1 row in set (0.035 sec)
3回目:
1 row in set (0.042 sec)

平均時間は0.041 secになりました。

MySQLの測定

  1. MySQLのGROUP BYの実行を行います。
SELECT
       LEFT(name, 2) AS prefix, COUNT(*)
FROM my_table
GROUP BY prefix
ORDER BY COUNT(*) DESC
LIMIT 10;



計測結果は以下になります。

1回目:
1 row in set (0.18 sec)
2回目:
1 row in set (0.19 sec)
3回目:
1 row in set (0.18 sec)

平均時間は0.18 secになりました。

検証結果の要約と考察

本検証では、同一データセットに対して LEFT(name, 2) でプレフィックスを生成し、GROUP BY と COUNT(*) を行う集計クエリの性能を TiFlash と MySQL で比較しました。

TiFlash(3回計測の平均):0.041 sec
計測ログ:0.047 / 0.035 / 0.042 sec

MySQL(3回計測の平均):0.18 sec
計測ログ:0.18 / 0.19 / 0.18 sec

平均実行時間から速度比を算出すると、0.18 ÷ 0.041 ≈ 4.39。
したがって、TiFlashはMySQLに比べてGroup Byでは約4.39倍高速であり、処理時間は約77%短縮(1 − 0.041/0.18 ≈ 0.772)されました。

この結果は、列指向エンジンである TiFlash が、列型集計やグルーピング処理に強みを持つことを反映していると考えられます。特に、GROUP BY と COUNT(*) のような分析的ワークロードでは、列圧縮やベクトル化実行、集計のプッシュダウン最適化などにより、行指向の MySQL よりも有利になりやすいです。

まとめ

  • 結果(平均実行時間)

    • COUNT(*):MySQL 0.004s vs TiFlash 0.016sMySQLが約4倍速い
    • GROUP BY(LEFT(name,2)+COUNT(*)):TiFlash 0.041s vs MySQL 0.18sTiFlashが約4.4倍速い(約77%短縮)
  • 使い分けの目安

    • OLTP/ポイント検索・少量データの読み書きMySQL(行ストア)
    • 集計・大規模GROUP BY・少列投影の分析処理TiFlash(列ストア)
  • 注意点(検証条件で変動)

    • TiFlashレプリカは非同期反映(わずかな遅延あり)
    • 統計情報・並列度・キャッシュ・ハードウェア差で性能は変わる
コラボスタイル Developers

Discussion