文档首页
MySQL 9.0 参考手册
相关文档 下载此手册
PDF(US Ltr) - 40.0Mb
PDF(A4) - 40.1Mb
手册页(TGZ) - 258.2Kb
手册页(Zip) - 365.3Kb
Info(Gzip) - 4.0Mb
Info(Zip) - 4.0Mb


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

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() 的等效表达式,如下所示,此 EXPLAIN 语句之后的 SHOW WARNINGS 语句的输出

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 列上创建索引,该函数使用表达式,该表达式可用于优化使用该表达式的查询。有关更多信息和示例,请参阅该函数的描述。

NDB Cluster 中的 JSON 列和间接索引

在 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