## モジュール 6: TiKV MVCC インメモリエンジン (v8.5.0)

マルチバージョン同時実行制御（MVCC）は、現代のデータベースの重要な側面であり、整合性を損なうことなく同時アクセスを可能にします。TiKV MVCCインメモリエンジンは、MVCCの履歴バージョンを大量にスキャンする必要があるクエリのパフォーマンスを向上させるために主に使用されます。最適化されたクエリでも、多くのMVCCバージョンをスキャンするために遅いクエリになることがあります。

TiKV MVCCインメモリエンジンは、次のシナリオでクエリパフォーマンスを向上させるのに適しています：

+ 頻繁に更新または削除されるレコードをクエリする必要があるアプリケーション。
+ TiDBで履歴バージョンを長期間保持するために[tidb_gc_life_time](https://docs.pingcap.com/tidb/stable/system-variable-reference/#tidb_gc_life_time)を調整する必要があるアプリケーション。

### 手順 1. デフォルトの in-memory-engine 設定を確認する

+ TiDBに接続します。

  ```bash
  mysql -h ${HOST_TIPROXY1_PRIVATE_IP} --port 6000 -u root
  ```

  ```output
  mysql> 
  ```

+ TiKV MVCC `in-memory-engine`がデフォルトでオフになっていることを確認します。

  ```sql
  SHOW CONFIG WHERE type='tikv' AND name='in-memory-engine.enable';
  ```

  ```output
  +------+-------------------+-------------------------+-------+
  | Type | Instance          | Name                    | Value |
  +------+-------------------+-------------------------+-------+
  | tikv | 10.90.1.41:20160  | in-memory-engine.enable | false |
  | tikv | 10.90.1.223:20160 | in-memory-engine.enable | false |
  | tikv | 10.90.1.231:20160 | in-memory-engine.enable | false |
  +------+-------------------+-------------------------+-------+
  3 rows in set (0.01 sec)
  ```

+ この演習の期間中に以前のバージョンを保持できるように`tidb_gc_life_time`を増やします。

  ```sql
  SET GLOBAL tidb_gc_life_time='1440m';
  ```

### 手順 2. in-memory-engine 設定が OFF の状態での動作を確認する

+ 同じスキーマを持つ2つのテストテーブル`ime_test1`と`ime_test2`を設定します。各テーブルには`content`列にインデックスがあります。

  ```sql
  DROP TABLE IF EXISTS test.ime_test1;
  DROP TABLE IF EXISTS test.ime_test2;
  CREATE TABLE test.ime_test1 (
      `id` BIGINT AUTO_RANDOM PRIMARY KEY,
      `content` INT,
      `created_at` DATETIME,
      `updated_at` DATETIME,
      `version` INT,
      KEY content_idx (`content`)
  );
  CREATE TABLE test.ime_test2 (
      `id` BIGINT AUTO_RANDOM PRIMARY KEY,
      `content` INT,
      `created_at` DATETIME,
      `updated_at` DATETIME,
      `version` INT,
      KEY content_idx (`content`)
  );
  ```

+ 両方のテーブルに1行を挿入します。

  ```sql
  INSERT INTO test.ime_test1 (
      content, created_at, updated_at, version
  ) VALUES (
      FLOOR(1 + (RAND() * 99999999)), NOW(), NOW(), 1
  );
  INSERT INTO test.ime_test2 (
      content, created_at, updated_at, version
  ) VALUES (
      FLOOR(1 + (RAND() * 99999999)), NOW(), NOW(), 1
  );
  SELECT * FROM test.ime_test1;
  SELECT * FROM test.ime_test2;
  EXIT
  ```

+ 両方のテーブルを32768行に拡張します。

  ```bash
  cat > double.sql <<EOF
  INSERT INTO test.ime_test1 (
      content, created_at, updated_at, version
  )
  SELECT FLOOR(1 + (RAND() * 99999999)), NOW(), NOW(), 1 FROM test.ime_test1;
  INSERT INTO test.ime_test2 (
      content, created_at, updated_at, version
  )
  SELECT FLOOR(1 + (RAND() * 99999999)), NOW(), NOW(), 1 FROM test.ime_test2;
  EOF
  cat > double.sh <<EOF
  mysql -h ${HOST_TIPROXY1_PRIVATE_IP} --port 6000 -u root < double.sql
  EOF
  chmod +x double.sh
  for c in {1..15}; do
  ./double.sh
  done;
  ```

+ `test.ime_test2`テーブルの各行に対して100バージョンを生成し、`content`列に対して100回の更新操作を実行します。私たちの演習環境では（パフォーマンスが最適化されていないため）、約2分かかります。

  ```bash
  cat > update_hog.sql <<EOF
  UPDATE test.ime_test2
  SET content = FLOOR(1 + (RAND() * 99999999)), version = version + 1;
  EOF
  cat > update_hog.sh <<EOF
  mysql -h ${HOST_TIPROXY1_PRIVATE_IP} --port 6000 -u root < update_hog.sql
  EOF
  chmod +x update_hog.sh
  for c in {1..100}; do
  ./update_hog.sh
  done;
  ```

+ 再度TiDBに接続します。

  ```bash
  mysql -h ${HOST_TIPROXY1_PRIVATE_IP} --port 6000 -u root
  ```

  ```output
  mysql> 
  ```

+ テーブル`test.ime_test1`と`test.ime_test2`に対してフルテーブルスキャンを実行する2つのSQLステートメントを実行します。

  ```sql
  SELECT COUNT(*) FROM test.ime_test1;
  SELECT COUNT(*) FROM test.ime_test2;
  EXIT
  ```

  ```output
  +----------+
  | COUNT(*) |
  +----------+
  |    32768 |
  +----------+
  1 row in set (0.03 sec)

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

  Bye
  ```

+ [TiDB Dashboard](http://${LAB:HOST_PD1_PUBLIC_IP}:2379/dashboard)にアクセスし、ユーザー名`root`でログインします。現時点ではパスワードは不要です。`SQL Statements`パネルに移動し、表示時間を`Recent 5 min`に変更します。

  ![60%](https://lab-static.pingcap.com/quick-demo/ime-off-dashboard-time-1.png)

+ データベースを`test`に選択します。

  ![60%](https://lab-static.pingcap.com/quick-demo/ime-off-dashboard-db-2.png)

+ 表示される操作を`Select`と`Update`に設定します。

  ![60%](https://lab-static.pingcap.com/quick-demo/ime-off-dashboard-kind-3.png)

+ ダッシュボードの右上隅で`Columns`を選択し、メニュー項目`Mean Visible Version Per Query`と`Mean Meet Versions Per Query`をチェックし、`Query`ボタンをクリックします。

  ![60%](https://lab-static.pingcap.com/quick-demo/ime-off-dashboard-columns-4.png)

+ クエリ結果から、テーブル`test.ime_test1`のスキャンでは、`Mean Visible Version Per Query`が`Mean Meet Versions Per Query`と等しいことがわかります。これは、処理するMVCCスナップショットがないことを示しています。
対照的に、テーブル`test.ime_test2`のスキャンでは、`Mean Meet Versions Per Query`が`Mean Visible Version Per Query`よりもはるかに高いことがわかります。これは、スキャン中に大量のMVCCスナップショット処理が行われていることを示しています。これは、`Total Latency`と`Mean Latency`列からも明らかです。同じ行数をスキャンする場合、実行時間が異なり、テーブル`test.ime_test2`のスキャンではMVCCスナップショットのスキャンに多くの時間がかかっています。

  ![95%](https://lab-static.pingcap.com/quick-demo/ime-off-dashboard-sql-5.png)

+ [Grafana 監視](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000) をクリックし、ユーザー名とパスワード（両方とも「admin」）を使用してログインします。`Home` > `tidb-demo` > `tidb-demo-TikV-Details` > `In Memory Engine`に移動し、`Region Cache Hit`，`Region Cache Hit Rate`，`Region Cache Miss Reason`，`Memory Usage`および`Region Count`に関するパネルまでスクロールします。

  ![90%](https://lab-static.pingcap.com/quick-demo/ime-off-grafana-no-data-1.png)

+ 現在、これらのパネルは`No data`を表示しています。これは、現在のデータベースクラスタでTiKV MVCCインメモリエンジンが有効になっていないためです。`Absolute time range`を`Last 5 minutes`に変更します。

  ![60%](https://lab-static.pingcap.com/quick-demo/ime-off-grafana-time-range-2.png)

+ `Refresh time`を`10s`に変更します。

  ![60%](https://lab-static.pingcap.com/quick-demo/ime-off-grafana-refresh-time-3.png)

### 手順 3. TiKV MVCC インメモリエンジンを有効にする

+ [ターミナル 1](ssh://monitor1-ssh#1) に戻り、`tiup cluster edit-config`を使用して`vi`でTiDBクラスタの構成を開きます。

  ```bash
  export PATH="$PATH:/home/ec2-user/.tiup/bin"
  tiup cluster edit-config tidb-demo
  ```

+ TiKV MVCCインメモリエンジンを有効にするために、すべての`tikv_servers`に対して`in-memory-engine.capacity`を`1GiB`、`in-memory-engine.enable`を`true`、および`in-memory-engine.mvcc-amplification-threshold`を`100`に設定します。

+ 次のセクションを各TiKVの`os: linux`エントリの下にコピーし、`:wq`を使用して保存して終了します。

  ```yaml
    config:
      in-memory-engine.capacity: 1GiB
      in-memory-engine.enable: true
      in-memory-engine.mvcc-amplification-threshold: 50
  ```

  ```output
  ...
  tikv_servers:
  - host: 10.90.1.41
    ssh_port: 22
    port: 20160
    status_port: 20180
    deploy_dir: /tidb-deploy/tikv-20160
    data_dir: /tidb-data/tikv-20160
    log_dir: /tidb-deploy/tikv-20160/log
    arch: amd64
    os: linux
    config:
      in-memory-engine.capacity: 1GiB
      in-memory-engine.enable: true
      in-memory-engine.mvcc-amplification-threshold: 50
  - host: 10.90.1.223
    ssh_port: 22
    port: 20160
    status_port: 20180
    deploy_dir: /tidb-deploy/tikv-20160
    data_dir: /tidb-data/tikv-20160
    log_dir: /tidb-deploy/tikv-20160/log
    arch: amd64
    os: linux
    config:
      in-memory-engine.capacity: 1GiB
      in-memory-engine.enable: true
      in-memory-engine.mvcc-amplification-threshold: 50
  - host: 10.90.1.231
    ssh_port: 22
    port: 20160
    status_port: 20180
    deploy_dir: /tidb-deploy/tikv-20160
    data_dir: /tidb-data/tikv-20160
    log_dir: /tidb-deploy/tikv-20160/log
    arch: amd64
    os: linux
    config:
      in-memory-engine.capacity: 1GiB
      in-memory-engine.enable: true
      in-memory-engine.mvcc-amplification-threshold: 50
  ...
  ```

+ プロンプトに`y`と答えます。

  ```output
  Please check change highlight above, do you want to apply the change? [y/N]:(default=N) y
  Applying changes...
  Applied successfully, please use `tiup cluster reload tidb-demo [-N <nodes>] [-R <roles>]` to reload config.
  ```

+ `tiup cluster reload`を実行して構成をリロードします。

  ```bash
  tiup cluster reload tidb-demo --yes
  ```

  ```output
  ...
  Reloaded cluster `tidb-demo` successfully
  ```

+ 構成がリロードされた後、TiDBに接続します。

  ```bash
  mysql -h ${HOST_TIPROXY1_PRIVATE_IP} --port 6000 -u root
  ```

  ```output
  mysql> 
  ```

+ TiKV MVCC `in-memory-engine`が有効になっていることを確認します。

  ```sql
  SHOW CONFIG WHERE type='tikv' AND name='in-memory-engine.enable';
  EXIT
  ```

  ```output
  +------+-------------------+-------------------------+-------+
  | Type | Instance          | Name                    | Value |
  +------+-------------------+-------------------------+-------+
  | tikv | 10.90.1.41:20160  | in-memory-engine.enable | true  |
  | tikv | 10.90.1.223:20160 | in-memory-engine.enable | true  |
  | tikv | 10.90.1.231:20160 | in-memory-engine.enable | true  |
  +------+-------------------+-------------------------+-------+
  3 rows in set (0.02 sec)

  Bye
  ```

### 手順 4. in-memory-engine 設定が ON のときの動作を確認する

+ [Grafana 監視](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000) をクリックし、ユーザー名とパスワード（両方とも「admin」）を使用してログインします。`Home` > `tidb-demo` > `tidb-demo-TikV-Details` > `In Memory Engine`に移動し、`Region Cache Hit`，`Region Cache Hit Rate`，`Region Cache Miss Reason`，`Memory Usage`および`Region Count`に関するパネルまでスクロールします。現在、これらのパネルは`No data`を表示していませんが、`Region Cache Hit`、`Region Cache Hit Rate`、`Region Cache Miss Reason`、`Memory Usage`、および`Region Count`の値は非常に低く、ほぼゼロです。これは、現在MVCCスナップショットのクエリがないためです。

  ![90%](https://lab-static.pingcap.com/quick-demo/ime-on-grafana-no-query-1.png)

+ [ターミナル 1](ssh://monitor1-ssh#1) に戻り、コマンドラインから`test.ime_test1`テーブルに対して10,000回のスキャンを実行し、スキャンプロセス中に[Grafana 監視](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000) の`Home` > `tidb-demo` > `tidb-demo-TikV-Details` > `In Memory Engine`パネルを監視します。

  ```bash
  for i in `seq 10000`;
  do mysql -uroot -h ${HOST_TIPROXY1_PRIVATE_IP} -P 6000 -e "SELECT COUNT(*) FROM test.ime_test1;";
  done;
  ```

+ `test.ime_test1`テーブルのスキャンプロセス中、`tidb-demo-TikV-Details` > `In Memory Engine`パネルから、`Region Cache Hit`と`Region Cache Hit Rate`の両方にほとんど変化がないことが観察されます。（`Region Cache Hit`はデータがRegionキャッシュから正常に取得された回数を示し、`Region Cache Hit Rate`はRegionキャッシュのヒット率を表します。）これらの変化が少ない理由は、`test.ime_test1`テーブル自体にMVCCの履歴スナップショットがないためです。

  ![90%](https://lab-static.pingcap.com/quick-demo/ime-on-grafana-ime-test1.png)

+ `test.ime_test1`テーブルの10,000回のスキャンが終了した後、[ターミナル 1](ssh://monitor1-ssh#1) に戻り、コマンドラインから`test.ime_test2`テーブルに対してさらに50,000回のスキャンを実行し、再度スキャンプロセス中に[Grafana 監視](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000) の`Home` > `tidb-demo` > `tidb-demo-TikV-Details` > `In Memory Engine`パネルを監視します。

  ```bash
  for i in `seq 50000`;
  do mysql -uroot -h ${HOST_TIPROXY1_PRIVATE_IP} -P 6000 -e "SELECT COUNT(*) FROM test.ime_test2;";
  done;
  ```

+ `test.ime_test2`テーブルのスキャンプロセス中、`tidb-demo-TikV-Details` > `In Memory Engine`パネルから、`Region Cache Hit`と`Region Cache Hit Rate`の両方に大きな変化があることが観察されます。これは、`test.ime_test2`テーブル自体に大量のMVCC履歴スナップショットが含まれているためです。TiKV MVCCインメモリエンジンは、最新の書き込みMVCCバージョンをメモリにキャッシュし、TiDBとは独立したMVCC GCメカニズムを実装しています。その結果、スキャンプロセス中にTiKV MVCCインメモリエンジンのキャッシュから大量のデータが読み取られます。

  ![90%](https://lab-static.pingcap.com/quick-demo/ime-on-grafana-ime-test2.png)

+ TiKV MVCCインメモリエンジンがデータをタイムリーにキャッシュしない場合があります。この場合、上記の手順を繰り返し、再度`ime_test2`テーブルをスキャンし、監視パネルを観察してみてください。
