文档主页
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.

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

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

索引的虚拟列的值是 MVCC 日志记录的,以避免在回滚或清除操作期间不必要地重新计算生成列的值。日志记录的值的数据长度受索引键限制,对于 COMPACT 和 REDUNDANT 行格式为 767 字节,对于 DYNAMIC 和 COMPRESSED 行格式为 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。

用于创建表 jempn 的 CREATE 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.