doridoridoriand’s diary

主に技術的なことを書いていく予定(たぶん)

TiDBに何でもかんでも詰め込んでみた

この記事は TiDB Advent Calendar 2024 12日目の記事です。
この記事を見てくださっている方々には、TiDBはなんぞやなどは釈迦に説法だと思いますのでイントロダクション的なのは省かせていただきます :-)

目次

  • モチベーション
  • TiDBが対応するデータベースの種類
  • 色々データを入れてみましょう
  • データを横断的にみてみましょう
  • まとめ

モチベーション

NewSQLというワードが登場してから久しく、従来のリレーショナルデータベースが抱えるスケーラビリティの問題や、NoSQLが課題としていたACID特性の欠如を解消するデータベースが数多く登場しました。その中でも、MySQL互換とKVSの特性を組み合わせたTiDBは、NewSQLの代表格として注目を集めています。
従来のRDBは、データ量の増加に伴いスケールアウトが難しく、結果として性能のボトルネックが発生しがちです。一方、NoSQLは分散処理に優れるものの、データ整合性(ACID)を妥協するケースが多く、金融やECなど整合性が求められるユースケースでは導入が難しい場面もあります。
TiDBは、以下の特徴を持つ分散型データベースです。

  • MySQL互換: 既存のMySQLアプリケーションをほぼそのまま移行可能。
  • KVSベース: TiKVにより、スケーラブルで高性能なストレージを提供。
  • OLAP/OLTP統合: TiSparkやTiFlashを利用したリアルタイム分析も可能。
  • ベクトルデータ対応(ベータ版): 最新のAI/機械学習ユースケースにも対応。

1つのデータベースクラスタを用意するだけで、様々なタイプやワークロードのデータを格納しつつ、横断的に扱えるのは魅力的です。
DB-Engines Rankingでは、TiDBは2024年12月現在で71位にランクインしており、昨年から順位が上昇しています。このことから、スケーラブルなデータベースソリューションへの需要が高まる中、TiDBが世界的に注目されていることが伺えます。

サービス開発において「どのようにデータを格納し、活用するか」という課題は重要です。今回は、TiDBが対応するデータベースの種類やユースケースに注目し、データの格納から分析までを実際に試して、その特徴を整理していきたいと思います。

TiDBが対応するデータベースの種類

TiDBは以下のデータベースをサポートしています。

  • MySQL互換: 従来のMySQLアプリケーションと互換性があり、SQLベースで簡単に操作可能。
  • KVS (TiKV, TiFlash): トランザクションデータ(OLTP)と分析データ(OLAP)の両方を効率的に処理可能。
  • Apache Spark (TiSpark): 分散分析環境をTiDBに統合し、高速なビッグデータ処理を実現。
  • ベクトルデータ (experimental): AIや機械学習用途で必要とされる高次元ベクトルデータを格納可能。

1つのデータベースクラスタでこれら複数のデータベースタイプやオンラインワークロードを統合的に扱えるのは、TiDBの大きな特徴です。 例えば、ユーザーのトランザクションデータを記録しながら、そのデータをリアルタイムで分析したり、AIモデルに必要なベクトルデータを格納するなど、幅広いユースケースを1つの環境で実現できます。

TiDBの各機能を使ってデータを実際に入れてみることで、操作の簡単さや処理性能を体感していきましょう。

色々データを入れてみましょう

今回はECサイトのデーターベースを例として、それぞれのデータベースタイプにデータを入れてみましょう。

MySQL互換

当然すぎて取り上げる必要があるかかなり微妙ですが、MySQL互換なのでもちろんリレーショナルデータを扱うことができます。

CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date DATETIME NOT NULL,
    status ENUM('pending', 'completed', 'cancelled') NOT NULL DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE order_details (
    detail_id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    product_name VARCHAR(255) NOT NULL,
    quantity INT NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
        ON DELETE CASCADE
);

テストデータを作って入れてみましょう

INSERT INTO orders (customer_id, order_date, status)
VALUES 
    (1, '2024-12-01 10:00:00', 'completed'),
    (2, '2024-12-02 12:30:00', 'pending'),
    (3, '2024-12-03 15:45:00', 'cancelled');

INSERT INTO order_details (order_id, product_id, product_name, quantity, price)
VALUES 
    -- Order ID 1
    (1, 101, 'Wireless Mouse', 2, 1500.00),
    (1, 102, 'USB-C Hub', 1, 3500.00),
    
    -- Order ID 2
    (2, 201, 'Bluetooth Speaker', 1, 7500.00),
    (2, 202, 'Laptop Stand', 2, 2000.00),

    -- Order ID 3
    (3, 301, 'Gaming Keyboard', 1, 12000.00);

order_id = 1のデータを取得してみましょう

SELECT o.order_id, o.customer_id, o.order_date, o.status, 
       d.product_name, d.quantity, d.price
FROM orders o
JOIN order_details d ON o.order_id = d.order_id
WHERE o.order_id = 1;

各注文の合計金額を計算してみましょう

SELECT o.order_id, o.customer_id, o.order_date, o.status, 
       SUM(d.quantity * d.price) AS total_amount
FROM orders o
JOIN order_details d ON o.order_id = d.order_id
GROUP BY o.order_id;

JSON側も扱えますので、以下のようなデータ形式でも格納可能です(この正規化の設計が適切かはさておき)

CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date DATETIME NOT NULL,
    status ENUM('pending', 'completed', 'cancelled') NOT NULL DEFAULT 'pending',
    order_details JSON NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

テストデータを作って入れてみましょう

INSERT INTO orders (customer_id, order_date, status, order_details)
VALUES 
    (1, '2024-12-01 10:00:00', 'completed', '[
        {"product_id": 101, "product_name": "Wireless Mouse", "quantity": 2, "price": 1500.00},
        {"product_id": 102, "product_name": "USB-C Hub", "quantity": 1, "price": 3500.00}
    ]'),
    (2, '2024-12-02 12:30:00', 'pending', '[
        {"product_id": 201, "product_name": "Bluetooth Speaker", "quantity": 1, "price": 7500.00},
        {"product_id": 202, "product_name": "Laptop Stand", "quantity": 2, "price": 2000.00}
    ]'),
    (3, '2024-12-03 15:45:00', 'cancelled', '[
        {"product_id": 301, "product_name": "Gaming Keyboard", "quantity": 1, "price": 12000.00}
    ]');

order_id = 1のデータを取得してみましょう

SELECT o.order_id, o.customer_id, o.order_date, o.status, 
       o.order_details->'$.total_amount' AS total_amount
FROM orders o
WHERE o.order_id = 1;

各注文の合計金額を計算してみましょう

SELECT o.order_id, o.customer_id, o.order_date, o.status, 
       o.order_details->'$.total_amount' AS total_amount
FROM orders o
GROUP BY o.order_id;

KVS

TiKVをKVSとして利用してみましょう。TiKVへのアクセス方法としてRawKVとTxnKVの2つの方法がありますが、今回はTxnKVを利用します。以下サンプルコードになります。

まとめ

本来はTiSparkやベクトルデータも扱いたかったのですが、筆者の知識不足もあり、一旦仕切り直しで別途記事にしたいと思います。

参考文献