文档主页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  二级索引和生成列

15.1.20.9 二级索引和生成列

InnoDB 支持在虚拟生成列上创建二级索引。不支持其他索引类型。在虚拟列上定义的二级索引有时被称为 虚拟索引.

可以在一个或多个虚拟列上创建二级索引,也可以在虚拟列和普通列或存储的生成列的组合上创建二级索引。包含虚拟列的二级索引可以定义为 UNIQUE.

当在虚拟生成列上创建二级索引时,生成列值会在索引记录中物化。如果索引是 覆盖索引(包含查询检索的所有列的索引),则会从索引结构中的物化值中检索生成列值,而不是动态计算。

在虚拟列上使用二级索引时,需要考虑额外的写入成本,因为在 INSERTUPDATE 操作期间,在二级索引记录中物化虚拟列值时会执行计算。即使有额外的写入成本,虚拟列上的二级索引也可能比生成的 存储 列更可取,因为生成的 存储 列是在聚簇索引中物化的,这会导致表更大,需要更多的磁盘空间和内存。如果没有在虚拟列上定义二级索引,则读取操作会有额外的成本,因为每次检查列的行时都必须计算虚拟列值。

索引的虚拟列的值是 MVCC 日志记录的,以避免在回滚或清除操作期间不必要地重新计算生成列的值。日志记录的值的数据长度受索引键限制,对于 COMPACTREDUNDANT 行格式为 767 字节,对于 DYNAMICCOMPRESSED 行格式为 3072 字节。

在虚拟列上添加或删除二级索引是一个就地操作。

索引生成列以提供 JSON 列索引

如其他地方所述,JSON 列无法直接索引。要创建间接引用此类列的索引,可以定义一个生成列来提取要索引的信息,然后在生成列上创建索引,如本示例所示

mysql> CREATE TABLE jemp (
    ->     c JSON,
    ->     g INT GENERATED ALWAYS AS (c->"$.id"),
    ->     INDEX i (g)
    -> );
Query OK, 0 rows affected (0.28 sec)

mysql> INSERT INTO jemp (c) VALUES
     >   ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'),
     >   ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');
Query OK, 4 rows affected (0.04 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT c->>"$.name" AS name
     >     FROM jemp WHERE g > 2;
+--------+
| name   |
+--------+
| Barney |
| Betty  |
+--------+
2 rows in set (0.00 sec)

mysql> EXPLAIN SELECT c->>"$.name" AS name
     >    FROM jemp WHERE g > 2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jemp
   partitions: NULL
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name'))
AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2)
1 row in set (0.00 sec)

(为了适应查看区域,我们已将本示例中最后一条语句的输出进行了换行。)

在对包含一个或多个使用 ->->> 运算符的表达式的 SELECT 或其他 SQL 语句使用 EXPLAIN 时,这些表达式将转换为使用 JSON_EXTRACT() 和(如果需要)JSON_UNQUOTE() 的等效表达式,如 SHOW WARNINGS 在此 EXPLAIN 语句后的输出中所示

mysql> EXPLAIN SELECT c->>"$.name"
     > FROM jemp WHERE g > 2 ORDER BY c->"$.name"\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jemp
   partitions: NULL
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using where; Using filesort
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name')) AS
`c->>"$.name"` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) order by
json_extract(`test`.`jemp`.`c`,'$.name')
1 row in set (0.00 sec)

有关其他信息和示例,请参阅 ->->> 运算符以及 JSON_EXTRACT()JSON_UNQUOTE() 函数的说明。

此技术也可以用于提供间接引用无法直接索引的其他类型列的索引,例如 GEOMETRY 列。

还可以使用 JSON_VALUE() 函数在 JSON 列上创建索引,该函数使用可以用于优化使用表达式的查询的表达式。有关更多信息和示例,请参阅该函数的说明。

JSON 列和 NDB Cluster 中的间接索引

也可以在 MySQL NDB Cluster 中使用 JSON 列的间接索引,但要满足以下条件

  1. NDB 在内部将 JSON 列值处理为 BLOB。这意味着任何具有一个或多个 JSON 列的 NDB 表都必须具有主键,否则它无法在二进制日志中记录。

  2. NDB 存储引擎不支持虚拟列的索引。由于生成列的默认值为 VIRTUAL,因此必须显式指定要应用间接索引的生成列为 STORED

用于创建表 jempnCREATE TABLE 语句是先前显示的 jemp 表的版本,已进行修改以使其与 NDB 兼容

CREATE TABLE jempn (
  a BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  c JSON DEFAULT NULL,
  g INT GENERATED ALWAYS AS (c->"$.id") STORED,
  INDEX i (g)
) ENGINE=NDB;

我们可以使用以下 INSERT 语句填充此表

INSERT INTO jempn (c) VALUES
  ('{"id": "1", "name": "Fred"}'),
  ('{"id": "2", "name": "Wilma"}'),
  ('{"id": "3", "name": "Barney"}'),
  ('{"id": "4", "name": "Betty"}');

现在 NDB 可以使用索引 i,如这里所示

mysql> EXPLAIN SELECT c->>"$.name" AS name
    ->           FROM jempn WHERE g > 2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jempn
   partitions: p0,p1,p2,p3
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 3
     filtered: 100.00
        Extra: Using pushed condition (`test`.`jempn`.`g` > 2)
1 row in set, 1 warning (0.01 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select
json_unquote(json_extract(`test`.`jempn`.`c`,'$.name')) AS `name` from
`test`.`jempn` where (`test`.`jempn`.`g` > 2)   
1 row in set (0.00 sec)

您应该记住,存储的生成列以及此类列上的任何索引都使用 DataMemory.