## モジュール 5: ベクトル検索 (v8.5.0)

AIの進歩と高次元空間での効率的なデータ処理の必要性により、TiDBはベクトルデータ型とデータベース内ベクトル検索をサポートしています。

このモジュールでは、ベクトルデータ型を持つテーブルを作成し、ベクトルデータを含むレコードを挿入し、ベクトル検索のパフォーマンスを向上させるためにHNSWインデックスを使用します。

+ TiDBをベクトルストアとして使用することで、すぐに次のような利点が得られます：
  + 効率的な近似最近傍検索。
  + 計画的および非計画的なダウンタイムがゼロで無制限のスケール。
  + リレーショナルデータとベクトルデータの統一ストレージ、極めて大規模なボリュームにスケーラブル。

### 手順 1. ベクトルデータ型を持つテストテーブルを作成する

+ ベクトルデータ型を持つテーブルを作成し、ランダムデータ（32768行）を挿入します。テーブル名は`vs_example.vector_table`です。出力に示されているように、ベクトルデータ型の列は`embedding`です。

  ```sql
  CREATE DATABASE vs_example;
  USE vs_example;
  DROP SEQUENCE IF EXISTS vs_seq1;
  CREATE SEQUENCE vs_seq1;
  DROP TABLE IF EXISTS vector_table;
  CREATE TABLE vector_table (
      id BIGINT AUTO_RANDOM PRIMARY KEY,
      doc TEXT,
      embedding VECTOR(3)
  );
  SOURCE populate_vector_table.sql
  DESC vector_table;
  SELECT COUNT(1) FROM vector_table;
  SHOW CREATE TABLE vector_table\G
  ```

  ```output
  ...
  Query OK, 16384 rows affected (0.06 sec)
  Records: 16384 Duplicates: 0  Warnings: 0
  Query OK, 0 rows affected (0.10 sec)

  +-----------+-----------+------+------+---------+-------+
  | Field     | Type      | Null | Key  | Default | Extra |
  +-----------+-----------+------+------+---------+-------+
  | id        | bigint    | NO   | PRI  | NULL    |       |
  | doc       | text      | YES  |      | NULL    |       |
  | embedding | vector(3) | YES  |      | NULL    |       |
  +-----------+-----------+------+------+---------+-------+
  3 rows in set (0.00 sec)

  +----------+
  | COUNT(1) |
  +----------+
  |    32768 |
  +----------+
  1 row in set (0.02 sec)

  *************************** 1. row ***************************
         Table: vector_table
  Create Table: CREATE TABLE `vector_table` (
    `id` bigint NOT NULL /*T![auto_rand] AUTO_RANDOM(5) */,
    `doc` text DEFAULT NULL,
    `embedding` vector(3) DEFAULT NULL,
    PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![auto_rand_base] AUTO_RANDOM_BASE=625585 */
  1 row in set (0.01 sec)
  ```

### 手順 2. K-Nearest Neighbor 検索を実行し、実行計画を確認する

+ 次のクエリを確認します。これは、コサイン距離を使用した典型的なK-nearest neighbor検索を示しています。

+ アルゴリズムは、データセット内の参照ノード`[0.10752534,0.3524283,0.46134815]`に最も近いノード（上位3つ）を探します。ステップ1でランダムデータを使用したため、結果はサンプル出力とは異なります。

  ```sql
  SELECT doc, embedding
  FROM vector_table
  ORDER BY VEC_COSINE_DISTANCE(embedding, '[0.10752534,0.3524283,0.46134815]')
  LIMIT 3;
  ```

  ```output
  +-------+-----------------------------------+
  | doc   | embedding                         |
  +-------+-----------------------------------+
  | 3343  | [0.10953534,0.3534283,0.46134815] |
  | 20717 | [0.18479975,0.59234893,0.7714548] |
  | 19597 | [0.20609,0.6679932,0.8865328]     |
  +-------+-----------------------------------+
  3 rows in set (0.02 sec)
  ```

+ 実行計画を確認して、クエリで`FullTableScan`が使用されていることを確認します。テーブル`vector_table`には32768行しかないため、応答時間は非常に迅速ですが、特に数十億行を扱う場合の潜在的なパフォーマンスへの影響を考慮することが重要です。`explain`出力の最下部の操作に注目してください。推定行数（`estRows`）は`3.00`よりもはるかに大きい数値です。

  ```sql
  EXPLAIN
  SELECT doc, embedding
  FROM vector_table
  ORDER BY VEC_COSINE_DISTANCE(embedding, '[0.10752534,0.3524283,0.46134815]')
  LIMIT 3;
  ```

  ```output
  +--------------------------------+----------+-----------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
  | id                             | estRows  | task      | access object      | operator info                                                                                                                                      |
  +--------------------------------+----------+-----------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
  | Projection_14                  | 3.00     | root      |                    | vs_example.vector_table.doc, vs_example.vector_table.embedding                                                                                     |
  | └─TopN_7                       | 3.00     | root      |                    | Column#4, offset:0, count:3                                                                                                                        |
  |   └─Projection_15              | 3.00     | root      |                    | vs_example.vector_table.doc, vs_example.vector_table.embedding, vec_cosine_distance(vs_example.vector_table.embedding, [0.11,0.35,0.46])->Column#4 |
  |     └─TableReader_13           | 3.00     | root      |                    | data:TopN_12                                                                                                                                       |
  |       └─TopN_12                | 3.00     | cop[tikv] |                    | vec_cosine_distance(vs_example.vector_table.embedding, [0.11,0.35,0.46]), offset:0, count:3                                                        |
  |         └─TableFullScan_11     | 30179.00 | cop[tikv] | table:vector_table | keep order:false                                                                                                                                   |
  +--------------------------------+----------+-----------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
  6 rows in set (0.00 sec)
  ```

### 手順 3. ベクトル列に HNSW インデックスを追加する

+ TiFlashレプリカを有効にします。

  ```sql
  ALTER TABLE vector_table SET TIFLASH REPLICA 1;
  ```

+ 列`embedding`にHNSWベクトルインデックスを作成します。HNSWインデックスは、近似精度（ほとんどの場合98％以上）で優れたパフォーマンスを提供します。

  ```sql
  CREATE VECTOR INDEX embedding_idx ON vector_table ((VEC_COSINE_DISTANCE(embedding))) USING HNSW;
  ```

+ 列が完全にインデックス化されていることを確認します。`ROWS_STABLE_INDEXED`が`32768`であることを確認します。

  ```sql
  SELECT * FROM information_schema.tiflash_indexes
  WHERE tidb_table='vector_table'\G
  ```

  ```output
  *************************** 1. row ***************************
            TIDB_DATABASE: vs_example
               TIDB_TABLE: vector_table
                 TABLE_ID: 114
              COLUMN_NAME: embedding
               INDEX_NAME: embedding_idx
                COLUMN_ID: 3
                 INDEX_ID: 1
               INDEX_KIND: HNSW
      ROWS_STABLE_INDEXED: 32768
  ROWS_STABLE_NOT_INDEXED: 0
       ROWS_DELTA_INDEXED: 0
   ROWS_DELTA_NOT_INDEXED: 0
            ERROR_MESSAGE:
         TIFLASH_INSTANCE: 10.90.4.131:3930
  1 row in set (0.00 sec)
  ```

### 手順 4. インデックス付きテーブルに対して K-Nearest Neighbor クエリを実行する

+ テーブル`vector_table`に対して同じk-nearest neighbors検索を実行します。

  ```sql
  SELECT doc, embedding
  FROM vector_table
  ORDER BY VEC_COSINE_DISTANCE(embedding, '[0.10752534,0.3524283,0.46134815]')
  LIMIT 3;
  ```

  ```output
  +-------+------------------------------------+
  | doc   | embedding                          |
  +-------+------------------------------------+
  | 3343  | [0.10953534,0.3534283,0.46134815]  |
  | 20717 | [0.18479975,0.59234893,0.7714548]  |
  | 24641 | [0.21949558,0.73159087,0.94375384] |
  +-------+------------------------------------+
  3 rows in set (0.03 sec)
  ```

### 手順 5. 実行計画を確認する

+ 実行計画を確認して、クエリで近似最近傍インデックス（`access object`列の`index:embedding_idx(embedding)`オブジェクトと`operator info`列の`annIndex:COSINE`オペレーター）が使用されていることを確認します。大規模に運用される本番環境では、テーブル`vector_table`がこのクエリを提供する際に`vector_table_noidx`よりも優れたパフォーマンスを発揮することが期待されます。`explain`出力の最下部の操作に注目してください。推定行数（`estRows`）は`3.00`のみであり、ステップ2の`TableFullScan`よりもはるかに効率的です。インデックス最適化に加えて、`task`列の`mpp[tiflash]`タスクも見つけることができます。これは、TiDBによって実装されたカラムナストレージクエリ最適化です。

  ```sql
  EXPLAIN
  SELECT doc, embedding
  FROM vector_table
  ORDER BY VEC_COSINE_DISTANCE(embedding, '[0.10752534,0.3524283,0.46134815]')
  LIMIT 3;
  ```

  ```output
  +--------------------------------------+---------+--------------+----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
  | id                                   | estRows | task         | access object                                      | operator info                                                                                                                                      |
  +--------------------------------------+---------+--------------+----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
  | Projection_28                        | 3.00    | root         |                                                    | vs_example.vector_table.doc, vs_example.vector_table.embedding                                                                                     |
  | └─TopN_10                            | 3.00    | root         |                                                    | Column#5, offset:0, count:3                                                                                                                        |
  |   └─Projection_29                    | 3.00    | root         |                                                    | vs_example.vector_table.doc, vs_example.vector_table.embedding, vec_cosine_distance(vs_example.vector_table.embedding, [0.11,0.35,0.46])->Column#5 |
  |     └─TableReader_25                 | 3.00    | root         |                                                    | MppVersion: 2, data:ExchangeSender_24                                                                                                              |
  |       └─ExchangeSender_24            | 3.00    | mpp[tiflash] |                                                    | ExchangeType: PassThrough                                                                                                                          |
  |         └─Projection_26              | 3.00    | mpp[tiflash] |                                                    | vs_example.vector_table.doc, vs_example.vector_table.embedding                                                                                     |
  |           └─TopN_23                  | 3.00    | mpp[tiflash] |                                                    | Column#4, offset:0, count:3                                                                                                                        |
  |             └─Projection_27          | 3.00    | mpp[tiflash] |                                                    | vs_example.vector_table.doc, vs_example.vector_table.embedding, vec_cosine_distance(vs_example.vector_table.embedding, [0.11,0.35,0.46])->Column#4 |
  |               └─TableFullScan_22     | 3.00    | mpp[tiflash] | table:vector_table, index:embedding_idx(embedding) | keep order:false, annIndex:COSINE(embedding..[0.11,0.35,0.46], limit:3)                                                                            |
  +--------------------------------------+---------+--------------+----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
  9 rows in set (0.00 sec)
  ```

+ 前述のように、HNSWインデックスはTiFlashに依存しています。TiDBのカラムナストレージTiFlashに不慣れな場合は、他のハンズオン演習 - Getting Started with TiDB Cloud Serverlessを確認するか、自己ペースのeラーニング[Introduction to TiDB](https://www.pingcap.com/education/course-introduction-to-tidb/)に登録してください。次のモジュールに進んでください。

+ このモジュールの3次元ベクトルとは異なり、実際のシナリオでは、埋め込みモデルによって生成された1536次元のベクトルなど、はるかに多くの次元を持つベクトルが関与することがよくあります。これらのトピックに焦点を当てたハンズオン演習も提供しています - [Generative AI at TiDB](/labs?category=Generative+AI+at+TiDB)。

+ 次のモジュールに進んでください。
