## 模块 3: 使用 TiKV MVCC 内存引擎来提升查询性能

在本模块中，参与这首先会创建 2 张定义、数据和 MVCC 历史快照相同的表，2 张表基于之前定义的 PLACEMENT POLICY 将 LEADER 角色的 Region 数据放在 2 个不同的 TiKV 节点上，其中一个 TiKV 节点开启了 TiKV MVCC 内存引擎特性，另一个没有开启。参与者分别对这 2 张表进行索引扫描和表扫描两类操作，并通过对于监控的观察来对比 TiKV MVCC 内存引擎特性对于性能的提升。

### 步骤 1. 用两个终端连接到 TiUP 控制机器

+ 通过点击以下链接打开两个 SSH 连接到你的实验虚拟机：
  + [终端 1](ssh://monitor1-ssh#1)
  + [终端 2](ssh://monitor1-ssh#2)

+ 现在你将有两个活动的终端会话。

### 步骤 2. 创建表

+ 在[终端 1](ssh://monitor1-ssh#1) 上，连接到 TiDB 服务器实例。

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```output
  mysql> 
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，创建 `test` 下的表 `tbl_ime_R1` 和 `tbl_ime_R2`，并了解创建表所用的 DDL 语句。

  ```sql
  DROP TABLE IF EXISTS test.tbl_ime_R1;
  CREATE TABLE test.tbl_ime_R1 (
  `ID` bigint (20) NOT NULL AUTO_INCREMENT,
  `pID` bigint (20) NOT NULL,
  `cID` bigint (20) NOT NULL,
  `sID` bigint (20) NOT NULL,
  `pState` varchar (100) NOT NULL,
  `cState` tinyint (4) DEFAULT NULL,
  `created_at` DATETIME,
  `updated_at` DATETIME,
  `txt` varchar(64) DEFAULT 'IMETEST',
  PRIMARY KEY (`sID`, `ID`),
  UNIQUE KEY `pcsID` (`pID`, `cID`, `sID`),
  KEY `pcStateIndex` (`pState`, `cState`)
  ) PLACEMENT POLICY = IME_R1;

  DROP TABLE IF EXISTS test.tbl_ime_R2;
  CREATE TABLE test.tbl_ime_R2 (
  `ID` bigint (20) NOT NULL AUTO_INCREMENT,
  `pID` bigint (20) NOT NULL,
  `cID` bigint (20) NOT NULL,
  `sID` bigint (20) NOT NULL,
  `pState` varchar (100) NOT NULL,
  `cState` tinyint (4) DEFAULT NULL,
  `created_at` DATETIME,
  `updated_at` DATETIME,
  `txt` varchar(64) DEFAULT 'IMETEST',
  PRIMARY KEY (`sID`, `ID`),
  UNIQUE KEY `pcsID` (`pID`, `cID`, `sID`),
  KEY `pcStateIndex` (`pState`, `cState`)
  ) PLACEMENT POLICY = IME_R2;

  EXIT;
  ```

  ```output
  mysql> CREATE TABLE test.tbl_ime_R1 (
      -> `ID` bigint (20) NOT NULL AUTO_INCREMENT,
      -> `pID` bigint (20) NOT NULL,
      -> `cID` bigint (20) NOT NULL,
      -> `sID` bigint (20) NOT NULL,
      -> `pState` varchar (100) NOT NULL,
      -> `cState` tinyint (4) DEFAULT NULL,
      -> `created_at` DATETIME,
      -> `updated_at` DATETIME,
      -> `txt` varchar(64) DEFAULT 'IMETEST',
      -> PRIMARY KEY (`sID`, `ID`),
      -> UNIQUE KEY `pcsID` (`pID`, `cID`, `sID`),
      -> KEY `pcStateIndex` (`pState`, `cState`)
      -> ) PLACEMENT POLICY = IME_R1;
  Query OK, 0 rows affected, 5 warnings (0.52 sec)

  mysql> 
  mysql> DROP TABLE IF EXISTS test.tbl_ime_R2;
  Query OK, 0 rows affected, 1 warning (0.00 sec)

  mysql> CREATE TABLE test.tbl_ime_R2 (
      -> `ID` bigint (20) NOT NULL AUTO_INCREMENT,
      -> `pID` bigint (20) NOT NULL,
      -> `cID` bigint (20) NOT NULL,
      -> `sID` bigint (20) NOT NULL,
      -> `pState` varchar (100) NOT NULL,
      -> `cState` tinyint (4) DEFAULT NULL,
      -> `created_at` DATETIME,
      -> `updated_at` DATETIME,
      -> `txt` varchar(64) DEFAULT 'IMETEST',
      -> PRIMARY KEY (`sID`, `ID`),
      -> UNIQUE KEY `pcsID` (`pID`, `cID`, `sID`),
      -> KEY `pcStateIndex` (`pState`, `cState`)
      -> ) PLACEMENT POLICY = IME_R2;
  Query OK, 0 rows affected, 5 warnings (0.52 sec)

  mysql> 
  mysql> EXIT;
  Bye
  ```

+ 通过创建表的 DDL 语句中对于 PLACEMENT POLICY 的定义，可以了解到：
  + 表 `tbl_ime_R1` 的 PLACEMENT POLICY 为 `IME_R1`，表示它所有的 LEADER 角色 Region 都存储在 `region1` 的 TiKV 节点上，对其进行的读写默认也在 `region1` 的 TiKV 节点上。
  + 表 `tbl_ime_R2` 的 PLACEMENT POLICY 为 `IME_R2`，表示它所有的 LEADER 角色 Region 都存储在 `region2` 的 TiKV 节点上，对其进行的读写默认也在 `region2` 的 TiKV 节点上。
  + 表 `tbl_ime_R1` 和 `tbl_ime_R2` 在 TiKV 集群中的存储如下图所示：

  ![IME_PART2_TABLES_5.png 70%](https://lab-static.pingcap.com/images/2025/8/20/05cea87a3e752536c404e0a246e6e72afcf7960c.png)

### 步骤 3. 初始化数据

+ 在[终端 1](ssh://monitor1-ssh#1) 上，连接到 TiDB 服务器实例。

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```output
  mysql> 
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，创建 2 个序列（SEQUENCE）对象，`test.seq_ime_R1` 和 `test.seq_ime_R2`，供接下来向表 `test.tbl_ime_R1` 和 `test.tbl_ime_R2` 插入数据使用。

  ```sql
  DROP SEQUENCE IF EXISTS test.seq_ime_R1;
  CREATE SEQUENCE test.seq_ime_R1;
  DROP SEQUENCE IF EXISTS test.seq_ime_R2;
  CREATE SEQUENCE test.seq_ime_R2;
  EXIT
  ```

  ```output
  mysql> DROP SEQUENCE IF EXISTS test.seq_ime_R1;
  Query OK, 0 rows affected (0.51 sec)

  mysql> CREATE SEQUENCE test.seq_ime_R1;
  Query OK, 0 rows affected (0.52 sec)

  mysql> DROP SEQUENCE IF EXISTS test.seq_ime_R2;
  Query OK, 0 rows affected (0.51 sec)

  mysql> CREATE SEQUENCE test.seq_ime_R2;
  Query OK, 0 rows affected (0.52 sec)

  mysql> EXIT
  Bye
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，使用 tiup bench 工具向表 `test.tbl_ime_R1` 中插入 1,000 行随机数据，每插入一行数据后会对该行数据进行 10 次更新操作（UPDATE），即产生 10 个 MVCC 快照，大约需要 1 分钟完成。

  ```bash
  cat > tbl_ime_query_R1.sql <<EOF
  SELECT @sID := 200001 + FLOOR(RAND() * 99999);
  INSERT INTO test.tbl_ime_R1 (pID, cID, sID, pState, cState, created_at, updated_at) VALUES (1 + FLOOR(RAND() * 99999), 100001 + FLOOR(RAND() * 99999), @sID, ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), 1 + FLOOR(RAND() * 29), NOW(), NOW());
  SELECT @ID := NEXTVAL(test.seq_ime_R1);
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  UPDATE test.tbl_ime_R1 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID;
  EOF
  tiup bench rawsql run -H ${HOST_DB1_PRIVATE_IP} -P 4000 --count 1000 --threads 1 --query-files tbl_ime_query_R1.sql
  ```

  ```output
  Starting component bench: /home/ec2-user/.tiup/components/bench/v1.12.0/tiup-bench rawsql run -H 10.90.1.40 -P 4000 --count 1000 --threads 1 --query-files tbl_ime_query_R1.sql
  [Current] TBL_IME_QUERY_R1: 0.00s
  [Current] TBL_IME_QUERY_R1: 0.00s
  [Current] TBL_IME_QUERY_R1: 0.00s
  [Current] TBL_IME_QUERY_R1: 0.00s
  [Current] TBL_IME_QUERY_R1: 0.00s
  Finished
  [Summary] TBL_IME_QUERY_R1: 0.00s
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，使用 tiup bench 工具向表 `test.tbl_ime_R2` 中插入 1,000 行随机数据， 每插入一行数据后会对该行数据进行 10 次更新操作（UPDATE），即产生 10 个 MVCC 快照，大约需要 1 分钟完成。

  ```bash
  cat > tbl_ime_query_R2.sql <<EOF
  SELECT @sID := 200001 + FLOOR(RAND() * 99999);
  INSERT INTO test.tbl_ime_R2 (pID, cID, sID, pState, cState, created_at, updated_at) VALUES (1 + FLOOR(RAND() * 99999), 100001 + FLOOR(RAND() * 99999), @sID, ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), 1 + FLOOR(RAND() * 29), NOW(), NOW());
  SELECT @ID := NEXTVAL(test.seq_ime_R2);
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  UPDATE test.tbl_ime_R2 SET pState = ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), cState = 1 + FLOOR(RAND() * 29) WHERE sID = @sID AND ID = @ID; 
  EOF
  tiup bench rawsql run -H ${HOST_DB1_PRIVATE_IP} -P 4000 --count 1000 --threads 1 --query-files tbl_ime_query_R2.sql
  ```

  ```output
  Starting component bench: /home/ec2-user/.tiup/components/bench/v1.12.0/tiup-bench rawsql run -H 10.90.1.40 -P 4000 --count 1000 --threads 1 --query-files tbl_ime_query_R2.sql
  [Current] TBL_IME_QUERY_R2: 0.00s
  [Current] TBL_IME_QUERY_R2: 0.00s
  [Current] TBL_IME_QUERY_R2: 0.00s
  [Current] TBL_IME_QUERY_R2: 0.00s
  [Current] TBL_IME_QUERY_R2: 0.00s
  [Current] TBL_IME_QUERY_R2: 0.00s
  Finished
  [Summary] TBL_IME_QUERY_R2: 0.00s
  ```

### 步骤 4. 提升索引扫描的查询性能

+ 在[终端 1](ssh://monitor1-ssh#1) 上，连接到 TiDB 服务器实例。

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```output
  mysql> 
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，运行下面 SQL 命令，对表 `test.tbl_ime_R1` 进行查询。

  ```sql
  SELECT sID, ID, txt FROM test.tbl_ime_R1 USE INDEX (pcStateIndex) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  ```

  ```output
  +--------+------+---------+
  | sID    | ID   | txt     |
  +--------+------+---------+
  | 200875 | 1425 | IMETEST |
  | 201896 | 2340 | IMETEST |
  ...
  | 296436 | 2098 | IMETEST |
  | 298352 | 2262 | IMETEST |
  +--------+------+---------+
  50 rows in set (0.01 sec)
  EXIT
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，运行下面 SQL 命令，对表 `test.tbl_ime_R2` 进行查询。

  ```sql
  SELECT sID, ID, txt FROM test.tbl_ime_R2 USE INDEX (pcStateIndex) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  ```

  ```output
  +--------+-----+---------+
  | sID    | ID  | txt     |
  +--------+-----+---------+
  | 202196 |  91 | IMETEST |
  | 203510 | 375 | IMETEST |
  ...
  | 299728 | 514 | IMETEST |
  | 299902 | 832 | IMETEST |
  +--------+-----+---------+
  50 rows in set (0.00 sec)
  mysql> EXIT
  Bye
  ```

+ 转到 [TiDB PD 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`。

  ![IME_PART3_DASHBOARD_INDEX_1.png 70%](https://lab-static.pingcap.com/images/2025/8/20/22d376120199a59922852df88503485e6e666be4.png)

+ 选取刚刚执行的查询表 `test.tbl_ime_R1` 的 SQL 命令（执行次数为 1），点击进入。

  ![IME_PART3_DASHBOARD_INDEX_2.png 70%](https://lab-static.pingcap.com/images/2025/8/20/a80f9ac47d2356173879fc28ef998fac0821c560.png)

+ 查看 `Execution Detail` 中的 `Execution Plan` 项目，根据算子 `IndexLookUp_14` 得知该查询采用索引扫描，找到算子 `Limit_13(Build)`，查看其 `Execution Info` 信息，发现 `total_process_keys` 为 `96`，`total_keys` 为 `2193`，说明表中需要扫描过滤的行数比实际存储的总行数少很多，所以该查询适合使用 TiKV MVCC 内存引擎来提升查询性能。（输出结果可能会略有不同）

  ![IME_PART3_DASHBOARD_INDEX_3.png 70%](https://lab-static.pingcap.com/images/2025/8/20/9db33fbe6b450f2d5797c07c7d739218b087d975.png)

+ 选取刚刚执行的查询表 `test.tbl_ime_R2` 的 SQL 命令（执行次数为 1），点击进入。

  ![IME_PART3_DASHBOARD_INDEX_4.png 70%](https://lab-static.pingcap.com/images/2025/8/20/5de60326bf7ec54e6f7dd7cb2706e70bede561ff.png)

+ 查看 `Execution Detail` 中的 `Execution Plan` 项目，发现其执行计划与上一条 SQL 命令相同， 都采用索引扫描算子，如下图所示：

  ![IME_PART3_DASHBOARD_INDEX_5.png 70%](https://lab-static.pingcap.com/images/2025/8/20/7110b4053fa2dc3145d44e578fa54d9b2bff182a.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，使用 tiup bench 工具对表 `test.tbl_ime_R1` 进行扫描，扫描会持续进行，请不要等待扫描结束并马上执行后面的操作。(扫描大概会持续 3~5 分钟。)

  ```bash
  cat > index_scan_tbl_ime_R1.sql <<EOF
  SELECT sID, ID, txt FROM test.tbl_ime_R1 USE INDEX (pcStateIndex) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  EOF
  tiup bench rawsql run -H ${HOST_DB1_PRIVATE_IP} -P 4000 --count 100000 --threads 1 --query-files index_scan_tbl_ime_R1.sql
  ```

  ```output
  Starting component bench: /home/ec2-user/.tiup/components/bench/v1.12.0/tiup-bench rawsql run -H 10.90.1.40 -P 4000 --count 100000 --threads 1 --query-files index_scan_tbl_ime_R1.sql
  [Current] INDEX_SCAN_TBL_IME_R1: 0.00s
  [Current] INDEX_SCAN_TBL_IME_R1: 0.00s
  ...
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，并设置监控时间范围为 15 分钟，同时将刷新频率改为 5 秒。

  ![IME_PART3_TiKV_DETAIL_INDEX_1.png 70%](https://lab-static.pingcap.com/images/2025/8/20/de118edc706857e83407e65968e1a7be7eca79dc.png)

+ 在 `tidb-demo-TiKV-Details` 的 `In Memory Engine` 标签，通过 `Region Cache Hit`，`Region Cache Hit Rate` 和 `Region Cache Miss Reason` 面板了解到，目前`region1` 的 TiKV 节点没有开启 TiKV MVCC 内存引擎， 所以没有显示，当前查询并没有使用 TiKV MVCC 内存引擎，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_2.png 70%](https://lab-static.pingcap.com/images/2025/8/20/4c1bf579870186fd2737072fda121448992c9473.png)

+ 在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签，通过 `Total Ops Details by CF (Index Scan)` 面板了解到目前 `region1` 的 TiKV 节点的 coprocessor 中对于 RocksDB 的每个 CF 中 index 的 scan 过程中各种事件发生的速率(`Total Ops Details by CF (Index Scan)` 面板显示执行索引读时每秒发送到 RocksDB 的 Column Family (CF) 中的事件数，用户可以通过其来监控索引读时存储层的负载程度)，例如 `write-next` 事件大约为 400K ops/m 以上， `write-processed_keys` 事件大约为 20.0K ops/m 以上， 如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_3.png 70%](https://lab-static.pingcap.com/images/2025/8/20/ad42056f7be0ad77f4d4aad46c40b631bb421b15.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签，通过 `99% gRPC message duration` 面板（`99% gRPC message duration` 面板显示 99% TiKV 各种消息在单位时间内的最大延迟， 能够帮助用户定位特定类型的消息，比如 `coprocessor`， `kv_commit` 等，在 TiKV 中处理的性能瓶颈）了解到目前 `coprocessor` 的执行 99% 执行延迟大概在 `1ms` 左右范围，如下图所示（可以点击图例中的 `coprocessor` 标签便于查看）：

  ![IME_PART3_TiKV_DETAIL_INDEX_4.png 70%](https://lab-static.pingcap.com/images/2025/8/20/d73a6268beaffed0ea5a8c2d3062bd9b6cca4d03.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，等待对表 `test.tbl_ime_R1` 的扫描结束，或者按**CTRL-C**终止扫描。

  ```output
  ...
  [Current] INDEX_SCAN_TBL_IME_R1: 0.00s
  [Current] INDEX_SCAN_TBL_IME_R1: 0.00s
  Finished
  [Summary] INDEX_SCAN_TBL_IME_R1: 0.00s
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签观察 `Total Ops Details by CF (Index Scan)` 面板中相关事件的速率开始下降，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_5.png 70%](https://lab-static.pingcap.com/images/2025/8/20/5fca4b2f57d7b76f7dfa22c740709c6e8a77af14.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签观察 `99% gRPC message duration` 中 `coprocessor` 的执行 99% 执行延迟开始下降，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_6.png 70%](https://lab-static.pingcap.com/images/2025/8/20/31bc2eb6a9131c047094ea3d43541b8cb423a9f0.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，使用 tiup bench 工具对表 `test.tbl_ime_R2` 进行扫描，扫描会持续进行，请不要等待扫描结束并马上执行后面的操作。(扫描大概会持续 3~5 分钟。)

  ```bash
  cat > index_scan_tbl_ime_R2.sql <<EOF
  SELECT sID, ID, txt FROM test.tbl_ime_R2 USE INDEX (pcStateIndex) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  EOF
  tiup bench rawsql run -H ${HOST_DB1_PRIVATE_IP} -P 4000 --count 100000 --threads 1 --query-files index_scan_tbl_ime_R2.sql
  ```

  ```output
  Starting component bench: /home/ec2-user/.tiup/components/bench/v1.12.0/tiup-bench rawsql run -H 10.90.1.40 -P 4000 --count 100000 --threads 1 --query-files index_scan_tbl_ime_R2.sql
  [Current] INDEX_SCAN_TBL_IME_R2: 0.00s
  [Current] INDEX_SCAN_TBL_IME_R2: 0.00s
  ...
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `In Memory Engine` 标签，通过 `Region Cache Hit`，`Region Cache Hit Rate` 和 `Region Cache Miss Reason` 面板了解到，目前`region2` 的 TiKV 节点正在使用 TiKV MVCC 内存引擎并且 `Region Cache Hit Rate` 接近 `100%`，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_7.png 70%](https://lab-static.pingcap.com/images/2025/8/20/8e57d8b9c4b106f92d77df6a98976e798f7166c5.png)

+ 在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签，通过 `Total Ops Details by CF (Index Scan)` 面板了解到目前 `region2` 的 TiKV 节点的 coprocessor 中对于 RocksDB 的每个 CF 中 index 的 scan 过程中各种事件发生的速率，例如 `write-next` 和 `write-processed_keys` 事件大约为 30K ops/m ～ 40K ops/m， 相比上一个在 `region1` 的 TiKV 节点上没有使用 TiKV MVCC 内存引擎的查询 `write-next` 事件发生速率大幅降低，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_8.png 70%](https://lab-static.pingcap.com/images/2025/8/20/1cbcfc000927b6e01c90c234f986a9c239439c75.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签，通过 `99% gRPC message duration` 面板了解到目前 `coprocessor` 的 99% 执行延迟大概在 395μs 左右范围，相比上一个在 `region1` 的 TiKV 节点上没有使用 TiKV MVCC 内存引擎的查询也有降低，同时，`gRPC message count` 面板中`coprocessor` 的频率有所提升，如下图所示（可以点击图例中的 `coprocessor` 标签便于查看）：

  ![IME_PART3_TiKV_DETAIL_INDEX_9.png 70%](https://lab-static.pingcap.com/images/2025/8/20/16b5a73834e7fc9557a72ec387cc73a31b115566.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，等待对表 `test.tbl_ime_R2` 的扫描结束，或者按**CTRL-C**终止扫描。

  ```output
  ...
  [Current] INDEX_SCAN_TBL_IME_R2: 0.00s
  [Current] INDEX_SCAN_TBL_IME_R2: 0.00s
  Finished
  [Summary] INDEX_SCAN_TBL_IME_R2: 0.00s
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签观察 `Total Ops Details by CF (Index Scan)` 面板中相关事件的速率开始下降，并可以对比出两次查询对于 RocksDB 的扫描情况，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_10.png 70%](https://lab-static.pingcap.com/images/2025/8/20/1b0c334b77e4293542a525f0f3b1ac1307959394.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签观察 `99% gRPC message duration` 中 `coprocessor` 的执行 99% 执行延迟开始下降，并可以对比出两次查询的 99% 执行延迟情况，如下图所示：

  ![IME_PART3_TiKV_DETAIL_INDEX_11.png 70%](https://lab-static.pingcap.com/images/2025/8/20/d9abcad18eeb6866189449368ff1fba09fd27fdc.png)

### 步骤 5. 提升表扫描的查询性能

+ 在[终端 1](ssh://monitor1-ssh#1) 上，连接到 TiDB 服务器实例。

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```output
  mysql> 
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，运行下面 SQL 命令，对表 `test.tbl_ime_R1` 进行查询。

  ```sql
  SELECT sID, ID, txt FROM test.tbl_ime_R1 IGNORE INDEX (pcStateIndex, pcsID) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  ```

  ```output
  +--------+------+---------+
  | sID    | ID   | txt     |
  +--------+------+---------+
  | 200208 | 3411 | IMETEST |
  | 200228 | 1097 | IMETEST |
  ...
  | 202804 |   46 | IMETEST |
  | 202820 | 2306 | IMETEST |
  +--------+------+---------+
  50 rows in set (0.01 sec)
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上，运行下面 SQL 命令，对表 `test.tbl_ime_R2` 进行查询。

  ```sql
  SELECT sID, ID, txt FROM test.tbl_ime_R2 IGNORE INDEX (pcStateIndex, pcsID) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  EXIT
  ```

  ```output
  +--------+-----+---------+
  | sID    | ID  | txt     |
  +--------+-----+---------+
  | 200693 | 554 | IMETEST |
  | 200694 | 200 | IMETEST |
  ...
  | 214020 | 978 | IMETEST |
  | 214956 | 644 | IMETEST |
  +--------+-----+---------+
  50 rows in set (0.00 sec)

  mysql> EXIT
  Bye
  ```

+ 转到 [TiDB PD 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`。

  ![IME_PART3_DASHBOARD_TABLE_1.png 70%](https://lab-static.pingcap.com/images/2025/8/20/398f59c3dc85baace38caa0341f4c143c2cafc19.png)

+ 选取刚刚执行的查询表 `test.tbl_ime_R1` 的 SQL 命令（执行次数为 1），点击进入。

  ![IME_PART3_DASHBOARD_TABLE_2.png 70%](https://lab-static.pingcap.com/images/2025/8/20/a064e6e3cf0b6052ab97c490e7b2e6b33cce3103.png)

+ 查看 `Execution Detail` 中的 `Execution Plan` 项目，根据算子 `TableFullScan_10` 得知该查询采用全表扫描（注意：此时可能有 2 个执行计划，采用全表扫描的执行计划是刚刚执行过的），找到算子 `Limit_12`，查看其 `Execution Info` 信息，发现 `total_process_keys` 为 `224`，`total_keys` 为 `1793`，说明表中需要扫描过滤的行数比实际存储的总行数少很多，所以该查询适合使用 TiKV MVCC 内存引擎来提升查询性能。（输出结果可能会略有不同）

  ![IME_PART3_DASHBOARD_TABLE_3.png 70%](https://lab-static.pingcap.com/images/2025/8/20/c18939929796ede4565f4f3be0ad7edaa4b26267.png)

+ 选取刚刚执行的查询表 `test.tbl_ime_R2` 的 SQL 命令（执行次数为 1），点击进入。

  ![IME_PART3_DASHBOARD_TABLE_4.png 70%](https://lab-static.pingcap.com/images/2025/8/20/28cfe70c689c307a5f48b8508bd14ffa0e732538.png)

+ 查看 `Execution Detail` 中的 `Execution Plan` 项目，发现其执行计划与上一条 SQL 命令相同， 都采用全表扫描算子（注意：此时可能有 2 个执行计划，采用全表扫描的执行计划是刚刚执行过的），如下图所示：

  ![IME_PART3_DASHBOARD_TABLE_5.png 70%](https://lab-static.pingcap.com/images/2025/8/20/ff1c6801cc846790d2ad88d2de7502a9dbf73704.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，使用 tiup bench 工具对表 `test.tbl_ime_R1` 进行扫描，扫描会持续进行，请不要等待扫描结束并马上执行后面的操作。(扫描大概会持续 3~5 分钟。)

  ```bash
  cat > table_scan_tbl_ime_R1.sql <<EOF
  SELECT sID, ID, txt FROM test.tbl_ime_R1 IGNORE INDEX (pcStateIndex, pcsID) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  EOF
  tiup bench rawsql run -H ${HOST_DB1_PRIVATE_IP} -P 4000 --count 100000 --threads 1 --query-files table_scan_tbl_ime_R1.sql
  ```

  ```output
  Starting component bench: /home/ec2-user/.tiup/components/bench/v1.12.0/tiup-bench rawsql run -H 10.90.1.40 -P 4000 --count 100000 --threads 1 --query-files table_scan_tbl_ime_R1.sql
  [Current] TABLE_SCAN_TBL_IME_R1: 0.00s
  [Current] TABLE_SCAN_TBL_IME_R1: 0.00s
  ...
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `In Memory Engine` 标签，通过 `Region Cache Hit`，`Region Cache Hit Rate` 和 `Region Cache Miss Reason` 面板了解到，目前`region1` 的 TiKV 节点没有开启 TiKV MVCC 内存引擎， 所以没有显示，当前查询并没有使用 TiKV MVCC 内存引擎，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_2.png 70%](https://lab-static.pingcap.com/images/2025/8/20/eb160f376e4eeeeba5942385b06d4c0aebfa0309.png)

+ 在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签，通过 `Total Ops Details by CF (Table Scan)` 面板了解到目前 `region1` 的 TiKV 节点的 coprocessor 中对于 RocksDB 的每个 CF 中 table 的 scan 过程中各种事件发生的速率，例如 `write-next` 事件大约为 400K ops/m 以上， `write-processed_keys` 事件大约为 70K ops/m 以上，`write-seek` 事件大约为 60K ops/m 以上，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_3.png 70%](https://lab-static.pingcap.com/images/2025/8/20/e0871b4eed23ac0ef4ca21749d51d3bfcaab912c.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签，通过 `99% gRPC message duration` 面板了解到目前 `coprocessor` 的执行 99% 执行延迟大概在 `1ms` 左右范围，如下图所示（可以点击图例中的 `coprocessor` 标签便于查看）：

  ![IME_PART3_TiKV_DETAIL_TABLE_4.png 70%](https://lab-static.pingcap.com/images/2025/8/20/99e898e6e46febbe133885ff09f910b22828db8a.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，等待对表 `test.tbl_ime_R1` 的扫描结束，或者按**CTRL-C**终止扫描。

  ```output
  ...
  [Current] TABLE_SCAN_TBL_IME_R1: 0.00s
  [Current] TABLE_SCAN_TBL_IME_R1: 0.00s
  Finished
  [Summary] TABLE_SCAN_TBL_IME_R1: 0.00s
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签观察 `Total Ops Details by CF (Table Scan)` 面板中相关事件的速率开始下降，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_5.png 70%](https://lab-static.pingcap.com/images/2025/8/20/34e236c12adf4de7dd80c37b59dc82a0df74ee9d.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签观察 `99% gRPC message duration` 中 `coprocessor` 的执行 99% 执行延迟开始下降，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_6.png 70%](https://lab-static.pingcap.com/images/2025/8/20/4b45d2214274ece8dabdb0816a2eab112c042c0f.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，使用 tiup bench 工具对表 `test.tbl_ime_R2` 进行扫描，扫描会持续进行，请不要等待扫描结束并马上执行后面的操作。(扫描大概会持续 3~5 分钟。)

  ```bash
  cat > table_scan_tbl_ime_R2.sql <<EOF
  SELECT sID, ID, txt FROM test.tbl_ime_R2 IGNORE INDEX (pcStateIndex, pcsID) WHERE ((pState = 's1') OR (pState = 's2') OR (pState = 's3')) OR ((pState = 's3' AND cState = 10) OR (pState = 's3' AND cState = 20)) AND NOT (sID = 200001 AND ID = 1097) AND NOT (sID = 0 AND ID = 0) LIMIT 50;
  EOF
  tiup bench rawsql run -H ${HOST_DB1_PRIVATE_IP} -P 4000 --count 100000 --threads 1 --query-files table_scan_tbl_ime_R2.sql
  ```

  ```output
  Starting component bench: /home/ec2-user/.tiup/components/bench/v1.12.0/tiup-bench rawsql run -H 10.90.2.164 -P 4000 --count 100000 --threads 1 --query-files table_scan_tbl_ime_R2.sql
  [Current] TABLE_SCAN_TBL_IME_R2: 0.00s
  [Current] TABLE_SCAN_TBL_IME_R2: 0.00s
  ...
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `In Memory Engine` 标签，通过 `Region Cache Hit`，`Region Cache Hit Rate` 和 `Region Cache Miss Reason` 面板了解到，目前`region2` 的 TiKV 节点正在使用 TiKV MVCC 内存引擎并且 `Region Cache Hit Rate` 接近 `100%`，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_7.png 70%](https://lab-static.pingcap.com/images/2025/8/20/8377b214517b9c937a309c8e7e7e4b82c1ed7093.png)

+ 在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签，通过 `Total Ops Details by CF (Table Scan)` 面板了解到目前 `region2` 的 TiKV 节点的 coprocessor 中对于 RocksDB 的每个 CF 中 table 的 scan 过程中各种事件发生的速率，例如 `write-next` 和 `write-processed_keys` 事件大约为 90K ops/m ～ 150K ops/m， `write-seek` 事件大约为 450 ops/m左右范围， 相比上一个在 `region1` 的 TiKV 节点上没有使用 TiKV MVCC 内存引擎的查询，相关事件发生速率大幅降低，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_8.png 70%](https://lab-static.pingcap.com/images/2025/8/20/d36cf2c3da511834019a24fc061701776e5ed39b.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签，通过 `99% gRPC message duration` 面板了解到目前 `coprocessor` 的 99% 执行延迟大概在 743μs 左右范围，相比上一个在 `region1` 的 TiKV 节点上没有使用 TiKV MVCC 内存引擎的查询有所下降，通过 `gRPC message count` 面板了解到 `coprocessor` 的频率有所提升，如下图所示（可以点击图例中的 `coprocessor` 标签便于查看）：

  ![IME_PART3_TiKV_DETAIL_TABLE_9.png 70%](https://lab-static.pingcap.com/images/2025/8/20/5859487265ca34e1409ed6eea1b1e3d2cfab982b.png)

+ 在[终端 2](ssh://monitor1-ssh#2) 上，等待对表 `test.tbl_ime_R2` 的扫描结束，或者按**CTRL-C**终止扫描。

  ```output
  ...
  [Current] INDEX_SCAN_TBL_IME_R2: 0.00s
  [Current] INDEX_SCAN_TBL_IME_R2: 0.00s
  Finished
  [Summary] INDEX_SCAN_TBL_IME_R2: 0.00s
  ```

+ 回到 [Grafana 监控](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:3000)，在 `tidb-demo-TiKV-Details` 的 `Coprocessor Detail` 标签观察 `Total Ops Details by CF (Table Scan)` 面板中相关事件的速率开始下降，并可以对比出两次查询对于 RocksDB 的扫描情况，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_10.png 70%](https://lab-static.pingcap.com/images/2025/8/20/24e71cc077ee387f4b8c980b398a1d8f1257f541.png)

+ 在 `tidb-demo-TiKV-Details` 的 `gRPC` 标签观察 `99% gRPC message duration` 中 `coprocessor` 的执行 99% 执行延迟开始下降，并可以对比出两次查询的 99% `coprocessor` 执行延迟情况，如下图所示：

  ![IME_PART3_TiKV_DETAIL_TABLE_11.png 70%](https://lab-static.pingcap.com/images/2025/8/20/e0de9ea57d7ae9cd97c65f6fd545ccfd57fb9989.png)

### 步骤 6. 清理数据

+ 在[终端 1](ssh://monitor1-ssh#1) 上，连接到 TiDB 服务器实例。

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} --port 4000 -u root
  ```

  ```output
  mysql> 
  ```

+ 在[终端 1](ssh://monitor1-ssh#1) 上， 清理本模块使用的所有数据。

  ```sql
  DROP TABLE IF EXISTS test.tbl_ime_R1;
  DROP TABLE IF EXISTS test.tbl_ime_R2;

  DROP SEQUENCE IF EXISTS test.seq_ime_R1;
  DROP SEQUENCE IF EXISTS test.seq_ime_R2;

  EXIT
  ```

  ```output
  mysql> DROP TABLE IF EXISTS test.tbl_ime_R1;
  Query OK, 0 rows affected (0.51 sec)

  mysql> DROP TABLE IF EXISTS test.tbl_ime_R2;
  Query OK, 0 rows affected (0.52 sec)

  mysql> 
  mysql> DROP SEQUENCE IF EXISTS test.seq_ime_R1;
  Query OK, 0 rows affected (0.51 sec)

  mysql> DROP SEQUENCE IF EXISTS test.seq_ime_R2;
  Query OK, 0 rows affected (0.51 sec)

  mysql> 
  mysql> EXIT
  Bye
  ```

+ 本模块到此结束。
