MySQL8-中文参考-十八-

MySQL8 中文参考(十八)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

10.9.3 优化提示

原文:dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html

控制优化器策略的一种方法是设置optimizer_switch系统变量(参见第 10.9.2 节,“可切换的优化”)。对此变量的更改会影响所有后续查询的执行;要使一个查询与另一个查询不同,需要在每个查询之前更改optimizer_switch

控制优化器的另一种方法是使用优化提示,可以在单个语句中指定。因为优化提示是基于每个语句的基础上应用的,所以它们比使用optimizer_switch可以更精细地控制语句执行计划。例如,您可以为语句中的一个表启用优化,并为另一个表禁用优化。语句中的提示优先于optimizer_switch标志。

示例:

SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
  FROM t3 WHERE f1 > 30 AND f1 < 33;
SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ NO_ICP(t1, t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...;
EXPLAIN SELECT /*+ NO_ICP(t1) */ * FROM t1 WHERE ...;
SELECT /*+ MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;
INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);

此处描述的优化提示与第 10.9.4 节,“索引提示”中描述的索引提示不同。优化提示和索引提示可以单独使用或一起使用。

  • 优化提示概述

  • 优化提示语法

  • 连接顺序优化提示

  • 表级别优化提示

  • 索引级别优化提示

  • 子查询优化提示

  • 语句执行时间优化提示

  • 变量设置提示语法

  • 资源组提示语法

  • 用于命名查询块的优化提示

优化提示概述

优化提示适用于不同的作用域级别:

  • 全局: 提示影响整个语句

  • 查询块: 提示影响语句中的特定查询块

  • 表级别: 提示影响查询块内的特定表

  • 索引级别: 提示影响表内的特定索引

以下表格总结了可用的优化提示、它们影响的优化策略以及适用的范围。更多细节稍后给出。

表 10.2 可用的优化提示

提示名称 描述 适用范围
BKA, NO_BKA 影响批量键访问连接处理 查询块,表
BNL, NO_BNL 在 MySQL 8.0.20 之前:影响块嵌套循环连接处理;MySQL 8.0.18 及更高版本:还影响哈希连接优化;MySQL 8.0.20 及更高版本:仅影响哈希连接优化 查询块,表
DERIVED_CONDITION_PUSHDOWN, NO_DERIVED_CONDITION_PUSHDOWN 使用或忽略对物化派生表的派生条件推送优化(MySQL 8.0.22 中添加) 查询块,表
GROUP_INDEX, NO_GROUP_INDEX 使用或忽略指定的索引或索引进行GROUP BY操作中的索引扫描(MySQL 8.0.20 中添加) 索引
HASH_JOIN, NO_HASH_JOIN 影响哈希连接优化(仅适用于 MySQL 8.0.18) 查询块,表
INDEX, NO_INDEX 作为JOIN_INDEX, GROUP_INDEX, 和 ORDER_INDEX 的组合,或者NO_JOIN_INDEX, NO_GROUP_INDEX, 和 NO_ORDER_INDEX 的组合(MySQL 8.0.20 中添加) 索引
INDEX_MERGE, NO_INDEX_MERGE 影响索引合并优化 表,索引
JOIN_FIXED_ORDER 使用 FROM 子句中指定的表顺序作为连接顺序 查询块
JOIN_INDEX, NO_JOIN_INDEX 使用或忽略指定的索引或索引以供任何访问方法使用(MySQL 8.0.20 中添加) 索引
JOIN_ORDER 使用提示中指定的表顺序作为连接顺序 查询块
JOIN_PREFIX 使用提示中指定的表顺序作为连接顺序的第一个表 查询块
JOIN_SUFFIX 使用提示中指定的表顺序作为连接顺序的最后表 查询块
MAX_EXECUTION_TIME 限制语句执行时间 全局
MERGE, NO_MERGE 影响派���表/视图合并到外部查询块
MRR, NO_MRR 影响多范围读取优化 表,索引
NO_ICP 影响索引条件下推优化 表,索引
NO_RANGE_OPTIMIZATION 影响范围优化 表,索引
ORDER_INDEX, NO_ORDER_INDEX 使用或忽略指定的索引或索引以供对行进行排序(MySQL 8.0.20 中添加) 索引
QB_NAME 为查询块分配名称 查询块
RESOURCE_GROUP 在语句执行期间设置资源组 全局
SEMIJOIN, NO_SEMIJOIN 影响半连接策略;从 MySQL 8.0.17 开始,这也适用于反连接 查询块
SKIP_SCAN, NO_SKIP_SCAN 影响跳过扫描优化 表,索引
SET_VAR 在语句执行期间设置变量 全局
SUBQUERY 影响物化,IN-to-EXISTS子查询策略 查询块
提示名称 描述 适用范围

禁用优化会阻止优化器使用它。启用优化意味着优化器可以自由使用该策略,如果它适用于语句执行,而不是优化器一定会使用它。

优化器提示语法

MySQL 支持 SQL 语句中的注释,如第 11.7 节,“注释”所述。优化器提示必须在/*+ ... */注释中指定。也就是说,优化器提示使用一种/* ... */ C 风格注释语法的变体,其中在/*注释开头序列后跟着一个+字符。例如:

/*+ BKA(t1) */
/*+ BNL(t1, t2) */
/*+ NO_RANGE_OPTIMIZATION(t4 PRIMARY) */
/*+ QB_NAME(qb2) */

+字符后允许空格。

解析器在SELECTUPDATEINSERTREPLACEDELETE语句的初始关键字后识别优化器提示注释。在这些上下文中允许使用提示:

  • 在查询和数据更改语句的开头:

    SELECT /*+ ... */ ...
    INSERT /*+ ... */ ...
    REPLACE /*+ ... */ ...
    UPDATE /*+ ... */ ...
    DELETE /*+ ... */ ...
    
  • 在查询块的开头:

    (SELECT /*+ ... */ ... )
    (SELECT ... ) UNION (SELECT /*+ ... */ ... )
    (SELECT /*+ ... */ ... ) UNION (SELECT /*+ ... */ ... )
    UPDATE ... WHERE x IN (SELECT /*+ ... */ ...)
    INSERT ... SELECT /*+ ... */ ...
    
  • 在以EXPLAIN为前缀的可提示语句中。例如:

    EXPLAIN SELECT /*+ ... */ ...
    EXPLAIN UPDATE ... WHERE x IN (SELECT /*+ ... */ ...)
    

    这意味着您可以使用EXPLAIN查看优化器提示如何影响执行计划。在EXPLAIN之后立即使用SHOW WARNINGS查看提示的使用情况。由后续的SHOW WARNINGS显示的扩展EXPLAIN输出指示使用了哪些提示。被忽略的提示不会显示。

一个提示注释可以包含多个提示,但一个查询块不能包含多个提示注释。这是有效的:

SELECT /*+ BNL(t1) BKA(t2) */ ...

但这是无效的:

SELECT /*+ BNL(t1) */ /* BKA(t2) */ ...

当一个提示注释包含多个提示时,存在重复和冲突的可能性。以下是一般准则。对于特定的提示类型,可能会适用额外的规则,如提示描述中所示。

  • 重复的提示:对于像/*+ MRR(idx1) MRR(idx1) */这样的提示,MySQL 使用第一个提示,并发出关于重复提示的警告。

  • 冲突的提示:对于像/*+ MRR(idx1) NO_MRR(idx1) */这样的提示,MySQL 使用第一个提示,并发出关于第二个冲突提示的警告。

查询块名称是标识符,遵循关于哪些名称有效以及如何引用它们的通常规则(参见第 11.2 节,“模式对象名称”)。

提示名称、查询块名称和策略名称不区分大小写。表和索引名称的引用遵循通常的标识符大小写敏感性规则(参见第 11.2.3 节,“标识符大小写敏感性”)。

连接顺序优化提示

连接顺序提示影响优化器连接表的顺序。

JOIN_FIXED_ORDER提示的语法:

*hint_name*([@*query_block_name*])

其他连接顺序提示的语法:

*hint_name*([@*query_block_name*] *tbl_name* [, *tbl_name*] ...)
*hint_name*(*tbl_name*[@*query_block_name*] [, *tbl_name*[@*query_block_name*]] ...)

语法指的是这些术语:

  • hint_name:允许使用这些提示名称:

    • JOIN_FIXED_ORDER:强制优化器按照它们在FROM子句中出现的顺序连接表。这与指定SELECT STRAIGHT_JOIN相同。

    • JOIN_ORDER:指示优化器使用指定的表顺序连接表。提示适用于指定的表。优化器可以在连接顺序中放置未命名的表,包括在指定表之间。

    • JOIN_PREFIX:指示优化器使用连接执行计划的第一个表的指定表顺序连接表。提示适用于指定的表。优化器将所有其他表放在指定表之后。

    • JOIN_SUFFIX:指示优化器使用指定的表顺序连接连接执行计划的最后表。提示适用于指定的表。优化器将所有其他表放在指定表之前。

  • tbl_name:语句中使用的表的名称。指定表名称的提示适用于所有指定的表。JOIN_FIXED_ORDER提示不指定表名,并适用于其出现的查询块的FROM子句中的所有表。

    如果表有别名,则提示必须引用别名,而不是表名。

    提示中的表名不能带有模式名限定。

  • query_block_name:提示适用的查询块。如果提示中不包含前导@*query_block_name*,则提示适用于其出现的查询块。对于*tbl_name*@*query_block_name*语法,提示适用于指定查询块中的指定表。要为查询块指定名称,请参见为查询块命名的优化提示。

示例:

SELECT
/*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1)
    JOIN_ORDER(t4@subq1, t3)
    JOIN_SUFFIX(t1) */
COUNT(*) FROM t1 JOIN t2 JOIN t3
           WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4)
             AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5);

提示控制将合并到外部查询块的半连接表的行为。如果子查询 subq1subq2 被转换为半连接,表 t4@subq1t5@subq2 将合并到外部查询块。在这种情况下,外部查询块中指定的提示控制 t4@subq1t5@subq2 表的行为。

优化器根据以下原则解析连接顺序提示:

  • 多个提示实例

    每种类型只应用一个 JOIN_PREFIXJOIN_SUFFIX 提示。后续相同类型的提示将被忽略,并显示警告。JOIN_ORDER 可以多次指定。

    示例:

    /*+ JOIN_PREFIX(t1) JOIN_PREFIX(t2) */
    

    第二个 JOIN_PREFIX 提示将被忽略,并显示警告。

    /*+ JOIN_PREFIX(t1) JOIN_SUFFIX(t2) */
    

    两个提示都适用。不会出现警告。

    /*+ JOIN_ORDER(t1, t2) JOIN_ORDER(t2, t3) */
    

    两个提示都适用。不会出现警告。

  • 冲突的提示

    在某些情况下,提示可能会发生冲突,例如当 JOIN_ORDERJOIN_PREFIX 具有不可能同时应用的表顺序时:

    SELECT /*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */ ... FROM t1, t2;
    

    在这种情况下,第一个指定的提示被应用,后续冲突的提示将被忽略,且不会出现警告。一个无法应用的有效提示将被静默忽略,且不会出现警告。

  • 被忽略的提示

    如果提示中指定的表存在循环依赖,则提示将被忽略。

    示例:

    /*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */
    

    JOIN_ORDER 提示将表 t2 设置为依赖于 t1JOIN_PREFIX 提示被忽略,因为表 t1 不能依赖于 t2。被忽略的提示不会显示在扩展的 EXPLAIN 输出中。

  • const 表的交互

    MySQL 优化器将 const 表放在连接顺序的首位,const 表的位置不受提示影响。在连接顺序提示中忽略对 const 表的引用,尽管提示仍然适用。例如,以下两种提示是等效的:

    JOIN_ORDER(t1, *const_tbl*, t2)
    JOIN_ORDER(t1, t2)
    

    在扩展的 EXPLAIN 输出中显示的接受的提示包括按照指定方式指定的 const 表。

  • 与连接操作类型的交互

    MySQL 支持几种类型的连接:LEFTRIGHTINNERCROSSSTRAIGHT_JOIN。与指定的连接类型冲突的提示将被忽略,且不会出现警告。

    示例:

    SELECT /*+ JOIN_PREFIX(t1, t2) */FROM t2 LEFT JOIN t1;
    

    在提示中请求的连接顺序与 LEFT JOIN 所需的顺序之间发生冲突。提示将被忽略,且不会出现警告。

表级优化提示

表级提示影响:

  • 使用块嵌套循环(BNL)和批量键访问(BKA)连接处理算法(参见第 10.2.1.12 节,“块嵌套循环和批量键访问连接”)。

  • 衍生表、视图引用或公共表达式是否应合并到外部查询块中,或者使用内部临时表进行材料化。

  • 使用衍生表条件下推优化(在 MySQL 8.0.22 中添加)。请参见第 10.2.2.5 节,“衍生条件下推优化”。

这些提示类型适用于特定表或查询块中的所有表。

表级提示的语法:

*hint_name*([@*query_block_name*] [*tbl_name* [, *tbl_name*] ...])
*hint_name*([*tbl_name*@*query_block_name* [, *tbl_name*@*query_block_name*] ...])

语法涉及这些术语:

  • hint_name:允许使用这些提示名称:

    • BKA, NO_BKA:启用或禁用指定表的批量键访问。

    • BNL, NO_BNL:启用或禁用指定表的块嵌套循环。在 MySQL 8.0.18 及更高版本中,这些提示还启用和禁用哈希连接优化。

      注意

      MySQL 8.0.20 及更高版本中移除了块嵌套循环优化,但仍支持BNLNO_BNL以启用和禁用哈希连接。

    • DERIVED_CONDITION_PUSHDOWN, NO_DERIVED_CONDITION_PUSHDOWN:启用或禁用指定表的衍生表条件下推(在 MySQL 8.0.22 中添加)。有关更多信息,请参见第 10.2.2.5 节,“衍生条件下推优化”。

    • HASH_JOIN, NO_HASH_JOIN:仅在 MySQL 8.0.18 中启用或禁用指定表的哈希连接。在 MySQL 8.0.19 或更高版本中,这些提示无效,应改用BNLNO_BNL

    • MERGE, NO_MERGE:为指定的表、视图引用或公共表达式启用合并;或禁用合并,改为使用材料化。

    注意

    要使用块嵌套循环或批量键访问提示为外连接的任何内部表启用连接缓冲,必须为外连接的所有内部表启用连接缓冲。

  • tbl_name:语句中使用的表的名称。提示适用于所有命名的表。如果提示未命名任何表,则适用于其出现的查询块中的所有表。

    如果表有别名,则提示必须引用别名,而不是表名。

    提示中的表名不能带有模式名。

  • query_block_name:提示适用的查询块。如果提示不包含前导@*query_block_name*,则提示适用于其出现的查询块。对于*tbl_name*@*query_block_name*语法,提示适用于命名查询块中的命名表。要为查询块分配名称,请参见为查询块命名的优化器提示。

示例:

SELECT /*+ NO_BKA(t1, t2) */ t1.* FROM t1 INNER JOIN t2 INNER JOIN t3;
SELECT /*+ NO_BNL() BKA(t1) */ t1.* FROM t1 INNER JOIN t2 INNER JOIN t3;
SELECT /*+ NO_MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;

表级提示适用于接收来自前面表的记录的表,而不是发送方表。考虑以下语句:

SELECT /*+ BNL(t2) */ FROM t1, t2;

如果优化器选择首先处理t1,则通过在开始从t2读取之前缓冲来自t1的行,将对t2应用块嵌套循环连接。如果优化器选择首先处理t2,则提示无效,因为t2是发送方表。

对于MERGENO_MERGE提示,适用以下优先规则:

  • 提示优先于任何不是技术约束的优化器启发式。 (如果提供提示作为建议没有效果,优化器有理由忽略它。)

  • 提示优先于optimizer_switch系统变量的derived_merge标志。

  • 对于视图引用,视图定义中的ALGORITHM={MERGE|TEMPTABLE}子句优先于在引用视图的查询中指定的提示。

索引级优化器提示

索引级提示影响优化器为特定表或索引使用哪些索引处理策略。这些提示类型影响索引条件下推(ICP)、多范围读取(MRR)、索引合并和范围优化的使用(参见第 10.2.1 节,“优化 SELECT 语句”)。

索引级提示的语法:

*hint_name*([@*query_block_name*] *tbl_name* [*index_name* [, *index_name*] ...])
*hint_name*(*tbl_name*@*query_block_name* [*index_name* [, *index_name*] ...])

语法涉及以下术语:

  • hint_name:允许使用这些提示名称:

    • GROUP_INDEX, NO_GROUP_INDEX:为GROUP BY操作启用或禁用指定索引或索引的索引扫描。等同于索引提示FORCE INDEX FOR GROUP BY, IGNORE INDEX FOR GROUP BY。从 MySQL 8.0.20 及更高版本提供。

    • INDEX, NO_INDEX:作为JOIN_INDEX, GROUP_INDEX, 和 ORDER_INDEX的组合,强制服务器使用指定的索引或索引来处理任何范围,或作为NO_JOIN_INDEX, NO_GROUP_INDEX, 和 NO_ORDER_INDEX的组合,导致服务器忽略指定的索引或索引用于任何范围。等同于FORCE INDEX, IGNORE INDEX。从 MySQL 8.0.20 开始提供。

    • INDEX_MERGE, NO_INDEX_MERGE:启用或禁用指定表或索引的索引合并访问方法。有关此访问方法的信息,请参见 Section 10.2.1.3, “Index Merge Optimization”。这些提示适用于所有三种索引合并算法。

      INDEX_MERGE提示强制优化器使用指定表的指定索引集进行索引合并。如果未指定索引,则优化器会考虑所有可能的索引组合并选择最经济的一个。如果索引组合不适用于给定的语句,则可能会忽略此提示。

      NO_INDEX_MERGE提示禁用涉及任何指定索引的索引合并组合。如果提示未指定任何索引,则不允许对表进行索引合并。

    • JOIN_INDEX, NO_JOIN_INDEX: 强制 MySQL 使用或忽略指定的索引或索引,用于任何访问方法,如refrangeindex_merge等。相当于FORCE INDEX FOR JOINIGNORE INDEX FOR JOIN。从 MySQL 8.0.20 版本开始提供。

    • MRR, NO_MRR: 启用或禁用指定表或索引的 MRR。MRR 提示仅适用于InnoDBMyISAM表。有关此访问方法的信息,请参见 Section 10.2.1.11, “Multi-Range Read Optimization”。

    • NO_ICP: 禁用指定表或索引的 ICP。默认情况下,ICP 是一种候选优化策略,因此没有启用它的提示。有关此访问方法的信息,请参见 Section 10.2.1.6, “Index Condition Pushdown Optimization”。

    • NO_RANGE_OPTIMIZATION: 禁用指定表或索引的索引范围访问。此提示还禁用了表或索引的索引合并和松散索引扫描。默认情况下,范围访问是一种候选优化策略,因此没有启用它的提示。

      当范围数量可能很高且范围优化需要大量资源时,此提示可能会有用。

    • ORDER_INDEX, NO_ORDER_INDEX: 导致 MySQL 使用或忽略指定的索引或索引来对行进行排序。相当于FORCE INDEX FOR ORDER BYIGNORE INDEX FOR ORDER BY。从 MySQL 8.0.20 版本开始提供。

    • SKIP_SCAN, NO_SKIP_SCAN: 启用或禁用指定表或索引的跳过扫描访问方法。有关此访问方法的信息,请参见跳过扫描范围访问方法。这些提示从 MySQL 8.0.13 版本开始提供。

      SKIP_SCAN提示强制优化器使用跳过扫描来使用指定的表和指定的索引集。如果未指定索引,则优化器考虑所有可能的索引并选择最经济的一个。如果索引不适用于给定语句,则可能会忽略提示。

      NO_SKIP_SCAN提示禁用了指定索引的跳过扫描。如果提示未指定任何索引,则不允许对表进行跳过扫描。

  • tbl_name:提示适用的表。

  • index_name:命名表中索引的名称。提示适用于所有命名的索引。如果提示未命名任何索引,则适用于表中的所有索引。

    要引用主键,请使用名称PRIMARY。要查看表的索引名称,请使用SHOW INDEX

  • query_block_name:提示适用的查询块。如果提示中不包含前导@*query_block_name*,则提示适用于出现在其中的查询块。对于*tbl_name*@*query_block_name*语法,提示适用于命名查询块中的命名表。要为查询块分配名称,请参见为查询块命名的优化器提示。

示例:

SELECT /*+ INDEX_MERGE(t1 f3, PRIMARY) */ f2 FROM t1
  WHERE f1 = 'o' AND f2 = f3 AND f3 <= 4;
SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3;
SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
  FROM t3 WHERE f1 > 30 AND f1 < 33;
INSERT INTO t3(f1, f2, f3)
  (SELECT /*+ NO_ICP(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2
   WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1
   AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1);
SELECT /*+ SKIP_SCAN(t1 PRIMARY) */ f1, f2
  FROM t1 WHERE f2 > 40;

下面的示例使用了索引合并提示,但其他索引级别的提示遵循相同的原则,关于提示忽略和优化器提示在与optimizer_switch系统变量或索引提示的优先级方面的关系。

假设表t1具有列abcd;并且在abc上分别存在名为i_ai_bi_c的索引:

SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1
  WHERE a = 1 AND b = 2 AND c = 3 AND d = 4;

在这种情况下,索引合并用于(i_a, i_b, i_c)

SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1
  WHERE b = 1 AND c = 2 AND d = 3;

在这种情况下,索引合并用于(i_b, i_c)

/*+ INDEX_MERGE(t1 i_a, i_b) NO_INDEX_MERGE(t1 i_b) */

NO_INDEX_MERGE被忽略,因为存在相同表的先前提示。

/*+ NO_INDEX_MERGE(t1 i_a, i_b) INDEX_MERGE(t1 i_b) */

INDEX_MERGE被忽略,因为存在相同表的先前提示。

对于INDEX_MERGENO_INDEX_MERGE优化器提示,适用以下优先规则:

  • 如果指定了优化器提示并且适用,则它优先于optimizer_switch系统变量的与索引合并相关的标志。

    SET optimizer_switch='index_merge_intersection=off';
    SELECT /*+ INDEX_MERGE(t1 i_b, i_c) */ * FROM t1
    WHERE b = 1 AND c = 2 AND d = 3;
    

    提示优先于optimizer_switch。在这种情况下,索引合并用于(i_b, i_c)

    SET optimizer_switch='index_merge_intersection=on';
    SELECT /*+ INDEX_MERGE(t1 i_b) */ * FROM t1
    WHERE b = 1 AND c = 2 AND d = 3;
    

    提示只指定了一个索引,因此不适用,并且 optimizer_switch 标志(on)适用。如果优化器评估为成本有效,则使用索引合并。

    SET optimizer_switch='index_merge_intersection=off';
    SELECT /*+ INDEX_MERGE(t1 i_b) */ * FROM t1
    WHERE b = 1 AND c = 2 AND d = 3;
    

    提示只指定了一个索引,因此不适用,并且 optimizer_switch 标志(off)适用。不使用索引合并。

  • 索引级别的优化提示 GROUP_INDEXINDEXJOIN_INDEXORDER_INDEX 都优先于等效的 FORCE INDEX 提示;也就是说,它们导致 FORCE INDEX 提示被忽略。同样,NO_GROUP_INDEXNO_INDEXNO_JOIN_INDEXNO_ORDER_INDEX 提示都优先于任何 IGNORE INDEX 等效项,也导致它们被忽略。

    索引级别的优化提示 GROUP_INDEXNO_GROUP_INDEXINDEXNO_INDEXJOIN_INDEXNO_JOIN_INDEXORDER_INDEXNO_ORDER_INDEX 提示都优先于所有其他优化提示,包括其他索引级别的优化提示。任何其他优化提示仅适用于这些提示允许的索引。

    GROUP_INDEXINDEXJOIN_INDEXORDER_INDEX 提示都等同于 FORCE INDEX,而不是 USE INDEX。这是因为使用这些提示中的一个或多个意味着只有在无法使用命名的索引来查找表中的行时才会使用表扫描。要使 MySQL 使用与给定的 USE INDEX 实例相同的索引或索引集,可以使用 NO_INDEXNO_JOIN_INDEXNO_GROUP_INDEXNO_ORDER_INDEX 或这些提示的某种组合。

    要复制查询 SELECT a,c FROM t1 USE INDEX FOR ORDER BY (i_a) ORDER BY aUSE INDEX 的效果,可以使用 NO_ORDER_INDEX 优化提示来覆盖表上除所需索引之外的所有索引,如下所示:

    SELECT /*+ NO_ORDER_INDEX(t1 i_b,i_c) */ a,c
        FROM t1
        ORDER BY a;
    

    尝试将整个表的 NO_ORDER_INDEXUSE INDEX FOR ORDER BY 结合起来不起作用,因为 NO_ORDER_BY 导致 USE INDEX 被忽略,如下所示:

    mysql> EXPLAIN SELECT /*+ NO_ORDER_INDEX(t1) */ a,c FROM t1
     ->     USE INDEX FOR ORDER BY (i_a) ORDER BY a\G
    *************************** 1\. row ***************************
               id: 1
      select_type: SIMPLE
            table: t1
       partitions: NULL
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 256
         filtered: 100.00
            Extra: Using filesort
    
  • USE INDEXFORCE INDEXIGNORE INDEX 索引提示的优先级高于 INDEX_MERGENO_INDEX_MERGE 优化提示。

    /*+ INDEX_MERGE(t1 i_a, i_b, i_c) */ ... IGNORE INDEX i_a
    

    IGNORE INDEX 优先于 INDEX_MERGE,因此索引 i_a 被排除在索引合并的可能范围之外。

    /*+ NO_INDEX_MERGE(t1 i_a, i_b) */ ... FORCE INDEX i_a, i_b
    

    由于 FORCE INDEXi_a, i_b 不允许进行索引合并,但是优化器被强制使用 i_ai_b 进行 rangeref 访问。没有冲突;两个提示都适用。

  • 如果 IGNORE INDEX 提示命名多个索引,则这些索引对于索引合并是不可用的。

  • FORCE INDEXUSE INDEX提示仅使命名的索引可用于索引合并。

    SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c) */ a FROM t1
    FORCE INDEX (i_a, i_b) WHERE c = 'h' AND a = 2 AND b = 'b';
    

    对于(i_a, i_b),使用索引合并交集访问算法。如果将FORCE INDEX更改为USE INDEX,情况也是如此。

子查询优化提示

子查询提示影响是否使用半连接转换以及允许哪些半连接策略,并且在不使用半连接时,是否使用子查询材料化或INEXISTS转换。有关这些优化的更多信息,请参见第 10.2.2 节,“优化子查询、派生表、视图引用和公共表表达式”。

影响半连接策略的提示的语法:

*hint_name*([@*query_block_name*] [*strategy* [, *strategy*] ...])

语法涉及以下术语:

  • hint_name:允许使用这些提示名称:

    • SEMIJOINNO_SEMIJOIN:启用或禁用指定的半连接策略。
  • strategy:要启用或禁用的半连接策略。允许使用这些策略名称:DUPSWEEDOUTFIRSTMATCHLOOSESCANMATERIALIZATION

    对于SEMIJOIN提示,如果没有指定策略,则根据根据optimizer_switch系统变量启用的策略来尽可能使用半连接。如果指定了策略但对语句不适用,则使用DUPSWEEDOUT

    对于NO_SEMIJOIN提示,如果没有指定策略,则不使用半连接。如果指定的策略排除了语句的所有适用策略,则使用DUPSWEEDOUT

如果一个子查询嵌套在另一个子查询中,并且两者合并为外部查询的半连接,那么对于最内层查询的任何半连接策略规范都将被忽略。仍然可以使用SEMIJOINNO_SEMIJOIN提示来启用或禁用这种嵌套子查询的半连接转换。

如果禁用了DUPSWEEDOUT,有时优化器可能生成远非最佳的查询计划。这是由于贪婪搜索期间的启发式修剪导致的,可以通过设置optimizer_prune_level=0来避免。

示例:

SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) */ * FROM t2
  WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3);
SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2
  WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3);

影响是否使用子查询材料化或INEXISTS转换的提示的语法:

SUBQUERY([@*query_block_name*] *strategy*)

提示名称始终为SUBQUERY

对于SUBQUERY提示,允许使用这些strategy值:INTOEXISTSMATERIALIZATION

例子:

SELECT id, a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) */ a FROM t1) FROM t2;
SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) */ a FROM t1);

对于半连接和SUBQUERY提示,前导@*query_block_name*指定提示适用的查询块。如果提示不包含前导@*query_block_name*,则提示适用于其出现的查询块。要为查询块分配名称,请参阅用于命名查询块的优化提示。

如果提示注释包含多个子查询提示,则使用第一个。如果有其他相同类型的后续提示,则会产生警告。其他类型的后续提示会被静默忽略。

语句执行时间优化提示

MAX_EXECUTION_TIME提示仅适用于SELECT语句。它在服务器终止之前对语句执行的时间设置了限制N(以毫秒为单位的超时值):

MAX_EXECUTION_TIME(*N*)

例如,设置超时为 1 秒(1000 毫秒):

SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 WHERE ...

MAX_EXECUTION_TIME(*N*)提示设置了N毫秒的语句执行超时。如果此选项不存在或N为 0,则由max_execution_time系统变量设置的语句超时生效。

MAX_EXECUTION_TIME提示适用如下:

  • 对于包含多个SELECT关键字的语句,例如联合或包含子查询的语句,MAX_EXECUTION_TIME适用于整个语句,并且必须出现在第一个SELECT之后。

  • 适用于只读SELECT语句。不是只读的语句是那些调用修改数据的存储函数的语句。

  • 不适用于存储程序中的SELECT语句,并且会被忽略。

变量设置提示语法

SET_VAR提示临时设置系统变量的会话值(在单个语句的持续时间内)。例子:

SELECT /*+ SET_VAR(sort_buffer_size = 16M) */ name FROM people ORDER BY name;
INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off') */ 1;

SET_VAR提示的语法:

SET_VAR(*var_name* = *value*)

var_name 是命名具有会话值的系统变量(尽管并非所有这些变量都可以命名,稍后会解释)。value 是要分配给变量的值;该值必须是标量。

SET_VAR 进行临时变量更改,如下面的语句所示:

mysql> SELECT @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
|               1 |
+-----------------+
mysql> SELECT /*+ SET_VAR(unique_checks=OFF) */ @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
|               0 |
+-----------------+
mysql> SELECT @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
|               1 |
+-----------------+

使用 SET_VAR,无需保存和恢复变量值。这使您可以通过单个语句替换多个语句。考虑以下语句序列:

SET @saved_val = @@SESSION.*var_name*;
SET @@SESSION.*var_name* = *value*;
SELECT ...
SET @@SESSION.*var_name* = @saved_val;

这个序列可以被这个单个语句替代:

SELECT /*+ SET_VAR(*var_name* = *value*) ...

独立的 SET 语句允许使用以下任何语法来命名会话变量:

SET SESSION *var_name* = *value*;
SET @@SESSION.*var_name* = *value*;
SET @@.*var_name* = *value*;

因为 SET_VAR 提示仅适用于会话变量,会话范围是隐含的,SESSION@@SESSION.@@ 既不需要也不允许。包括显式会话指示符语法会导致忽略带有警告的 SET_VAR 提示。

并非所有会话变量都可以与 SET_VAR 一起使用。各个系统变量描述指示每个变量是否可提示;请参阅 第 7.1.8 节,“服务器系统变量”。您还可以通过尝试在 SET_VAR 中使用它来在运行时检查系统变量。如果变量不可提示,则会发出警告:

mysql> SELECT /*+ SET_VAR(collation_server = 'utf8mb4') */ 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1\. row ***************************
  Level: Warning
   Code: 4537
Message: Variable 'collation_server' cannot be set using SET_VAR hint.

SET_VAR 语法允许设置单个变量,但可以提供多个提示以设置多个变量:

SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off')
           SET_VAR(max_heap_table_size = 1G) */ 1;

如果在同一语句中出现多个具有相同变量名称的提���,第一个将被应用,其他的将被忽略并显示警告:

SELECT /*+ SET_VAR(max_heap_table_size = 1G)
           SET_VAR(max_heap_table_size = 3G) */ 1;

在这种情况下,第二个提示将被忽略并显示冲突警告。

如果没有系统变量具有指定名称或变量值不正确,则会忽略带有警告的 SET_VAR 提示:

SELECT /*+ SET_VAR(max_size = 1G) */ 1;
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=yes') */ 1;

对于第一条语句,不存在 max_size 变量。对于第二条语句,mrr_cost_based 可以取值 onoff,因此尝试将其设置为 yes 是不正确的。在每种情况下,提示都会被忽略并显示警告。

SET_VAR 提示仅在语句级别允许。如果在子查询中使用,提示将被忽略并显示警告。

复制品会忽略复制语句中的 SET_VAR 提示,以避免安全问题的潜在风险。

资源组提示语法

RESOURCE_GROUP 优化提示用于资源组管理(参见第 7.1.16 节,“资源组”)。此提示将执行语句的线程暂时分配给命名的资源组(在语句执行期间)。它需要RESOURCE_GROUP_ADMINRESOURCE_GROUP_USER 权限。

例子:

SELECT /*+ RESOURCE_GROUP(USR_default) */ name FROM people ORDER BY name;
INSERT /*+ RESOURCE_GROUP(Batch) */ INTO t2 VALUES(2);

RESOURCE_GROUP 提示的语法:

RESOURCE_GROUP(*group_name*)

group_name 表示线程在语句执行期间应分配给的资源组。如果组不存在,则会发出警告并忽略提示。

RESOURCE_GROUP 提示必须出现在初始语句关键字(SELECTINSERTREPLACEUPDATEDELETE)之后。

RESOURCE_GROUP 的另一种选择是 SET RESOURCE GROUP 语句,它将线程非暂时地分配给资源组。参见第 15.7.2.4 节,“SET RESOURCE GROUP 语句”。

为查询块命名的优化提示

表级、索引级和子查询优化提示允许特定查询块作为其参数语法的一部分命名。要创建这些名称,请使用 QB_NAME 提示,它为其出现的查询块分配一个名称:

QB_NAME(*name*)

QB_NAME 可以清晰地表明其他提示适用于哪些查询块。它们还允许在单个提示注释中指定所有非查询块名称提示,以便更容易理解复杂语句。考虑以下语句:

SELECT ...
  FROM (SELECT ...
  FROM (SELECT ... FROM ...)) ...

QB_NAME 提示为语句中的查询块分配名称:

SELECT /*+ QB_NAME(qb1) */ ...
  FROM (SELECT /*+ QB_NAME(qb2) */ ...
  FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...

然后其他提示可以使用这些名称来引用适当的查询块:

SELECT /*+ QB_NAME(qb1) MRR(@qb1 t1) BKA(@qb2) NO_MRR(@qb3t1 idx1, id2) */ ...
  FROM (SELECT /*+ QB_NAME(qb2) */ ...
  FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...

结果效果如下:

  • MRR(@qb1 t1) 适用于查询块 qb1 中的表 t1

  • BKA(@qb2) 适用于查询块 qb2

  • NO_MRR(@qb3 t1 idx1, id2) 适用于查询块 qb3 中表 t1 中的索引 idx1idx2

查询块名称是标识符,并遵循关于名称有效性和如何引用它们的通常规则(参见第 11.2 节,“模式对象名称”)。例如,包含空格的查询块名称必须用引号引起来,可以使用反引号:

SELECT /*+ BKA(@`my hint name`) */ ...
  FROM (SELECT /*+ QB_NAME(`my hint name`) */ ...) ...

如果启用了ANSI_QUOTES SQL 模式,则还可以用双引号引用查询块名称:

SELECT /*+ BKA(@"my hint name") */ ...
  FROM (SELECT /*+ QB_NAME("my hint name") */ ...) ...

10.9.4 索引提示

原文:dev.mysql.com/doc/refman/8.0/en/index-hints.html

索引提示为优化器提供有关在查询处理期间如何选择索引的信息。这里描述的索引提示与第 10.9.3 节“优化器提示”中描述的优化器提示不同。索引提示和优化器提示可以单独或一起使用。

索引提示适用于SELECTUPDATE语句。它们还适用于多表DELETE语句,但不适用于单表DELETE,如本节后面所示。

索引提示在表名后指定。(有关在SELECT语句中指定表的一般语法,请参见第 15.2.13.2 节“JOIN 子句”。)指定单个表的语法,包括索引提示,如下所示:

*tbl_name* [[AS] *alias*] [*index_hint_list*]

*index_hint_list*:
    *index_hint* [*index_hint*] ...

*index_hint*:
    USE {INDEX|KEY}
      [FOR {JOIN|ORDER BY|GROUP BY}] ([*index_list*])
  | {IGNORE|FORCE} {INDEX|KEY}
      [FOR {JOIN|ORDER BY|GROUP BY}] (*index_list*)

*index_list*:
    *index_name* [, *index_name*] ...

USE INDEX (*index_list*)提示告诉 MySQL 仅使用命名索引之一来查找表中的行。另一种语法IGNORE INDEX (*index_list*)告诉 MySQL 不使用某些特定的索引。如果EXPLAIN显示 MySQL 正在使用列表中错误的索引,则这些提示���有用。

FORCE INDEX提示类似于USE INDEX (*index_list*),但额外假定表扫描是非常昂贵的。换句话说,只有在无法使用命名索引之一来查找表中的行时,才会使用表扫描。

注意

截至 MySQL 8.0.20,服务器支持索引级别的优化器提示JOIN_INDEXGROUP_INDEXORDER_INDEXINDEX,它们相当于并打算取代FORCE INDEX索引提示,以及NO_JOIN_INDEXNO_GROUP_INDEXNO_ORDER_INDEXNO_INDEX优化器提示,它们相当于并打算取代IGNORE INDEX索引提示。因此,您应该期望USE INDEXFORCE INDEXIGNORE INDEX在将来的 MySQL 版本中被弃用,并在此后的某个时间被完全移除。

这些索引级优化器提示支持单表和多表DELETE语句。

更多信息,请参见 Index-Level Optimizer Hints。

每个提示都需要索引名称,而不是列名称。要引用主键,请使用名称PRIMARY。要查看表的索引名称,请使用SHOW INDEX语句或 Information Schema STATISTICS表。

index_name值不必是完整的索引名称。它可以是索引名称的明确前缀。如果前缀不明确,则会出现错误。

示例:

SELECT * FROM table1 USE INDEX (col1_index,col2_index)
  WHERE col1=1 AND col2=2 AND col3=3;

SELECT * FROM table1 IGNORE INDEX (col3_index)
  WHERE col1=1 AND col2=2 AND col3=3;

索引提示的语法具有以下特点:

  • 对于USE INDEX,省略index_list在语法上是有效的,这意味着“不使用索引”。对于FORCE INDEXIGNORE INDEX省略index_list是语法错误。

  • 通过在提示中添加FOR子句,您可以指定索引提示的范围。这可以更精细地控制优化器在查询处理的各个阶段选择执行计划。要仅影响 MySQL 在决定如何在表中查找行以及如何处理连接时使用的索引,请使用FOR JOIN。要影响用于对行进行排序或分组的索引使用,请使用FOR ORDER BYFOR GROUP BY

  • 您可以指定多个索引提示:

    SELECT * FROM t1 USE INDEX (i1) IGNORE INDEX FOR ORDER BY (i2) ORDER BY a;
    

    在几个提示中命名相同的索引不是错误的(即使在同一个提示中):

    SELECT * FROM t1 USE INDEX (i1) USE INDEX (i1,i1);
    

    但是,对于同一表混合使用USE INDEXFORCE INDEX是错误的:

    SELECT * FROM t1 USE INDEX FOR JOIN (i1) FORCE INDEX FOR JOIN (i2);
    

如果索引提示不包含FOR子句,则提示的范围是应用于语句的所有部分。例如,这个提示:

IGNORE INDEX (i1)

等同于以下提示的组合:

IGNORE INDEX FOR JOIN (i1)
IGNORE INDEX FOR ORDER BY (i1)
IGNORE INDEX FOR GROUP BY (i1)

在 MySQL 5.0 中,没有FOR子句的提示范围仅适用于行检索。要在没有FOR子句的情况下使服务器使用这种较旧的行为,请在服务器启动时启用old系统变量。在复制设置中启用此变量时要小心。使用基于语句的二进制日志记录,源和副本之间具有不同模式可能会导致复制错误。

处理索引提示时,它们按类型(USEFORCEIGNORE)和范围(FOR JOINFOR ORDER BYFOR GROUP BY)被收集到单个列表中。例如:

SELECT * FROM t1
  USE INDEX () IGNORE INDEX (i2) USE INDEX (i1) USE INDEX (i2);

等同于:

SELECT * FROM t1
   USE INDEX (i1,i2) IGNORE INDEX (i2);

然后,索引提示按以下顺序应用于每个范围:

  1. 如果存在,则应用{USE|FORCE} INDEX。(如果不存在,则使用优化器确定的索引集。)

  2. IGNORE INDEX应用于上一步的结果。例如,以下两个查询是等效的:

    SELECT * FROM t1 USE INDEX (i1) IGNORE INDEX (i2) USE INDEX (i2);
    
    SELECT * FROM t1 USE INDEX (i1);
    

对于FULLTEXT搜索,索引提示的工作方式如下:

  • 对于自然语言模式搜索,索引提示会被静默忽略。例如,IGNORE INDEX(i1)会被忽略而不会有警告,索引仍然会被使用。

  • 对于布尔模式搜索,带有FOR ORDER BYFOR GROUP BY的索引提示会被静默忽略。带有FOR JOIN或没有FOR修饰符的索引提示会被应用。与非FULLTEXT搜索的提示应用方式相反,该提示用于查询执行的所有阶段(查找行和检索、分组和排序)。即使为非FULLTEXT索引提供提示,也是如此。

    例如,以下两个查询是等效的:

    SELECT * FROM t
      USE INDEX (index1)
      IGNORE INDEX FOR ORDER BY (index1)
      IGNORE INDEX FOR GROUP BY (index1)
      WHERE ... IN BOOLEAN MODE ... ;
    
    SELECT * FROM t
      USE INDEX (index1)
      WHERE ... IN BOOLEAN MODE ... ;
    

索引提示适用于DELETE语句,但仅当您使用多表DELETE语法时,如下所示:

mysql> EXPLAIN DELETE FROM t1 USE INDEX(col2) 
 -> WHERE col1 BETWEEN 1 AND 100 AND COL2 BETWEEN 1 AND 100\G
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near 'use
index(col2) where col1 between 1 and 100 and col2 between 1 and 100' at line 1 
mysql> EXPLAIN DELETE t1.* FROM t1 USE INDEX(col2) 
 -> WHERE col1 BETWEEN 1 AND 100 AND COL2 BETWEEN 1 AND 100\G
*************************** 1\. row ***************************
           id: 1
  select_type: DELETE
        table: t1
   partitions: NULL
         type: range
possible_keys: col2
          key: col2
      key_len: 5
          ref: NULL
         rows: 72
     filtered: 11.11
        Extra: Using where 1 row in set, 1 warning (0.00 sec)

10.9.5 优化器成本模型

原文:dev.mysql.com/doc/refman/8.0/en/cost-model.html

为生成执行计划,优化器使用基于查询执行过程中各种操作成本的估算的成本模型。优化器具有一组编译默认的“成本常量”可供其在决定执行计划时使用。

优化器还具有成本估算数据库,用于执行计划构建过程中使用。这些估算存储在mysql系统数据库的server_costengine_cost表中,并且可以随时进行配置。这些表的目的是使得优化器在尝试生成查询执行计划时能够轻松调整使用的成本估算。

  • 成本模型一般操作

  • 成本模型数据库

  • 对成本模型数据库进行更改

成本模型一般操作

可配置的优化器成本模型工作方式如下:

  • 服务器在启动时将成本模型表读入内存,并在运行时使用内存中的值。表中指定的任何非NULL成本估算优先于相应的编译默认成本常量。任何NULL估算指示优化器使用编译默认值。

  • 在运行时,服务器可能会重新读取成本表。当动态加载存储引擎或执行FLUSH OPTIMIZER_COSTS语句时会发生这种情况。

  • 成本表使服务器管理员可以通过更改表中的条目轻松调整成本估算。通过将条目的成本设置为NULL可以轻松恢复默认值。优化器使用内存中的成本值,因此更改表后应跟随FLUSH OPTIMIZER_COSTS以生效。

  • 当客户端会话开始时,内存中的成本估算值将贯穿整个会话直至结束。特别是,如果服务器重新读取成本表,则任何更改的估算仅适用于随后启动的会话。现有会话不受影响。

  • 成本表特定于给定的服务器实例。服务器不会将成本表更改复制到副本。

成本模型数据库

优化器成本模型数据库包括mysql系统数据库中的两个表,这些表包含查询执行过程中发生的操作的成本估算信息:

  • server_cost:一般服务器操作的优化器成本估算

  • engine_cost:特定存储引擎操作的优化器成本估算

server_cost表包含以下列:

  • cost_name

    用于成本模型的成本估算名称。名称不区分大小写。如果服务器在读取此表时无法识别成本名称,则会将警告写入错误日志。

  • cost_value

    成本估算值。如果值为非NULL,服务器将其用作成本。否则,它使用默认估算值(编译值)。DBA 可以通过更新此列来更改成本估算。如果服务器在读取此表时发现成本值无效(非正数),则会将警告写入错误日志。

    要覆盖默认成本估算(对于指定NULL的条目),请将成本设置为非NULL值。要恢复默认值,请将值设置为NULL。然后执行FLUSH OPTIMIZER_COSTS以告诉服务器重新读取成本表。

  • last_update

    最后一次行更新的时间。

  • comment

    与成本估算相关联的描述性注释。DBA 可以使用此列提供有关为什么成本估算行存储特定值的信息。

  • default_value

    成本估算的默认(编译内)值。此列是只读生成的列,即使关联的成本估算更改,它也保留其值。对于在运行时添加到表中的行,此列的值为NULL

server_cost表的主键是cost_name列,因此不可能为任何成本估算创建多个条目。

服务器识别server_cost表的这些cost_name值:

  • disk_temptable_create_costdisk_temptable_row_cost

    存储在基于磁盘的存储引擎(InnoDBMyISAM)中的内部创建临时表的成本估算。增加这些值会增加使用内部临时表的成本估算,并使优化器更倾向于使用较少的查询计划。有关此类表的信息,请参见 Section 10.4.4, “MySQL 中的内部临时表使用”。

    与相应内存参数(memory_temptable_create_costmemory_temptable_row_cost)的默认值相比,这些磁盘参数的默认值较大,反映了处理基于磁盘的表的更高成本。

  • key_compare_cost

    比较记录键的成本。增加此值会导致比较许多键的查询计划变得更昂贵。例如,执行filesort的查询计划相对于通过使用索引避免排序的查询计划变得更昂贵。

  • memory_temptable_create_costmemory_temptable_row_cost

    存储在MEMORY存储引擎中的内部创建临时表的成本估算。增加这些值会增加使用内部临时表的成本估算,并使优化器更倾向于使用较少的查询计划。有关这些表的信息,请参见 Section 10.4.4, “MySQL 中的内部临时表使用”。

    与相应磁盘参数的默认值(disk_temptable_create_costdisk_temptable_row_cost)相比,这些内存参数的默认值较小,反映了处理基于内存的表的较低成本。

  • row_evaluate_cost

    评估记录条件的成本。增加这个值会导致查询计划检查许多行变得比检查较少行的查询计划更昂贵。例如,与读取较少行的范围扫描相比,表扫描变得相对更昂贵。

engine_cost表包含以下列:

  • engine_name

    适用于此成本估算的存储引擎的名称。名称不区分大小写。如果值为default,则适用于所有没有自己命名条目的存储引擎。如果服务器在读取此表时不识别引擎名称,则会将警告写入错误日志。

  • device_type

    适用于此成本估算的设备类型。该列用于指定不同存储设备类型(例如硬盘驱动器与固态驱动器)的不同成本估算。目前,此信息未被使用,0 是唯一允许的值。

  • cost_name

    server_cost表中相同。

  • cost_value

    server_cost表中相同。

  • last_update

    server_cost表中相同。

  • 注释

    server_cost表中相同。

  • default_value

    成本估算的默认(编译内)值。此列是一个只读生成的列,即使相关的成本估算发生变化,它也会保留其值。对于在运行时添加到表中的行,此列的值为NULL,但有一个例外,即如果该行具有与原始行之一相同的cost_name值,则default_value列的值与该行相同。

engine_cost表的主键是由(cost_name, engine_name, device_type)列组成的元组,因此不可能为这些列中的任何值组合创建多个条目。

服务器识别engine_cost表中的这些cost_name值:

  • io_block_read_cost

    从磁盘读取索引或数据块的成本。增加这个值会导致读取许多磁盘块的查询计划变得比读取较少磁盘块的查询计划更昂贵。例如,与读取较少块的范围扫描相比,表扫描变得相对更昂贵。

  • memory_block_read_cost

    类似于io_block_read_cost,但表示从内存数据库缓冲区读取索引或数据块的成本。

如果io_block_read_costmemory_block_read_cost的值不同,同一查询的两次运行之间可能会导致执行计划的变化。假设内存访问的成本低于磁盘访问的成本。在服务器启动时,数据尚未读入缓冲池之前,可能会得到不同的计划,而在查询运行后,数据已经在内存中。

更改成本模型数据库

对于希望从默认值更改成本模型参数的数据库管理员,尝试将值加倍或减半,并测量效果。

更改io_block_read_costmemory_block_read_cost参数最有可能产生有价值的结果。这些参数值使得数据访问方法的成本模型能够考虑从不同来源读取信息的成本;也就是说,从磁盘读取信息的成本与从内存缓冲区中读取信息的成本。例如,其他条件相同的情况下,将io_block_read_cost设置为大于memory_block_read_cost的值会导致优化器更倾向于选择已经保存在内存中的信息而不是需要从磁盘读取的查询计划。

这个示例展示了如何更改io_block_read_cost的默认值:

UPDATE mysql.engine_cost
  SET cost_value = 2.0
  WHERE cost_name = 'io_block_read_cost';
FLUSH OPTIMIZER_COSTS;

这个示例展示了如何仅为InnoDB存储引擎更改io_block_read_cost的值:

INSERT INTO mysql.engine_cost
  VALUES ('InnoDB', 0, 'io_block_read_cost', 3.0,
  CURRENT_TIMESTAMP, 'Using a slower disk for InnoDB');
FLUSH OPTIMIZER_COSTS;

10.9.6 优化器统计

原文:dev.mysql.com/doc/refman/8.0/en/optimizer-statistics.html

column_statistics数据字典表存储关于列值的直方图统计信息,供优化器在构建查询执行计划时使用。要执行直方图管理,请使用ANALYZE TABLE语句。

column_statistics表具有以下特征:

  • 该表包含所有数据类型的列的统计信息,除了几何类型(空间数据)和JSON

  • 该表是持久的,因此每次服务器启动时不需要创建列统计信息。

  • 服务器对表执行更新;用户不执行。

column_statistics表不直接可被用户访问,因为它是数据字典的一部分。直方图信息可通过INFORMATION_SCHEMA.COLUMN_STATISTICS获得,它被实现为数据字典表上的视图。COLUMN_STATISTICS包含以下列:

  • SCHEMA_NAMETABLE_NAMECOLUMN_NAME:适用于统计的模式、表和列的名称。

  • HISTOGRAM:描述以直方图形式存储的列统计信息的JSON值。

列直方图包含存储在列中值范围的部分的桶。直方图是JSON对象,以允许在列统计的表示中灵活性。这是一个示例直方图对象:

{
  "buckets": [
    [
      1,
      0.3333333333333333
    ],
    [
      2,
      0.6666666666666666
    ],
    [
      3,
      1
    ]
  ],
  "null-values": 0,
  "last-updated": "2017-03-24 13:32:40.000000",
  "sampling-rate": 1,
  "histogram-type": "singleton",
  "number-of-buckets-specified": 128,
  "data-type": "int",
  "collation-id": 8
}

直方图对象具有以下键:

  • buckets:直方图桶。桶结构取决于直方图类型。

    对于singleton直方图,桶包含两个值:

    • 值 1:桶的值。类型取决于列数据类型。

    • 值 2:表示该值的累积频率的双精度值。例如,.25 和.75 表示列中值的 25%和 75%小于或等于桶值。

    对于equi-height直方图,桶包含四个值:

    • 值 1、2:桶的下限和上限值(包括)。类型取决于列数据类型。

    • 值 3:表示该值的累积频率的双精度值。例如,.25 和.75 表示列中值的 25%和 75%小于或等于桶的上限值。

    • 值 4:从桶下限值到上限值的范围内的不同值的数量。

  • null-values:介于 0.0 和 1.0 之间的数字,表示列值中为 SQL NULL值的比例。如果为 0,则列不包含NULL值。

  • last-updated:生成直方图的时间,以YYYY-MM-DD hh:mm:ss.uuuuuu格式的 UTC 值。

  • sampling-rate: 介于 0.0 和 1.0 之间的数字,表示用于创建直方图的数据比例。值为 1 表示读取了所有数据(无抽样)。

  • histogram-type: 直方图类型:

    • singleton: 一个桶代表列中的一个单个值。当列中不同数值的数量小于或等于ANALYZE TABLE语句生成直方图时指定的桶数时,将创建此直方图类型。

    • equi-height: 一个桶代表一系列数值。当列中不同数值的数量大于ANALYZE TABLE语句生成直方图时指定的桶数时,将创建此直方图类型。

  • number-of-buckets-specified: ANALYZE TABLE语句生成直方图时指定的桶数。

  • data-type: 此直方图包含的数据类型。在将直方图从持久存储读取和解析到内存时需要。其值为intuint(无符号整数)、doubledecimaldatetimestring(包括字符和二进制字符串)之一。

  • collation-id: 直方图数据的排序规则 ID。当data-type值为string时,这个值大多有意义。这些值对应于信息模式COLLATIONS表中的ID列值。

要从直方图对象中提取特定值,可以使用JSON操作。例如:

mysql> SELECT
         TABLE_NAME, COLUMN_NAME,
         HISTOGRAM->>'$."data-type"' AS 'data-type',
         JSON_LENGTH(HISTOGRAM->>'$."buckets"') AS 'bucket-count'
       FROM INFORMATION_SCHEMA.COLUMN_STATISTICS;
+-----------------+-------------+-----------+--------------+
| TABLE_NAME      | COLUMN_NAME | data-type | bucket-count |
+-----------------+-------------+-----------+--------------+
| country         | Population  | int       |          226 |
| city            | Population  | int       |         1024 |
| countrylanguage | Language    | string    |          457 |
+-----------------+-------------+-----------+--------------+

优化器使用直方图统计信息(如果适用)来处理收集了统计信息的任何数据类型的列。优化器应用直方图统计信息来根据列值与常量值的比较的选择性(过滤效果)确定行估计。以下形式的谓词符合直方图使用的条件:

*col_name* = *constant*
*col_name* <> *constant*
*col_name* != *constant*
*col_name* > *constant*
*col_name* < *constant*
*col_name* >= *constant*
*col_name* <= *constant*
*col_name* IS NULL
*col_name* IS NOT NULL
*col_name* BETWEEN *constant* AND *constant*
*col_name* NOT BETWEEN *constant* AND *constant*
*col_name* IN (*constant*[, *constant*] ...)
*col_name* NOT IN (*constant*[, *constant*] ...)

例如,以下语句包含符合直方图使用条件的谓词:

SELECT * FROM orders WHERE amount BETWEEN 100.0 AND 300.0;
SELECT * FROM tbl WHERE col1 = 15 AND col2 > 100;

对常量值的比较要求包括常量函数,如ABS()FLOOR()

SELECT * FROM tbl WHERE col1 < ABS(-34);

直方图统计信息主要适用于非索引列。为适用直方图统计信息的列添加索引也可能帮助优化器进行行估计。权衡如下:

  • 当表数据被修改时,索引必须更新。

  • 直方图仅在需要时创建或更新,因此在修改表数据时不会增加额外开销。另一方面,当表发生修改时,统计信息会逐渐变得过时,直到下次更新为止。

优化器更喜欢使用范围优化器的行估计值,而不是直方图统计数据。如果优化器确定范围优化器适用,则不使用直方图统计数据。

对于已建立索引的列,可以通过索引潜入来获取相等比较的行估计值(参见 Section 10.2.1.2, “Range Optimization”)。在这种情况下,直方图统计数据未必有用,因为索引潜入可以提供更好的估计值。

在某些情况下,使用直方图统计数据可能不会改善查询执行(例如,如果统计数据已过时)。要检查是否是这种情况,请使用ANALYZE TABLE重新生成直方图统计数据,然后再次运行查询。

或者,要禁用直方图统计数据,可以使用ANALYZE TABLE来删除它们。另一种禁用直方图统计数据的方法是关闭optimizer_switch系统变量的condition_fanout_filter标志(尽管这可能会禁用其他优化):

SET optimizer_switch='condition_fanout_filter=off';

如果使用直方图统计数据,可以通过EXPLAIN查看结果。考虑以下查询,其中col1列没有可用的索引:

SELECT * FROM t1 WHERE col1 < 24;

如果直方图统计数据表明t1中有 57%的行满足col1 < 24的条件,即使没有索引,也可以进行过滤,并且EXPLAIN中的filtered列显示 57.00。

10.10 缓冲和缓存

原文:dev.mysql.com/doc/refman/8.0/en/buffering-caching.html

10.10.1 InnoDB 缓冲池优化

10.10.2 MyISAM 键缓存

10.10.3 缓存准备语句和存储程序

MySQL 使用多种策略在内存缓冲区中缓存信息以提高性能。

10.10.1 InnoDB 缓冲池优化

原文:dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool-optimization.html

InnoDB维护一个称为缓冲池的存储区域,用于在内存中缓存数据和索引。了解InnoDB缓冲池的工作原理,并利用它将频繁访问的数据保留在内存中,是 MySQL 调优的重要方面。

有关InnoDB缓冲池内部工作原理、LRU 替换算法概述和一般配置信息的解释,请参阅第 17.5.1 节,“缓冲池”。

有关额外的InnoDB缓冲池配置和调优信息,请参阅以下章节:

  • 第 17.8.3.4 节,“配置 InnoDB 缓冲池预取(预读)”

  • 第 17.8.3.5 节,“配置缓冲池刷新”

  • 第 17.8.3.3 节,“使缓冲池具有扫描抵抗性”

  • 第 17.8.3.2 节,“配置多个缓冲池实例”

  • 第 17.8.3.6 节,“保存和恢复缓冲池状态”

  • 第 17.8.3.1 节,“配置 InnoDB 缓冲池大小”

10.10.2 MyISAM 关键缓存

原文:dev.mysql.com/doc/refman/8.0/en/myisam-key-cache.html

10.10.2.1 共享关键缓存访问

10.10.2.2 多个关键缓存

10.10.2.3 中点插入策略

10.10.2.4 索引预加载

10.10.2.5 关键缓存块大小

10.10.2.6 重构关键缓存

为了最小化磁盘 I/O,MyISAM存储引擎利用了许多数据库管理系统使用的策略。它使用缓存机制将最常访问的表块保留在内存中:

  • 对于索引块,维护一个称为关键缓存(或关键缓冲区)的特殊结构。该结构包含多个块缓冲区,其中放置了最常用的索引块。

  • 对于数据块,MySQL 不使用特殊缓存。相反,它依赖于本机操作系统文件系统缓存。

本节首先描述了MyISAM关键缓存的基本操作。然后讨论了改进关键缓存性能的特性,以及使您能够更好地控制缓存操作的功能:

  • 多个会话可以同时访问缓存。

  • 您可以设置多个关键缓存并将表索引分配给特定缓存。

要控制关键缓存的大小,请使用key_buffer_size系统变量。如果将此变量设置为零,则不使用关键缓存。如果key_buffer_size的值太小,无法分配最小数量的块缓冲区(8),则也不使用关键缓存。

当关键缓存不可用时,索引文件仅使用操作系统提供的本机文件系统缓冲区进行访问。(换句话说,表索引块的访问采用与表数据块相同的策略。)

索引块是对MyISAM索引文件的连续访问单元。通常,索引块的大小等于索引 B 树节点的大小。(索引在磁盘上使用 B 树数据结构表示。树底部的节点是叶节点。叶节点上面的节点是非叶节点。)

关键缓存结构中的所有块缓冲区大小相同。这个大小可以等于、大于或小于表索引块的大小。通常这两个值中的一个是另一个的倍数。

当需要访问任何表索引块的数据时,服务器首先检查它是否在关键缓存的某个块缓冲区中可用。如果是,服务器访问关键缓存中的数据而不是在磁盘上。也就是说,它从缓存中读取或写入数据,而不是从磁盘读取或写入数据。否则,服务器选择一个包含不同表索引块(或块)的缓存块缓冲区,并将数据替换为所需表索引块的副本。一旦新的索引块在缓存中,索引数据就可以被访问。

如果选择要替换的块已被修改,该块被视为“脏”。在这种情况下,在替换之前,其内容被刷新到其来源的表索引中。

通常,服务器遵循 LRU(最近最少使用)策略:在选择要替换的块时,它选择最近最少使用的索引块。为了更容易做出这个选择,关键缓存模块维护了一个特殊列表(LRU 链),按使用时间排序所有已使用的块。当访问一个块时,它是最近使用的,并被放在列表的末尾。当需要替换块时,列表开头的块是最近最少使用的,成为首选的驱逐候选。

InnoDB存储引擎也使用 LRU 算法来管理其缓冲池。请参阅第 17.5.1 节,“缓冲池”。

原文:dev.mysql.com/doc/refman/8.0/en/shared-key-cache.html

10.10.2.1 共享密钥缓存访问

线程可以同时访问密钥缓存缓冲区,但需满足以下条件:

  • 一个未被更新的缓冲区可以被多个会话访问。

  • 正在更新的缓冲区会导致需要使用它的会话等待更新完成。

  • 多个会话可以发起导致缓存块替换的请求,只要它们不相互干扰(即,只要它们需要不同的索引块,从而导致替换不同的缓存块)。

共享对密钥缓存的访问使服务器能够显著提高吞吐量。

原文:dev.mysql.com/doc/refman/8.0/en/multiple-key-caches.html

10.10.2.2 多个关键缓存

注意

截至 MySQL 8.0,讨论在此处引用多个MyISAM关键缓存的复合部分结构化变量语法已被弃用。

关键缓存的共享访问可以提高性能,但并不能完全消除会话之间的争用。它们仍然竞争管理对关键缓存缓冲区访问的控制结构。为了进一步减少关键缓存访问的争用,MySQL 还提供了多个关键缓存。此功能使您能够将不同的表索引分配给不同的关键缓存。

当存在多个关键缓存时,服务器必须知道在处理给定MyISAM表的查询时使用哪个缓存。默认情况下,所有MyISAM表索引都缓存在默认关键缓存中。要将表索引分配给特定的关键缓存,请使用CACHE INDEX语句(请参见 Section 15.7.8.2, “CACHE INDEX Statement”)。例如,以下语句将表t1t2t3的索引分配给名为hot_cache的关键缓存:

mysql> CACHE INDEX t1, t2, t3 IN hot_cache;
+---------+--------------------+----------+----------+
| Table   | Op                 | Msg_type | Msg_text |
+---------+--------------------+----------+----------+
| test.t1 | assign_to_keycache | status   | OK       |
| test.t2 | assign_to_keycache | status   | OK       |
| test.t3 | assign_to_keycache | status   | OK       |
+---------+--------------------+----------+----------+

CACHE INDEX语句中引用的关键缓存可以通过使用SET GLOBAL参数设置语句设置其大小,或通过使用服务器启动选项创建。例如:

mysql> SET GLOBAL keycache1.key_buffer_size=128*1024;

要销毁关键缓存,请将其大小设置为零:

mysql> SET GLOBAL keycache1.key_buffer_size=0;

无法销毁默认关键缓存。任何尝试这样做都将被忽略:

mysql> SET GLOBAL key_buffer_size = 0;

mysql> SHOW VARIABLES LIKE 'key_buffer_size';
+-----------------+---------+
| Variable_name   | Value   |
+-----------------+---------+
| key_buffer_size | 8384512 |
+-----------------+---------+

关键缓存变量是具有名称和组件的结构化系统变量。对于keycache1.key_buffer_sizekeycache1是缓存变量名称,key_buffer_size是缓存组件。有关引用结构化关键缓存系统变量的语法描述,请参见 Section 7.1.9.5, “Structured System Variables”。

默认情况下,表索引分配给在服务器启动时创建的主(默认)关键缓存。当关键缓存被销毁时,分配给它的所有索引将重新分配给默认关键缓存。

对于繁忙的服务器,您可以使用涉及三个关键缓存的策略:

  • 一个占据分配给所有关键缓存空间 20%的“热”关键缓存。用于经常用于搜索但不更新的表。

  • 一个占据分配给所有关键缓存空间 20%的“冷”关键缓存。用于中等大小、经常修改的表,例如临时表。

  • 一个占据关键缓存空间 60%的“热”关键缓存。将其作为默认关键缓存,用于默认情况下所有其他表。

使用三个关键缓存是有益的一个原因是,对一个关键缓存结构的访问不会阻塞对其他缓存的访问。访问分配给一个缓存的表的语句不会与访问分配给另一个缓存的表的语句竞争。性能提升也出现在其他方面:

  • 热缓存仅用于检索查询,因此其内容永远不会被修改。因此,每当需要从磁盘中拉入一个索引块时,被选中用于替换的缓存块的内容无需首先刷新。

  • 对于分配给热缓存的索引,如果没有需要进行索引扫描的查询,那么非叶节点对应的索引 B 树块很可能仍然保留在缓存中。

  • 对于临时表最频繁执行的更新操作,当更新的节点在缓存中且无需首先从磁盘读取时,操作速度会快得多。如果临时表的索引大小与冷键缓存的大小相当,那么更新的节点在缓存中的概率非常高。

CACHE INDEX 语句建立了表与关键缓存之间的关联,但每次服务器重新启动时该关联会丢失。如果希望每次服务器启动时关联生效,一种实现方法是使用选项文件:包含配置关键缓存的变量设置,以及一个命名为包含要执行的CACHE INDEX 语句的文件的 init_file 系统变量。例如:

key_buffer_size = 4G
hot_cache.key_buffer_size = 2G
cold_cache.key_buffer_size = 2G
init_file=/*path*/*to*/*data-directory*/mysqld_init.sql

mysqld_init.sql 中的语句在每次服务器启动时执行。该文件应该每行包含一个 SQL 语句。以下示例将几个表分配给了hot_cachecold_cache

CACHE INDEX db1.t1, db1.t2, db2.t3 IN hot_cache
CACHE INDEX db1.t4, db2.t5, db2.t6 IN cold_cache

原文:dev.mysql.com/doc/refman/8.0/en/midpoint-insertion.html

10.10.2.3 中点插入策略

默认情况下,键缓存管理系统使用简单的 LRU 策略来选择要驱逐的键缓存块,但它还支持一种更复杂的称为中点插入策略的方法。

当使用中点插入策略时,LRU 链被分为两部分:热子列表和温热子列表。两部分之间的分界点不是固定的,但键缓存管理系统确保温热部分不会“太短”,始终至少包含key_cache_division_limit的百分比的键缓存块。key_cache_division_limit是结构化键缓存变量的组成部分,因此它的值是可以针对每个缓存设置的参数。

当从表中读取索引块到键缓存时,它被放置在温热子列表的末尾。在一定数量的命中(对块的访问)之后,它将被提升到热子列表。目前,提升块所需的命中次数(3)对所有索引块都是相同的。

提升到热子列表的块被放置在列表的末尾。然后,该块在该子列表内循环。如果该块在子列表的开头停留足够长的时间,它将被降级到温热子列表。这个时间由键缓存的key_cache_age_threshold组件的值确定。

阈值值规定,对于包含N块的键缓存,未在最后*N* * key_cache_age_threshold / 100次命中内访问的热子列表开头的块将移动到温热子列表的开头。然后它成为驱逐的第一个候选,因为替换块总是从温热子列表的开头取出。

中点插入策略使您能够始终将更有价值的块保留在缓存中。如果您更喜欢使用普通的 LRU 策略,请将key_cache_division_limit的值保持默认值 100。

中点插入策略有助于提高性能,当执行需要索引扫描的查询时,有效地将与有价值的高级 B 树节点对应的所有索引块推出缓存。为了避免这种情况,您必须使用中点插入策略,并将key_cache_division_limit设置为远低于 100。然后,在索引扫描操作期间,频繁命中的有价值节点将在热子列表中保留。

译文:dev.mysql.com/doc/refman/8.0/en/index-preloading.html

10.10.2.4 索引预加载

如果关键缓存中有足够的块来容纳整个索引的块,或者至少是对应于其非叶节点的块,那么在开始使用之前将关键缓存预加载索引块是有意义的。预加载使您能够以最有效的方式将表索引块放入关键缓存缓冲区中:通过按顺序从磁盘读取索引块。

没有预加载,块仍然根据查询的需要放入关键缓存中。尽管这些块保留在缓存中,因为所有这些块都有足够的缓冲区,它们是以随机顺序而不是顺序地从磁盘中获取的。

要将索引预加载到缓存中,请使用LOAD INDEX INTO CACHE语句。例如,以下语句预加载了表t1t2的索引的节点(索引块):

mysql> LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES;
+---------+--------------+----------+----------+
| Table   | Op           | Msg_type | Msg_text |
+---------+--------------+----------+----------+
| test.t1 | preload_keys | status   | OK       |
| test.t2 | preload_keys | status   | OK       |
+---------+--------------+----------+----------+

IGNORE LEAVES修饰符导致只预加载索引的非叶节点的块。因此,所示语句预加载了来自t1的所有索引块,但仅预加载了来自t2的非叶节点的块。

如果使用CACHE INDEX语句将索引分配给关键缓存,预加载将索引块放入该缓存中。否则,索引将加载到默认关键缓存中。

原文:dev.mysql.com/doc/refman/8.0/en/key-cache-block-size.html

10.10.2.5 关键缓存块大小

可以使用key_cache_block_size变量来指定单个关键缓存的块缓冲区大小。这允许调整索引文件的 I/O 操作性能。

当读取缓冲区的大小等于本机操作系统 I/O 缓冲区的大小时,I/O 操作的最佳性能可以实现。但是,将关键节点的大小设置为 I/O 缓冲区的大小并不总是确保获得最佳的整体性能。当读取大叶节点时,服务器会拉取许多不必要的数据,有效地阻止了对其他叶节点的读取。

要控制MyISAM表的.MYI索引文件中块的大小,请在服务器启动时使用--myisam-block-size选项。

原文:dev.mysql.com/doc/refman/8.0/en/key-cache-restructuring.html

10.10.2.6 重构关键缓存

可通过更新其参数值随时重构关键缓存。例如:

mysql> SET GLOBAL cold_cache.key_buffer_size=4*1024*1024;

如果您为key_buffer_sizekey_cache_block_size关键缓存组件分配一个与组件当前值不同的值,则服务器会销毁缓存的旧结构,并根据新值创建一个新结构。如果缓存包含任何脏块,服务器会在销毁和重新创建缓存之前将它们保存到磁盘。如果更改其他关键缓存参数,则不会发生重构。

重构关键缓存时,服务器首先将任何脏缓冲区的内容刷新到磁盘。之后,缓存内容变得不可用。然而,重构不会阻塞需要使用分配给缓存的索引的查询。相反,服务器直接使用本地文件系统缓存访问表索引。文件系统缓存不如使用关键缓存高效,因此虽然查询会执行,但可以预期会有减速。在缓存重构后,它再次可用于缓存分配给它的索引,并且索引的文件系统缓存使用停止。

10.10.3 预处理语句和存储程序的缓存

原文:dev.mysql.com/doc/refman/8.0/en/statement-caching.html

对于客户端可能在会话期间多次执行的某些语句,服务器会将语句转换为内部结构并缓存该结构以在执行期间使用。缓存使服务器能够更高效地执行,因为它避免了在会话期间再次需要语句时重新转换语句的开销。对这些语句进行转换和缓存:

  • 预处理语句,无论是在 SQL 级别(使用PREPARE语句)处理的还是使用二进制客户端/服务器协议(使用mysql_stmt_prepare() C API 函数)处理的。max_prepared_stmt_count系统变量控制服务器缓存的语句总数。(所有会话中预处理语句数量的总和。)

  • 存储程序(存储过程和函数、触发器和事件)。在这种情况下,服务器会转换并缓存整个程序体。stored_program_cache系统变量指示服务器每个会话缓存的存储程序的大致数量。

服务器在每个会话基础上维护预处理语句和存储程序的缓存。为一个会话缓存的语句对其他会话不可访问。当会话结束时,服务器会丢弃为其缓存的任何语句。

当服务器使用缓存的内部语句结构时,必须注意结构不会过时。对象的元数据更改可能会导致语句使用的对象的当前定义与内部语句结构中表示的定义不匹配。元数据更改会发生在 DDL 语句中,比如创建、删除、修改、重命名或截断表,或者分析、优化或修复表。表内容的更改(例如,使用INSERTUPDATE)不会更改元数据,也不会更改SELECT语句。

这里是问题的示例。假设客户端准备了这个语句:

PREPARE s1 FROM 'SELECT * FROM t1';

SELECT *在内部结构中扩展为表中的列列表。如果使用ALTER TABLE修改表中的列集,那么预处理语句就会过时。如果服务器在客户端下次执行s1时没有检测到这个更改,预处理语句将返回不正确的结果。

为了避免由于预处理语句引用的表或视图的元数据更改而引起的问题,服务器会检测这些更改,并在下次执行时自动重新准备该语句。也就是说,服务器会重新解析该语句并重建内部结构。在引用的表或视图从表定义缓存中刷新时,也会重新解析,无论是隐式地为新条目腾出缓存空间,还是显式地由于FLUSH TABLES

类似地,如果存储程序使用的对象发生更改,服务器会重新解析程序中受影响的语句。

服务器还会检测表达式中对象的元数据更改。这些对象可能被存储程序特定语句使用,例如DECLARE CURSOR或流程控制语句,如IFCASERETURN

为了避免重新解析整个存储程序,服务器仅在需要时重新解析程序中受影响的语句或表达式。例如:

  • 假设表或视图的元数据发生更改。程序中访问该表或视图的SELECT *将重新解析,但不会重新解析不访问该表或视图的SELECT *

  • 当语句受到影响时,服务器会尽可能部分重新解析它。考虑这个CASE语句:

    CASE *case_expr*
      WHEN *when_expr1* ...
      WHEN *when_expr2* ...
      WHEN *when_expr3* ...
      ...
    END CASE
    

    如果元数据更改仅影响WHEN *when_expr3*,那么该表达式将被重新解析。case_expr和其他WHEN表达式不会被重新解析。

重新解析使用了最初转换为内部形式时生效的默认数据库和 SQL 模式。

服务器尝试重新解析最多三次。如果所有尝试都失败,则会发生错误。

重新解析是自动的,但在发生时,会降低预处理语句和存储程序的性能。

对于预处理语句,Com_stmt_reprepare状态变量跟踪重新准备的次数。

10.11 优化锁操作

原文:dev.mysql.com/doc/refman/8.0/en/locking-issues.html

10.11.1 内部锁定方法

10.11.2 表锁定问题

10.11.3 并发插入

10.11.4 元数据锁定

10.11.5 外部锁定

MySQL 使用锁定来管理对表内容的争用:

  • 内部锁定是在 MySQL 服务器内部执行的,通过多个线程管理对表内容的争用。这种类型的锁定是内部的,因为它完全由服务器执行,不涉及其他程序。参见第 10.11.1 节,“内部锁定方法”。

  • 外部锁定发生在服务器和其他程序锁定MyISAM表文件以协调它们之间哪个程序可以在哪个时间访问表。参见第 10.11.5 节,“外部锁定”。

10.11.1 内部锁定方法

原文:dev.mysql.com/doc/refman/8.0/en/internal-locking.html

本节讨论内部锁定;即 MySQL 服务器内部执行的锁定,以管理多个会话对表内容的争用。这种类型的锁定是内部的,因为它完全由服务器执行,不涉及其他程序。有关其他程序在 MySQL 文件上执行的锁定,请参见第 10.11.5 节,“外部锁定”。

  • 行级锁定

  • 表级锁定

  • 选择锁定类型

行级锁定

MySQL 对InnoDB表使用行级锁定,以支持多个会话同时写入访问,使其适用于多用户、高并发和 OLTP 应用程序。

为了避免在单个InnoDB表上执行多个并发写操作时发生死锁,在事务开始时通过为预计修改的每组行发出SELECT ... FOR UPDATE语句来获取必要的锁,即使数据更改语句在事务后面执行。如果事务修改或锁定多个表,请在每个事务内以相同顺序发出适用的语句。死锁会影响性能而不是表示严重错误,因为InnoDB默认会自动检测死锁条件并回滚受影响的事务之一。

在高并发系统中,死锁检测可能导致许多线程等待相同锁时出现减速。有时,禁用死锁检测并依赖于innodb_lock_wait_timeout设置在死锁发生时回滚事务可能更有效。可以使用innodb_deadlock_detect配置选项禁用死锁检测。

行级锁定的优势:

  • 当不同会话访问不同行时,减少锁冲突。

  • 回滚更少的更改。

  • 可以长时间锁定单个行。

表级锁定

MySQL 对MyISAMMEMORYMERGE表使用表级锁定,每次只允许一个会话更新这些表。这种锁定级别使这些存储引擎更适合只读、读多或单用户应用程序。

这些存储引擎通过在查询开始时一次性请求所有所需的锁并始终以相同顺序锁定表来避免死锁。这种权衡是这种策略降低了并发性;其他想要修改表的会话必须等到当前数据更改语句完成。

表级锁定的优点:

  • 需要相对较少的内存(行锁定需要每行或每组行锁定的内存)

  • 在大部分表上使用时速度很快,因为只涉及一个锁。

  • 如果经常在大部分数据上执行GROUP BY操作或必须频繁扫描整个表,则速度很快。

MySQL 授予表写锁如下:

  1. 如果表上没有锁,请在其上放置写锁。

  2. 否则,将锁请求放入写锁队列。

MySQL 授予表读锁如下:

  1. 如果表上没有写锁,请在其上放置读锁。

  2. 否则,将锁请求放入读锁队列。

表更新比表检索具有更高的优先级。因此,当释放锁时,锁将提供给写锁队列中的请求,然后提供给读锁队列中的请求。这确保了即使表上有大量SELECT活动,对表的更新也不会“饿死”。但是,如果对表进行了许多更新,SELECT语句将等待直到没有更多更新。

有关修改读写优先级的信息,请参见第 10.11.2 节,“表锁问题”。

您可以通过检查Table_locks_immediateTable_locks_waited状态变量来分析系统上的表锁争用情况,这些变量分别表示请求表锁可以立即授予的次数以及必须等待的次数:

mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+---------+
| Variable_name         | Value   |
+-----------------------+---------+
| Table_locks_immediate | 1151552 |
| Table_locks_waited    | 15324   |
+-----------------------+---------+

Performance Schema 锁表还提供了锁定信息。请参见第 29.12.13 节,“Performance Schema 锁表”。

MyISAM 存储引擎支持并发插入以减少对给定表的读取者和写入者之间的争用:如果 MyISAM 表在数据文件中间没有空闲块,则行始终插入到数据文件的末尾。在这种情况下,您可以在不锁定的情况下自由混合并发 INSERTSELECT 语句用于 MyISAM 表。也就是说,您可以在其他客户端从中读取时向 MyISAM 表插入行。空洞可能是由于从表中间删除或更新行而导致的。如果有空洞,则并发插入将被禁用,但在所有空洞填满新数据后会自动重新启用。要控制此行为,请使用 concurrent_insert 系统变量。参见 Section 10.11.3, “Concurrent Inserts”。

如果您使用 LOCK TABLES 明确获取表锁,则可以请求 READ LOCAL 锁而不是 READ 锁,以便其他会话在您锁定表时执行并发插入。

当无法进行并发插入时,要在表 t1 上执行许多 INSERTSELECT 操作,您可以将行插入临时表 temp_t1,然后使用临时表中的行更新真实表:

mysql> LOCK TABLES t1 WRITE, temp_t1 WRITE;
mysql> INSERT INTO t1 SELECT * FROM temp_t1;
mysql> DELETE FROM temp_t1;
mysql> UNLOCK TABLES;

选择锁定类型

通常,在以下情况下,表锁优于行级锁:

  • 表的大多数语句是读取操作。

  • 表的语句是读取和写入的混合,其中写入是更新或删除单行,可以通过一个键读取获取:

    UPDATE *tbl_name* SET *column*=*value* WHERE *unique_key_col*=*key_value*;
    DELETE FROM *tbl_name* WHERE *unique_key_col*=*key_value*;
    
  • SELECT 与并发 INSERT 语句结合,以及很少的 UPDATEDELETE 语句。

  • 对整个表进行许多扫描或 GROUP BY 操作,没有任何写入者。

使用更高级别的锁,您可以更容易地通过支持不同类型的锁来调整应用程序,因为与行级锁相比,锁开销更小。

除了行级锁之外的选项:

  • 版本控制(例如 MySQL 中用于并发插入的方式),在同一时间可能有一个写入者和多个读取者。这意味着数据库或表支持根据访问开始时间而为数据提供不同视图。其他常见术语包括“时间旅行”,“写时复制”或“按需复制”。

  • 在许多情况下,按需复制比行级锁更优越。然而,在最坏的情况下,它可能比使用普通锁占用更多内存。

  • 你可以使用应用程序级别的锁,而不是使用行级别的锁,比如 MySQL 中提供的GET_LOCK()RELEASE_LOCK()。这些是咨询性锁,所以只能与相互合作的应用程序一起使用。参见第 14.14 节,“锁定函数”。

10.11.2 表锁定问题

原文:dev.mysql.com/doc/refman/8.0/en/table-locking.html

InnoDB表使用行级锁定,以便多个会话和应用程序可以同时从同一表中读取和写入,而不必等待或产生不一致的结果。对于这种存储引擎,避免使用LOCK TABLES语句,因为它不提供任何额外的保护,而是降低并发性。自动的行级锁定使这些表适用于您最繁忙的数据库和最重要的数据,同时简化应用程序逻辑,因为您不需要锁定和解锁表。因此,InnoDB存储引擎是 MySQL 的默认存储引擎。

MySQL 对所有存储引擎使用表锁定(而不是页面、行或列锁定),除了InnoDB。锁定操作本身没有太多开销。但由于一次只能有一个会话写入表,对于这些其他存储引擎的最佳性能,主要用于经常查询但很少插入或更新的表。

  • 支持 InnoDB 的性能考虑

  • 锁定性能问题的解决方法

支持 InnoDB 的性能考虑

在选择是使用InnoDB还是其他存储引擎创建表时,请记住表锁定的以下缺点:

  • 表锁定使许多会话可以同时从表中读取,但如果会话想要写入表,则必须首先获得独占访问,这意味着它可能必须等待其他会话先完成对表的操作。在更新期间,所有其他想要访问此特定表的会话必须等待更新完成。

  • 当会话因磁盘已满而等待时,表锁定会导致问题,需要在会话继续之前释放空间。在这种情况下,所有想要访问问题表的会话也会进入等待状态,直到更多的磁盘空间可用。

  • 运行时间较长的SELECT语句会阻止其他会话在此期间更新表,使其他会话看起来缓慢或无响应。当会话等待独占访问表以进行更新时,发出SELECT语句的其他会话排队等待,即使对于只读会话也会降低并发性。

锁定性能问题的解决方法

以下项目描述了一些避免或减少由表锁定引起的争用的方法:

  • 考虑将表切换到InnoDB存储引擎,可以在设置期间使用CREATE TABLE ... ENGINE=INNODB,或者对现有表使用ALTER TABLE ... ENGINE=INNODB。有关此存储引擎的更多详细信息,请参见第十七章,InnoDB 存储引擎

  • 优化SELECT语句以使其运行更快,从而使其锁定表的时间更短。您可能需要创建一些汇总表来实现这一点。

  • 使用带有--low-priority-updates启动mysqld。对于仅使用表级锁定的存储引擎(如MyISAMMEMORYMERGE),这会使所有更新(修改)表的语句比SELECT语句具有较低的优先级。在这种情况下,前述情景中的第二个SELECT语句将在UPDATE语句之���执行,并且不会等待第一个SELECT语句完成。

  • 要指定在特定连接中发出的所有更新都应以低优先级执行,请将low_priority_updates服务器系统变量设置为 1。

  • 要给特定的INSERTUPDATEDELETE语句降低优先级,请使用LOW_PRIORITY属性。

  • 要给特定的SELECT语句提高优先级,请使用HIGH_PRIORITY属性。请参见第 15.2.13 节,“SELECT 语句”。

  • 使用较低值的max_write_lock_count系统变量启动mysqld,以强制 MySQL 在特定数量的写锁定表之后(例如,对于插入操作)暂时提升所有等待表的SELECT语句的优先级。这允许在一定数量的写锁之后进行读锁定。

  • 如果您在混合使用SELECTDELETE语句时遇到问题,DELETELIMIT选项可能会有所帮助。请参见第 15.2.2 节,“DELETE 语句”。

  • SELECT语句中使用SQL_BUFFER_RESULT可以帮助缩短表锁定的持续时间。请参见第 15.2.13 节,“SELECT 语句”。

  • 将表内容拆分为单独的表可能会有所帮助,允许查询针对一个表中的列运行,而更新则限制在另一个表中的列。

  • 您可以更改mysys/thr_lock.c中的锁定代码以使用单个队列。在这种情况下,写锁和读锁将具有相同的优先级,这可能有助于某些应用程序。

10.11.3 并发插入

原文:dev.mysql.com/doc/refman/8.0/en/concurrent-inserts.html

MyISAM 存储引擎支持并发插入,以减少读者和写者在给定表中的竞争:如果 MyISAM 表在数据文件中没有空洞(中间删除的行),则可以执行 INSERT 语句,同时向表的末尾添加行,而 SELECT 语句正在从表中读取行。如果有多个 INSERT 语句,则它们会被排队并按顺序执行,与 SELECT 语句并发执行。并发 INSERT 的结果可能不会立即可见。

concurrent_insert 系统变量可用于修改并发插入处理。默认情况下,该变量设置为 AUTO(或 1),并发插入会按照上述描述进行处理。如果 concurrent_insert 设置为 NEVER(或 0),则禁用并发插入。如果该变量设置为 ALWAYS(或 2),即使对于具有已删除行的表,也允许在表的末尾进行并发插入。另请参阅 concurrent_insert 系统变量的描述。

如果您正在使用二进制日志,对于 CREATE ... SELECTINSERT ... SELECT 语句,将并发插入转换为普通插入。这样做是为了确保您可以通过在备份操作期间应用日志来重新创建表的精确副本。请参阅 Section 7.4.4, “The Binary Log”。此外,对于这些语句,会在所选表上放置读锁,以阻止对该表的插入。其效果是,该表的并发插入也必须等待。

使用 LOAD DATA,如果您在满足并发插入条件的 MyISAM 表中指定 CONCURRENT(即,它在中间不包含空闲块),则其他会话可以在 LOAD DATA 执行时从表中检索数据。即使没有其他会话同时使用该表,使用 CONCURRENT 选项也会稍微影响 LOAD DATA 的性能。

如果您指定 HIGH_PRIORITY,它会覆盖 --low-priority-updates 选项的效果,如果服务器是以该选项启动的。它还会导致不使用并发插入。

对于LOCK TABLEREAD LOCALREAD之间的区别在于,READ LOCAL允许非冲突的INSERT语句(并发插入)在持有锁的情况下执行。然而,如果在持有锁的同时要使用外部进程来操作数据库,则不能使用此选项。

10.11.4 元数据锁定

原文:dev.mysql.com/doc/refman/8.0/en/metadata-locking.html

MySQL 使用元数据锁定来管理对数据库对象的并发访问,并确保数据一致性。元数据锁定不仅适用于表,还适用于模式、存储程序(过程、函数、触发器、计划事件)、表空间、使用 GET_LOCK() 函数获取的用户锁(参见 Section 14.14, “锁定函数”),以及在 Section 7.6.9.1, “锁定服务” 中描述的锁定服务中获取的锁。

Performance Schema metadata_locks 表公开了元数据锁信息,可以用于查看哪些会话持有锁,正在等待锁等情况。详情请参见 Section 29.12.13.3, “metadata_locks 表”。

元数据锁定确实会带来一些开销,随着查询量的增加而增加。当多个查询尝试访问相同对象时,元数据争用会增加。

元数据锁定不是表定义缓存的替代品,其互斥体和锁与 LOCK_open 互斥体不同。以下讨论提供了有关元数据锁定工作原理的一些信息。

  • 元数据锁获取

  • 元数据锁释放

元数据锁获取

如果有多个等待者请求同一个锁,则最高优先级的锁请求首先得到满足,与 max_write_lock_count 系统变量相关的一个例外。写锁请求比读锁请求具有更高的优先级。但是,如果 max_write_lock_count 设置为较低值(比如,10),则读锁请求可能优先于待处理的写锁请求,如果读锁请求已经被忽略以优先处理 10 个写锁请求。通常情况下,这种行为不会发生,因为 max_write_lock_count 默认情况下具有非常大的值。

语句逐个获取元数据锁,而不是同时获取,并在此过程中执行死锁检测。

DML 语句通常按照语句中提到表的顺序获取锁。

DDL 语句、LOCK TABLES和其他类似语句尝试通过按名称顺序在显式命名的表上获取锁来减少并发 DDL 语句之间可能发生的死锁数量。对于隐式使用的表(例如外键关系中必须锁定的表),锁可能按不同顺序获取。

例如,RENAME TABLE是一个按名称顺序获取锁的 DDL 语句:

  • 这个RENAME TABLE语句将tbla重命名为其他内容,并将tblc重命名为tbla

    RENAME TABLE tbla TO tbld, tblc TO tbla;
    

    该语句按顺序在tblatblctbld上获取元数据锁(因为tbld在名称顺序中跟随tblc)。

  • 这个稍有不同的语句还将tbla重命名为其他内容,并将tblc重命名为tbla

    RENAME TABLE tbla TO tblb, tblc TO tbla;
    

    在这种情况下,语句按顺序在tblatblbtblc上获取元数据锁(因为tblb在名称顺序中位于tblc之前)。

两个语句按顺序获取tblatblc上的锁,但在剩余表名的锁是在tblc之前还是之后获取有所不同。

当多个事务同时执行时,元数据锁获取顺序可能会影响操作结果,如下例所示。

从具有相同结构的两个表xx_new开始。三个客户端发出涉及这些表的语句:

Client 1:

LOCK TABLE x WRITE, x_new WRITE;

该语句按名称顺序请求并获取xx_new上的写锁。

Client 2:

INSERT INTO x VALUES(1);

该语句请求并在x上等待写锁。

Client 3:

RENAME TABLE x TO x_old, x_new TO x;

该语句按名称顺序请求xx_newx_old上的排他锁,但在等待x上的锁时被阻塞。

Client 1:

UNLOCK TABLES;

该语句释放了xx_new上的写锁。Client 3 对x的排他锁请求比 Client 2 的写锁请求优先级更高,因此 Client 3 先获取了x的锁,然后也获取了x_newx_old的锁,执行重命名操作,然后释放锁。然后 Client 2 获取了x的锁,执行插入操作,然后释放锁。

锁获取顺序导致RENAME TABLEINSERT之前执行。插入发生的x是 Client 2 发出插入时命名为x_new,并由 Client 3 重命名为x的表:

mysql> SELECT * FROM x;
+------+
| i    |
+------+
|    1 |
+------+

mysql> SELECT * FROM x_old;
Empty set (0.01 sec)

现在改为以名称为xnew_x的表具有相同结构开始。再次,三个客户端发出涉及这些表的语句:

Client 1:

LOCK TABLE x WRITE, new_x WRITE;

该语句按名称顺序请求并获取new_xx上的写锁。

Client 2:

INSERT INTO x VALUES(1);

该语句请求并在x上等待写锁。

Client 3:

RENAME TABLE x TO old_x, new_x TO x;

该语句按名称顺序请求new_xold_xx上的排他锁,但在等待new_x上的锁时被阻塞。

Client 1:

UNLOCK TABLES;

该语句释放了xnew_x的写锁。对于x,唯一的挂起请求来自客户端 2,因此客户端 2 获取其锁,执行插入操作,并释放锁。对于new_x,唯一的挂起请求来自客户端 3,允许其获取该锁(以及old_x的锁)。重命名操作仍然会因为在客户端 2 插入完成并释放其锁之前对x的锁而阻塞。然后客户端 3 获取x的锁,执行重命名操作,并释放其锁。

在这种情况下,锁获取顺序导致INSERTRENAME TABLE之前执行。插入发生的x是原始的x,现在被重命名为old_x

mysql> SELECT * FROM x;
Empty set (0.01 sec)

mysql> SELECT * FROM old_x;
+------+
| i    |
+------+
|    1 |
+------+

如果并发语句中锁获取顺序对操作结果有影响,如前面的例子,您可以调整表名以影响锁获取顺序。

为了防止相关表上的冲突 DML 和 DDL 操作同时执行,元数据锁会根据外键约束扩展到相关表。在更新父表时,会在更新外键元数据时在子表上获取元数据锁。外键元数据由子表拥有。

元数据锁释放

为确保事务的可串行化,服务器不得允许一个会话在另一个会话中的未完成的显式或隐式启动的事务中对表执行数据定义语言(DDL)语句。服务器通过在事务中使用的表上获取元数据锁并推迟释放这些锁直到事务结束来实现这一点。表上的元数据锁阻止对表结构的更改。这种锁定方法意味着一个会话中正在使用的表在事务结束之前不能被其他会话用于 DDL 语句。

这个原则不仅适用于事务表,也适用于非事务表。假设一个会话开始一个事务,使用事务表t和非事务表nt如下:

START TRANSACTION;
SELECT * FROM t;
SELECT * FROM nt;

服务器在事务结束之前会持有表tnt的元数据锁。如果另一个会话尝试在任一表上执行 DDL 或写锁操作,它会阻塞,直到事务结束时释放元数据锁。例如,如果第二个会话尝试执行以下任何操作,它会被阻塞:

DROP TABLE t;
ALTER TABLE t ...;
DROP TABLE nt;
ALTER TABLE nt ...;
LOCK TABLE t ... WRITE;

相同的行为也适用于LOCK TABLES ... READ。也就是说,显式或隐式启动的更新任何表(事务或非事务)的事务会被LOCK TABLES ... READ阻塞,并且被该表阻塞。

如果服务器为一个在语法上有效但在执行过程中失败的语句获取元数据锁,它不会提前释放锁。锁的释放仍然延迟到事务结束,因为失败的语句被写入二进制日志,而锁保护日志的一致性。

在自动提交模式下,每个语句实际上是一个完整的事务,因此为语句获取的元数据锁仅在语句结束时保持。

PREPARE语句期间获取的元数据锁在语句准备完成后会被释放,即使准备过程发生在多语句事务中。

截至 MySQL 8.0.13 版本,对于处于PREPARED状态的 XA 事务,元数据锁在客户端断开连接和服务器重新启动时会被保留,直到执行XA COMMITXA ROLLBACK

10.11.5 外部锁定

原文:dev.mysql.com/doc/refman/8.0/en/external-locking.html

外部锁定是通过文件系统锁定来管理多个进程对MyISAM数据库表的争用。在单个进程(如 MySQL 服务器)不能被假定为是唯一需要访问表的进程的情况下,会使用外部锁定。以下是一些示例:

  • 如果您运行多个使用相同数据库目录(不推荐)的服务器,则每个服务器必须启用外部锁定。

  • 如果您使用myisamchkMyISAM表执行表维护操作,则必须确保服务器未运行,或者服务器已启用外部锁定,以便根据需要锁定表文件,以便与myisamchk协调访问表。对于使用myisampackMyISAM表进行打包也是如此。

    如果服务器启用了外部锁定,您可以随时使用myisamchk进行读取操作,例如检查表。在这种情况下,如果服务器尝试更新myisamchk正在使用的表,服务器会等待myisamchk完成后才继续。

    如果您使用myisamchk进行写操作,如修复或优化表,或者使用myisampack对表进行打包,必须始终确保mysqld服务器未使用该表。如果不停止mysqld,至少在运行myisamchk之前执行mysqladmin flush-tables。如果服务器和myisamchk同时访问表,您的表可能会损坏

在外部锁定生效时,每个需要访问表格的进程在继续访问表格之前会获取表格文件的文件系统锁定。如果无法获取所有必要的锁定,该进程将被阻止访问表格,直到可以获取锁定(在当前持有锁定的进程释放它们后)。

外部锁定会影响服务器性能,因为服务器有时必须等待其他进程才能访问表格。

如果你运行单个服务器来访问给定的数据目录(这是通常情况),并且没有其他程序需要在服务器运行时修改表格,那么外部锁定是不必要的。如果只是用其他程序读取表格,那么不需要外部锁定,尽管myisamchk在读取表格时,如果服务器更改表格,可能会报告警告。

禁用外部锁定后,要使用myisamchk,你必须在myisamchk执行时要么停止服务器,要么在运行myisamchk之前锁定和刷新表格。为避免此要求,使用CHECK TABLEREPAIR TABLE语句来检查和修复MyISAM表格。

对于mysqld,外部锁定由skip_external_locking系统变量的值控制。当启用此变量时,外部锁定被禁用,反之亦然。外部锁定默认情况下被禁用。

可以通过在服务器启动时使用--external-locking--skip-external-locking选项来控制外部锁定的使用。

如果使用外部锁定选项来允许多个 MySQL 进程更新MyISAM表格,请不要使用带有ALL设置的delay_key_write系统变量启动服务器,也不要为任何共享表格使用DELAY_KEY_WRITE=1表选项。否则,可能会导致索引损坏。

满足这个条件的最简单方法是始终将--external-locking--delay-key-write=OFF一起使用。(默认情况下不这样做,因为在许多设置中,拥有前述选项的混合是有用的。)

10.12 优化 MySQL 服务器

原文:dev.mysql.com/doc/refman/8.0/en/optimizing-server.html

10.12.1 优化磁盘 I/O

10.12.2 使用符号链接

10.12.3 优化内存使用

本节讨论了数据库服务器的优化技术,主要涉及系统配置而不是调整 SQL 语句。本节中的信息适用于希望确保管理的服务器性能和可伸缩性的数据库管理员;为构建包括设置数据库的安装脚本的开发人员;以及自行运行 MySQL 进行开发、测试等的人,他们希望最大化自己的生产力。

10.12.1 优化磁盘 I/O

原文:dev.mysql.com/doc/refman/8.0/en/disk-issues.html

本节描述了当你可以为数据库服务器提供更多和更快的存储硬件时如何配置存储设备。有关优化InnoDB配置以提高 I/O 性能的信息,请参见第 10.5.8 节,“优化 InnoDB 磁盘 I/O”。

  • 磁盘寻道是一个巨大的性能瓶颈。当数据量变得如此之大以至于有效缓存变得不可能时,这个问题变得更加明显。对于访问数据比较随机的大型数据库,你可以确信你至少需要一个磁盘寻道来读取数据,以及几个磁盘寻道来写入数据。为了最小化这个问题,使用寻道时间较低的磁盘。

  • 增加可用磁盘轴承的数量(从而减少寻道开销),可以通过将文件符号链接到不同的磁盘或者对磁盘进行条带化来实现:

    • 使用符号链接

      这意味着,对于MyISAM表,你可以将索引文件和数据文件从它们在数据目录中的通常位置创建符号链接到另一个磁盘(也可以进行条带化)。假设该磁盘没有用于其他目的,这将使寻道和读取时间更好。参见第 10.12.2 节,“使用符号链接”。

      InnoDB表不支持使用符号链接。但是,可以将InnoDB数据和日志文件放在不同的物理磁盘上。更多信息,请参见第 10.5.8 节,“优化 InnoDB 磁盘 I/O”。

    • 条带化

      条带化意味着你有许多磁盘,并且将第一个块放在第一个磁盘上,第二个块放在第二个磁盘上,第N个块放在(N MOD 磁盘数量)磁盘上,依此类推。这意味着如果你的正常数据大小小于条带大小(或者完全对齐),你将获得更好的性能。条带化非常依赖于操作系统和条带大小,因此请使用不同的条带大小对你的应用程序进行基准测试。参见第 10.13.2 节,“使用自己的基准测试”。

      条带化的速度差异非常依赖于参数。根据你设置的条带化参数和磁盘数量,你可能会得到数量级差异的结果。你必须选择优化随机访问还是顺序访问。

  • 为了可靠性,你可能希望使用 RAID 0+1(条带化加镜像),但在这种情况下,你需要 2×N个驱动器来存储N个驱动器的数据。如果你有资金,这可能是最好的选��。然而,你可能还需要投资一些卷管理软件来有效处理它。

  • 根据数据类型的重要性,可以根据 RAID 级别进行调整。例如,将可以重新生成的半重要数据存储在 RAID 0 磁盘上,但将主机信息和日志等真正重要的数据存储在 RAID 0+1 或 RAID N 磁盘上。如果有许多写操作,RAID N 可能会成为问题,因为更新奇偶校验位需要时间。

  • 您还可以设置数据库使用的文件系统的参数:

    如果您不需要知道文件上次访问的时间(这在数据库服务器上并不真正有用),您可以使用-o noatime选项挂载文件系统。这样可以跳过对文件系统中 inode 上的最后访问时间的更新,从而避免一些磁盘寻道。

    在许多操作系统上,您可以通过使用-o async选项挂载文件系统来异步更新文件系统。如果您的计算机相当稳定,这应该可以在不牺牲太多可靠性的情况下提供更好的性能。(在 Linux 上,默认情况下启用此标志。)

使用 NFS 与 MySQL

在考虑是否与 MySQL 一起使用 NFS 时应谨慎。潜在问题因操作系统和 NFS 版本而异,包括以下内容:

  • 放置在 NFS 卷上的 MySQL 数据和日志文件被锁定并无法使用。在多个 MySQL 实例访问相同数据目录或 MySQL 因不当关闭(例如由于断电)而发生锁定问题的情况下可能会发生。NFS 版本 4 通过引入咨询和基于租约的锁定来解决潜在的锁定问题。然而,不建议在多个 MySQL 实例之间共享数据目录。

  • 由于消息接收顺序混乱或网络流量丢失而引入的数据不一致性。为避免此问题,请使用带有hardintr挂载选项的 TCP。

  • 最大文件大小限制。NFS 版本 2 客户端只能访问文件的最低 2GB(带符号 32 位偏移)。NFS 版本 3 客户端支持更大的文件(最多 64 位偏移)。支持的最大文件大小还取决于 NFS 服务器的本地文件系统。

在专业 SAN 环境或其他存储系统中使用 NFS 往往比在此类环境之外使用 NFS 提供更高的可靠性。然而,在 SAN 环境中使用 NFS 可能比直接连接或总线连接的非旋转存储慢。

如果选择使用 NFS,建议使用 NFS 版本 4 或更高版本,并在部署到生产环境之前彻底测试您的 NFS 设置。

10.12.2 使用符号链接

原文:dev.mysql.com/doc/refman/8.0/en/symbolic-links.html

10.12.2.1 在 Unix 上使用符号链接处理数据库

10.12.2.2 在 Unix 上使用符号链接处理 MyISAM 表

10.12.2.3 在 Windows 上使用符号链接处理数据库

您可以将数据库或表从数据库目录移动到其他位置,并用指向新位置的符号链接替换它们。例如,您可能想要这样做,将数据库移动到具有更多可用空间的文件系统,或者通过将表分布到不同的磁盘上来提高系统速度。

对于InnoDB表,使用CREATE TABLE语句的DATA DIRECTORY子句,而不是符号链接,如 Section 17.6.1.2,“外部创建表”中所解释的那样。这个新功能是一种受支持的跨平台技术。

推荐的方法是将整个数据库目录创建符号链接到不同的磁盘上。只有在万不得已的情况下才将MyISAM表创建符号链接。

要确定数据目录的位置,请使用以下语句:

SHOW VARIABLES LIKE 'datadir';

原文:dev.mysql.com/doc/refman/8.0/en/symbolic-links-to-databases.html

10.12.2.1 在 Unix 上使用符号链接进行数据库操作

在 Unix 上,可以按照以下步骤使用符号链接来创建数据库:

  1. 使用CREATE DATABASE创建数据库:

    mysql> CREATE DATABASE mydb1;
    

    使用CREATE DATABASE在 MySQL 数据目录中创建数据库,并允许服务器更新数据字典中有关数据库目录的信息。

  2. 停止服务器以确保在移动数据库时不会发生任何活动。

  3. 将数据库目录移动到某个具有可用空间的磁盘上。例如,使用tarmv。如果使用复制而不是移动数据库目录的方法,请在复制后删除原始数据库目录。

  4. 在数据目录中创建一个软链接,指向已移动的数据库目录。

    $> ln -s */path/to/mydb1* */path/to/datadir*
    

    该命令在数据目录中创建一个名为mydb1的符号链接。

  5. 重新启动服务器。

原文:dev.mysql.com/doc/refman/8.0/en/symbolic-links-to-tables.html

10.12.2.2 在 Unix 上使用符号链接的 MyISAM 表

注意

此处描述的符号链接支持,以及控制它的--symbolic-links选项已被弃用;预计在未来的 MySQL 版本中将删除这些内容。此外,默认情况下该选项已禁用。

仅对 MyISAM 表完全支持符号链接。对于其他存储引擎表使用的文件,如果尝试使用符号链接可能会出现奇怪的问题。对于 InnoDB 表,请使用 Section 17.6.1.2, “Creating Tables Externally”中解释的替代技术。

在没有完全运行realpath()调用的系统上不要对表进行符号链接。 (Linux 和 Solaris 支持realpath()). 要确定您的系统是否支持符号链接,请使用以下语句检查have_symlink系统变量的值:

SHOW VARIABLES LIKE 'have_symlink';

以下是 MyISAM 表的符号链接处理方式:

  • 在数据目录中,您始终有数据(.MYD)文件和索引(.MYI)文件。数据文件和索引文件可以移动到其他位置,并在数据目录中用符号链接替换。

  • 你可以将数据文件和索引文件分别链接到不同的目录。

  • 要指示正在运行的 MySQL 服务器执行符号链接操作,请使用CREATE TABLE中的DATA DIRECTORYINDEX DIRECTORY选项。参见 Section 15.1.20, “CREATE TABLE Statement”。或者,如果mysqld没有运行,可以使用命令行中的ln -s手动完成符号链接。

    注意

    DATA DIRECTORYINDEX DIRECTORY选项中的路径可能不包括 MySQL data 目录。 (Bug #32167)

  • myisamchk不会用数据文件或索引文件替换符号链接。它直接在符号链接指向的文件上工作。任何临时文件都将在数据文件或索引文件所在的目录中创建。对于ALTER TABLEOPTIMIZE TABLEREPAIR TABLE语句也是如此。

  • 注意

    当您删除一个使用符号链接的表时,符号链接和符号链接指向的文件都会被删除。这是一个极好的理由root操作系统用户身份运行mysqld或允许操作系统用户对 MySQL 数据库目录具有写访问权限。

  • 如果您使用ALTER TABLE ... RENAMERENAME TABLE重命名一个表,并且不将表移动到另一个数据库,则数据库目录中的符号链接将被重命名为新名称,并相应地重命名数据文件和索引文件。

  • 如果您使用ALTER TABLE ... RENAMERENAME TABLE将表移动到另一个数据库,表将移动到另一个数据库目录。如果表名发生更改,则新数据库目录中的符号链接将被重命名为新名称,并相应地重命名数据文件和索引文件。

  • 如果您不使用符号链接,请使用--skip-symbolic-links选项启动mysqld,以确保没有人可以使用mysqld删除或重命名数据目录之外的文件。

这些表符号链接操作不受支持:

  • ALTER TABLE会忽略DATA DIRECTORYINDEX DIRECTORY表选项。

原文:dev.mysql.com/doc/refman/8.0/en/windows-symbolic-links.html

10.12.2.3 在 Windows 上使用符号链接进行数据库操作

在 Windows 上,可以使用符号链接来创建数据库目录。这使您可以通过设置符号链接将数据库目录放在不同的位置(例如,不同的磁盘)上。在 Windows 上使用数据库符号链接类似于在 Unix 上的使用,尽管设置链接的过程有所不同。

假设您想要将名为 mydb 的数据库的数据库目录放在 D:\data\mydb。为此,在 MySQL 数据目录中创建一个指向 D:\data\mydb 的符号链接。但是,在创建符号链接之前,请确保 D:\data\mydb 目录存在,必要时进行创建。如果您已经在数据目录中有一个名为 mydb 的数据库目录,请将其移动到 D:\data。否则,符号链接将不起作用。为避免问题,请确保在移动数据库目录时服务器未运行。

在 Windows 上,您可以使用 mklink 命令创建符号链接。此命令需要管理员权限。

  1. 确保所需的数据库路径存在。在本示例中,我们使用 D:\data\mydb 和一个名为 mydb 的数据库。

  2. 如果数据库尚不存在,请在 mysql 客户端中发出 CREATE DATABASE mydb 来创建它。

  3. 停止 MySQL 服务。

  4. 使用 Windows 资源管理器或命令行,将数据目录中的 mydb 目录移动到 D:\data,替换同名目录。

  5. 如果您尚未使用命令提示符,请打开它,并将位置更改为数据目录,如下所示:

    C:\> cd *\path\to\datadir*
    

    如果您的 MySQL 安装在默认位置,您可以使用以下命令:

    C:\> cd C:\ProgramData\MySQL\MySQL Server 8.0\Data
    
  6. 在数据目录中,创建一个名为 mydb 的符号链接,指向数据库目录的位置:

    C:\> mklink /d mydb D:\data\mydb
    
  7. 启动 MySQL 服务。

之后,所有在数据库 mydb 中创建的表都将在 D:\data\mydb 中创建。

或者,在 MySQL 支持的任何 Windows 版本上,您可以通过在数据目录中创建一个包含指向目标目录路径的 .sym 文件来创建 MySQL 数据库的符号链接。该文件应命名为 *db_name*.sym,其中 db_name 是数据库名称。

Windows 上默认启用使用 .sym 文件创建数据库符号链接的支持。如果您不需要 .sym 文件符号链接,可以通过使用 --skip-symbolic-links 选项启动 mysqld 来禁用对它们的支持。要确定您的系统是否支持 .sym 文件符号链接,请使用以下语句检查 have_symlink 系统变量的值:

SHOW VARIABLES LIKE 'have_symlink';

要创建 .sym 文件符号链接,请按照以下步骤进行:

  1. 将位置更改为数据目录:

    C:\> cd *\path\to\datadir*
    
  2. 在数据目录中,创建一个名为mydb.sym的文本文件,其中包含此路径名:D:\data\mydb\

    注意

    新数据库和表的路径名应为绝对路径。如果您指定相对路径,则位置相对于mydb.sym文件。

在此之后,所有在数据库mydb中创建的表都将被创建在D:\data\mydb中。

10.12.3 优化内存使用

原文:dev.mysql.com/doc/refman/8.0/en/optimizing-memory.html

MySQL 如何使用内存

监控 MySQL 内存使用

启用大页支持

原文:dev.mysql.com/doc/refman/8.0/en/memory-use.html

10.12.3.1 MySQL 如何使用内存

MySQL 分配缓冲区和缓存以提高数据库操作的性能。默认配置旨在允许 MySQL 服务器在具有大约 512MB RAM 的虚拟机上启动。您可以通过增加某些与缓存和缓冲区相关的系统变量的值来提高 MySQL 的性能。您还可以修改默认配置以在内存有限的系统上运行 MySQL。

以下列表描述了 MySQL 使用内存的一些方式。在适用的情况下,引用了相关的系统变量。一些项目是存储引擎或特定功能。

  • InnoDB缓冲池是一个保存用于表、索引和其他辅助缓冲区的缓存InnoDB数据的内存区域。为了提高高容量读操作的效率,缓冲池被划分为页,这些页可能可以容纳多行。为了提高缓存管理的效率,缓冲池被实现为页的链表;很少使用的数据会根据 Lru 算法的变体从缓存中淘汰。更多信息,请参见第 17.5.1 节,“缓冲池”。

    缓冲池的大小对系统性能很重要:

    • InnoDB在服务器启动时为整个缓冲池分配内存,使用malloc()操作。innodb_buffer_pool_size系统变量定义了缓冲池的大小。通常,推荐的innodb_buffer_pool_size值为系统内存的 50 到 75%。innodb_buffer_pool_size可以在服务器运行时动态配置。更多信息,请参见第 17.8.3.1 节,“配置 InnoDB 缓冲池大小”。

    • 在具有大量内存的系统上,可以通过将缓冲池划分为多个缓冲池实例来提高并发性。innodb_buffer_pool_instances系统变量定义了缓冲池实例的数量。

    • 过小的缓冲池可能会导致过多的翻转,因为页面从缓冲池刷新出来,只是在短时间后再次需要。

    • 过大的缓冲池可能会导致由于内存竞争而发生交换。

  • 存储引擎接口使优化器能够提供关于要用于估计可能读取多行的扫描的记录缓冲区大小的信息。缓冲区大小可以根据估计的大小变化。InnoDB使用这种可变大小的缓冲能力来利用行预取,并减少锁定和 B 树导航的开销。

  • 所有线程共享MyISAM关键缓冲区。key_buffer_size系统变量确定其大小。

    对于每个MyISAM表,服务器打开一次索引文件;对于每个同时运行访问该表的线程,数据文件会被打开一次。对于每个并发线程,都会分配一个表结构,每个列的列结构,以及大小为3 * *N*的缓冲区(其中N是最大行长度,不包括BLOB列)。一个BLOB列需要五到八个字节加上BLOB数据的长度。MyISAM存储引擎为内部使用维护了一个额外的行缓冲区。

  • myisam_use_mmap系统变量可以设置为 1,以启用所有MyISAM表的内存映射。

  • 如果一个内部内存临时表变得太大(根据tmp_table_sizemax_heap_table_size系统变量确定),MySQL 会自动将表从内存转换为磁盘格式。从 MySQL 8.0.16 开始,磁盘临时表总是使用InnoDB存储引擎。(以前,用于此目的的存储引擎是由internal_tmp_disk_storage_engine系统变量确定的,该变量不再受支持。)您可以按照第 10.4.4 节,“MySQL 中的内部临时表使用”中描述的方式增加临时表的大小。

    对于使用CREATE TABLE显式创建的MEMORY表,只有max_heap_table_size系统变量确定表可以增长多大,而且不会转换为磁盘格式。

  • MySQL 性能模式是一个用于监视 MySQL 服务器在低级别执行的功能。性能模式动态地逐步分配内存,根据实际服务器负载来调整内存使用,而不是在服务器启动期间分配所需内存。一旦分配了内存,直到服务器重新启动之前都不会释放。更多信息,请参阅第 29.17 节,“性能模式内存分配模型”。

  • 服务器用于管理客户端连接的每个线程都需要一些特定于线程的空间。以下列表指示了这些空间以及控制它们大小的系统变量:

    • 一个堆栈(thread_stack

    • 一个连接缓冲区(net_buffer_length

    • 一个结果缓冲区(net_buffer_length)

    连接缓冲区和结果缓冲区的初始大小均为net_buffer_length字节,但根据需要动态扩大至max_allowed_packet字节。每个 SQL 语句执行完毕后,结果缓冲区会缩小至net_buffer_length字节。在语句运行时,当前语句字符串的副本也会被分配。

    每个连接线程使用内存来计算语句摘要。服务器为每个会话分配max_digest_length字节。请参阅第 29.10 节,“性能模式语句摘要和采样”。

  • 所有线程共享相同的基本内存。

  • 当不再需要一个线程时,分配给它的内存会被释放并返回给系统,除非线程重新进入线程缓存。在这种情况下,内存仍然保持分配状态。

  • 每个执行表的顺序扫描的请求都会分配一个读取缓冲区。read_buffer_size系统变量确定了缓冲区的大小。

  • 在以任意顺序读取行(例如,遵循排序)时,可能会分配一个随机读取缓冲区以避免磁盘查找。read_rnd_buffer_size系统变量确定了���冲区的大小。

  • 所有连接都在单次执行中执行,大多数连接甚至可以在不使用临时表的情况下完成。大多数临时表都是基于内存的哈希表。具有大行长度(计算为所有列长度之和)或包含BLOB列的临时表存储在磁盘上。

  • 大多数执行排序操作的请求会分配一个排序缓冲区,并根据结果集大小分配零到两个临时文件。请参阅 Section B.3.3.5, “Where MySQL Stores Temporary Files”。

  • 几乎所有的解析和计算都是在线程本地和可重用的内存池中完成的。对于小项目,不需要额外的内存开销,从而避免了常规的缓慢内存分配和释放。只有对于异常大的字符串才会分配内存。

  • 对于每个具有BLOB列的表,缓冲区会动态扩大以读取更大的BLOB值。如果扫描一个表,缓冲区会增长到最大的BLOB值的大小。

  • MySQL 需要为表缓存分配内存和描述符。所有正在使用的表的处理程序结构保存在表缓存中,并按照“先进先出”(FIFO)的方式进行管理。table_open_cache系统变量定义了初始表缓存大小;请参阅 Section 10.4.3.1, “How MySQL Opens and Closes Tables”。

    MySQL 还需要为表定义缓存分配内存。table_definition_cache系统变量定义了可以存储在表定义缓存中的表定义数量。如果使用大量表,可以创建一个大的表定义缓存以加快表的打开速度。表定义缓存占用的空间较小,不使用文件描述符,与表缓存不同。

  • 一个FLUSH TABLES语句或mysqladmin flush-tables命令会立即关闭所有未使用的表,并标记所有正在使用的表在当前执行线程完成时关闭。这有效地释放了大部分正在使用的内存。FLUSH TABLES在所有表关闭后才会返回。

  • 服务器会因为GRANTCREATE USERCREATE SERVERINSTALL PLUGIN语句而在内存中缓存信息。这些内存不会被相应的REVOKEDROP USERDROP SERVERUNINSTALL PLUGIN语句释放,因此对于执行许多导致缓存的语句实例的服务器,除非使用FLUSH PRIVILEGES释放,否则缓存内存使用量会增加。

  • 在复制拓扑中,以下设置会影响内存使用,并可以根据需要进行调整:

    • 复制源上的max_allowed_packet系统变量限制源发送给其复制品进行处理的最大消息大小。此设置默认为 64M。

    • 多线程复制品上的replica_pending_jobs_size_max(从 MySQL 8.0.26 开始)或 MySQL 8.0.26 之前的slave_pending_jobs_size_max系统变量设置用于保存等待处理消息的最大内存量。此设置默认为 128M。只有在需要时才分配内存,但如果您的复制拓扑有时处理大事务,则可能会使用它。这是一个软限制,可以处理更大的事务。

    • 复制源或复制品上的rpl_read_size系统变量控制从二进制日志文件和中继日志文件中读取的最小数据量(以字节为单位)。默认值为 8192 字节。为从二进制日志和中继日志文件中读取数据的每个线程分配了与此值大小相同的缓冲区,包括源上的转储线程和复制品上的协调器线程。

    • binlog_transaction_dependency_history_size系统变量限制保存在内存中的行哈希数量。

    • max_binlog_cache_size系统变量指定单个事务的内存使用上限。

    • max_binlog_stmt_cache_size系统变量指定语句缓存的内存使用上限。

ps 和其他系统状态程序可能会报告说mysqld使用了大量内存。这可能是由于不同内存地址上的线程堆栈导致的。例如,Solaris 版本的 ps 将堆栈之间未使用的内存计算为已使用内存。要验证这一点,请使用 swap -s 检查可用交换空间。我们使用了几种内存泄漏检测工具来测试mysqld(包括商业和开源的),因此不应该有内存泄漏。

原文:dev.mysql.com/doc/refman/8.0/en/monitor-mysql-memory-use.html

10.12.3.2 监控 MySQL 内存使用情况

以下示例演示了如何使用性能模式和 sys 模式来监控 MySQL 内存使用情况。

大多数性能模式内存工具默认处于禁用状态。可以通过更新性能模式setup_instruments表的ENABLED列来启用工具。内存工具的名称形式为memory/*code_area*/*instrument_name*,其中code_area是诸如sqlinnodb之类的值,instrument_name是工具的详细信息。

  1. 要查看可用的 MySQL 内存工具,请查询性能模式setup_instruments表。以下查询返回所有代码区域的数百个内存工具。

    mysql> SELECT * FROM performance_schema.setup_instruments
           WHERE NAME LIKE '%memory%';
    

    通过指定代码区域,您可以缩小结果范围。例如,您可以通过指定innodb作为代码区域来限制结果为InnoDB内存工具。

    mysql> SELECT * FROM performance_schema.setup_instruments
           WHERE NAME LIKE '%memory/innodb%';
    +-------------------------------------------+---------+-------+
    | NAME                                      | ENABLED | TIMED |
    +-------------------------------------------+---------+-------+
    | memory/innodb/adaptive hash index         | NO      | NO    |
    | memory/innodb/buf_buf_pool                | NO      | NO    |
    | memory/innodb/dict_stats_bg_recalc_pool_t | NO      | NO    |
    | memory/innodb/dict_stats_index_map_t      | NO      | NO    |
    | memory/innodb/dict_stats_n_diff_on_level  | NO      | NO    |
    | memory/innodb/other                       | NO      | NO    |
    | memory/innodb/row_log_buf                 | NO      | NO    |
    | memory/innodb/row_merge_sort              | NO      | NO    |
    | memory/innodb/std                         | NO      | NO    |
    | memory/innodb/trx_sys_t::rw_trx_ids       | NO      | NO    |
    ...
    

    根据您的 MySQL 安装,代码区域可能包括performance_schemasqlclientinnodbmyisamcsvmemoryblackholearchivepartition等。

  2. 要启用内存工具,请向您的 MySQL 配置文件添加performance-schema-instrument规则。例如,要启用所有内存工具,请将此规则添加到您的配置文件中并重新启动服务器:

    performance-schema-instrument='memory/%=COUNTED'
    

    注意

    在启动时启用内存工具可确保计算启动时发生的内存分配。

    重新启动服务器后,性能模式setup_instruments表的ENABLED列应报告您启用的内存工具为YES。性能模式setup_instruments表中的TIMED列对于内存工具是被忽略的,因为内存操作不计时。

    mysql> SELECT * FROM performance_schema.setup_instruments
           WHERE NAME LIKE '%memory/innodb%';
    +-------------------------------------------+---------+-------+
    | NAME                                      | ENABLED | TIMED |
    +-------------------------------------------+---------+-------+
    | memory/innodb/adaptive hash index         | NO      | NO    |
    | memory/innodb/buf_buf_pool                | NO      | NO    |
    | memory/innodb/dict_stats_bg_recalc_pool_t | NO      | NO    |
    | memory/innodb/dict_stats_index_map_t      | NO      | NO    |
    | memory/innodb/dict_stats_n_diff_on_level  | NO      | NO    |
    | memory/innodb/other                       | NO      | NO    |
    | memory/innodb/row_log_buf                 | NO      | NO    |
    | memory/innodb/row_merge_sort              | NO      | NO    |
    | memory/innodb/std                         | NO      | NO    |
    | memory/innodb/trx_sys_t::rw_trx_ids       | NO      | NO    |
    ...
    
  3. 查询内存工具数据。在此示例中,内存工具数据在性能模式memory_summary_global_by_event_name表中查询,该表按EVENT_NAME汇总数据。EVENT_NAME是工具的名称。

    以下查询返回InnoDB缓冲池的内存数据。有关列描述,请参阅第 29.12.20.10 节,“内存摘要表”。

    mysql> SELECT * FROM performance_schema.memory_summary_global_by_event_name
           WHERE EVENT_NAME LIKE 'memory/innodb/buf_buf_pool'\G
                      EVENT_NAME: memory/innodb/buf_buf_pool
                     COUNT_ALLOC: 1
                      COUNT_FREE: 0
       SUM_NUMBER_OF_BYTES_ALLOC: 137428992
        SUM_NUMBER_OF_BYTES_FREE: 0
                  LOW_COUNT_USED: 0
              CURRENT_COUNT_USED: 1
                 HIGH_COUNT_USED: 1
        LOW_NUMBER_OF_BYTES_USED: 0
    CURRENT_NUMBER_OF_BYTES_USED: 137428992
       HIGH_NUMBER_OF_BYTES_USED: 137428992
    

    可以使用 sys schema 的 memory_global_by_current_bytes 表查询相同的基础数据,该表显示了服务器全局范围内当前内存使用情况,按分配类型进行了拆分。

    mysql> SELECT * FROM sys.memory_global_by_current_bytes
           WHERE event_name LIKE 'memory/innodb/buf_buf_pool'\G
    *************************** 1\. row ***************************
           event_name: memory/innodb/buf_buf_pool
        current_count: 1
        current_alloc: 131.06 MiB
    current_avg_alloc: 131.06 MiB
           high_count: 1
           high_alloc: 131.06 MiB
       high_avg_alloc: 131.06 MiB
    

    sys schema 查询通过代码区域对当前分配的内存(current_alloc)进行聚合:

    mysql> SELECT SUBSTRING_INDEX(event_name,'/',2) AS
           code_area, FORMAT_BYTES(SUM(current_alloc))
           AS current_alloc
           FROM sys.x$memory_global_by_current_bytes
           GROUP BY SUBSTRING_INDEX(event_name,'/',2)
           ORDER BY SUM(current_alloc) DESC;
    +---------------------------+---------------+
    | code_area                 | current_alloc |
    +---------------------------+---------------+
    | memory/innodb             | 843.24 MiB    |
    | memory/performance_schema | 81.29 MiB     |
    | memory/mysys              | 8.20 MiB      |
    | memory/sql                | 2.47 MiB      |
    | memory/memory             | 174.01 KiB    |
    | memory/myisam             | 46.53 KiB     |
    | memory/blackhole          | 512 bytes     |
    | memory/federated          | 512 bytes     |
    | memory/csv                | 512 bytes     |
    | memory/vio                | 496 bytes     |
    +---------------------------+---------------+
    

    注意

    在 MySQL 8.0.16 之前,使用 sys.format_bytes() 来进行 FORMAT_BYTES()

    有关 sys schema 的更多信息,请参阅 第三十章,MySQL sys Schema

原文:dev.mysql.com/doc/refman/8.0/en/large-page-support.html

10.12.3.3 启用大页支持

一些硬件和操作系统架构支持大于默认值(通常为 4KB)的内存页。此支持的实际实现取决于底层硬件和操作系统。执行大量内存访问的应用程序可能通过使用大页获得性能改进,因为减少了 TLB(Translation Lookaside Buffer)缺失。

在 MySQL 中,大页可以被InnoDB使用,为其缓冲池和额外内存池分配内存。

MySQL 中标准使用大页尝试使用支持的最大大小,最高可达 4MB。在 Solaris 下,“超大页”功能使得可以使用高达 256MB 的页面。这个功能适用于最近的 SPARC 平台。可以通过使用--super-large-pages--skip-super-large-pages选项来启用或禁用它。

MySQL 还支持 Linux 中的大页支持实现(在 Linux 中称为 HugeTLB)。

在 Linux 上使用大页之前,必须启用内核以支持它们,并且需要配置 HugeTLB 内存池。有关参考,HugeTBL API 在您的 Linux 源代码的Documentation/vm/hugetlbpage.txt文件中有文档。

一些最近的系统(如 Red Hat Enterprise Linux)的内核可能默认启用了大页功能。要检查您的内核是否为真,请使用以下命令,并查找包含“huge”的输出行:

$> grep -i huge /proc/meminfo
AnonHugePages:   2658304 kB
ShmemHugePages:        0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB

非空的命令输出表示大页支持存在,但零值表示未配置任何页面供使用。

如果您的内核需要重新配置以支持大页,请参考hugetlbpage.txt文件中的说明。

假设您的 Linux 内核已启用大页支持,请按以下步骤配置 MySQL 以使用它:

  1. 确定所需的大页数。这是 InnoDB 缓冲池大小除以大页大小,我们可以计算为innodb_buffer_pool_size / Hugepagesize。假设innodb_buffer_pool_size的默认值(128MB)并使用从/proc/meminfo中获得的Hugepagesize值(2MB),这是 128MB / 2MB,或 64 个大页。我们称这个值为P

  2. 作为系统根用户,在文本编辑器中打开文件/etc/sysctl.conf,并添加此处显示的行,其中P是先前步骤中获得的大页数:

    vm.nr_hugepages=*P*
    

    使用先前获得的实际值,附加行应如下所示:

    vm.nr_huge_pages=64
    

    保存更新后的文件。

  3. 作为系统根用户,运行以下命令:

    $> sudo sysctl -p
    

    注意

    在某些系统上,大页文件的名称可能略有不同;例如,某些发行版将其称为nr_hugepages。如果sysctl返回与文件名相关的错误,请检查/proc/sys/vm中相应文件的名称,并使用该名称。

    要验证大页配置,请再次检查/proc/meminfo,如前所述。现在,您应该在输出中看到一些额外的非零值,类似于这样:

    $> grep -i huge /proc/meminfo
    AnonHugePages:   2686976 kB
    ShmemHugePages:        0 kB
    HugePages_Total:     233
    HugePages_Free:      233
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    Hugetlb:          477184 kB
    
  4. 可选地,您可能希望压缩 Linux VM。您可以使用一系列命令来执行此操作,可能是在脚本文件中,类似于以下所示:

    sync
    sync
    sync
    echo 3 > /proc/sys/vm/drop_caches
    echo 1 > /proc/sys/vm/compact_memory
    

    查看您的操作平台文档以获取有关如何执行此操作的更多信息。

  5. 检查服务器使用的任何配置文件,如my.cnf,并确保innodb_buffer_pool_chunk_size设置为大于大页大小。此变量的默认值为 128M。

  6. MySQL 服务器中默认情况下禁用大页支持。要启用它,请使用--large-pages启动服务器。您还可以通过将以下行添加到服务器my.cnf文件的[mysqld]部分来执行此操作:

    large-pages=ON
    

    启用此选项后,InnoDB会自动为其缓冲池和额外内存池使用大页。如果InnoDB无法执行此操作,则会回退到使用传统内存,并在错误日志中写入警告:警告:使用传统内存池。

您可以通过在重新启动mysqld后再次检查/proc/meminfo来验证 MySQL 是否正在使用大页,就像这样:

$> grep -i huge /proc/meminfo
AnonHugePages:   2516992 kB
ShmemHugePages:        0 kB
HugePages_Total:     233
HugePages_Free:      222
HugePages_Rsvd:       55
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:          477184 kB

10.13 性能测量(基准测试)

原文:dev.mysql.com/doc/refman/8.0/en/optimize-benchmarking.html

10.13.1 测量表达式和函数的速度

10.13.2 使用自定义基准

10.13.3 使用 performance_schema 测量性能

为了衡量性能,请考虑以下因素:

  • 无论您是在安静系统上测量单个操作的速度,还是在一段时间内测试一组操作(“工作负载”)的工作方式。通过简单测试,通常测试改变一个方面(配置设置、表上的索引集合、查询中的 SQL 子句)如何影响性能。基准测试通常是长时间运行和复杂的性能测试,结果可能决定高级选择,如硬件和存储配置,或者何时升级到新的 MySQL 版本。

  • 对于基准测试,有时必须模拟繁重的数据库工作负载以获得准确的图片。

  • 性能可能会因为许多不同因素而变化,几个百分点的差异可能不是决定性的胜利。当在不同环境中进行测试时,结果可能会相反。

  • 某些 MySQL 功能根据工作负载是否有助于性能。为了全面性,始终测试开启和关闭这些功能的性能。对于每种工作负载尝试的最重要功能是InnoDB表的自适应哈希索引。

本节从单个开发人员可以执行的简单直接的测量技术开始,逐渐发展到需要额外专业知识来执行和解释结果的更复杂的技术。

10.13.1 测量表达式和函数的速度

原文:dev.mysql.com/doc/refman/8.0/en/select-benchmarking.html

要测量特定 MySQL 表达式或函数的速度,请使用 mysql 客户端程序调用 BENCHMARK() 函数。其语法为 BENCHMARK(*loop_count*,*expr*)。返回值始终为零,但 mysql 会打印一行显示语句执行大约花费的时间。例如:

mysql> SELECT BENCHMARK(1000000,1+1);
+------------------------+
| BENCHMARK(1000000,1+1) |
+------------------------+
|                      0 |
+------------------------+
1 row in set (0.32 sec)

这个结果是在一台 Pentium II 400MHz 系统上获得的。它显示 MySQL 在该系统上可以在 0.32 秒内执行 1,000,000 个简单的加法表达式。

内置的 MySQL 函数通常经过高度优化,但也可能有一些例外。BENCHMARK() 是一个很好的工具,可以找出某个函数是否影响了你的查询。

10.13.2 使用您自己的基准测试

原文:dev.mysql.com/doc/refman/8.0/en/custom-benchmarks.html

对应用程序和数据库进行基准测试,找出瓶颈所在。在解决一个瓶颈(或用“虚拟”模块替换它)之后,您可以继续识别下一个瓶颈。即使您当前的应用程序整体性能是可接受的,您也应该至少为每个瓶颈制定计划,并决定如何解决它,以防有一天您真的需要额外的性能。

一个免费的基准测试套件是开源数据库基准测试,可在osdb.sourceforge.net/找到。

当系统负载非常重时,问题只会发生是非常常见的。我们曾经有许多客户在生产中遇到负载问题时联系我们。在大多数情况下,性能问题往往是由于基本数据库设计问题(例如,在高负载下表扫描效果不佳)或操作系统或库的问题。如果系统尚未投入生产,这些问题大多数情况下会更容易解决。

为避免出现此类问题,请在最坏的负载情况下对整个应用程序进行基准测试:

  • mysqlslap程序对于模拟多个客户端同时发出查询产生的高负载非常有帮助。请参阅 Section 6.5.8, “mysqlslap — A Load Emulation Client”。

  • 您还可以尝试诸如 SysBench 和 DBT2 等基准测试软件包,可在launchpad.net/sysbenchosdldbt.sourceforge.net/#dbt2找到。

这些程序或软件包可能会使系统崩溃,因此请确保仅在开发系统上使用它们。

10.13.3 使用 performance_schema 测量性能

原文:dev.mysql.com/doc/refman/8.0/en/monitoring-performance-schema.html

你可以查询performance_schema数据库中的表,查看关于服务器性能特征和正在运行的应用程序的实时信息。详细信息请参见第二十九章,MySQL 性能模式

10.14 检查服务器线程(进程)信息

原文:dev.mysql.com/doc/refman/8.0/en/thread-information.html

10.14.1 访问进程列表

10.14.2 线程命令值

10.14.3 通用线程状态

10.14.4 复制源线程状态

10.14.5 复制 I/O(接收器)线程状态

10.14.6 复制 SQL 线程状态

10.14.7 复制连接线程状态

10.14.8 NDB 集群线程状态

10.14.9 事件调度器线程状态

要了解你的 MySQL 服务器正在做什么,检查进程列表可能会有所帮助,该列表显示了服务器内执行的一组线程当前正在执行的操作。例如:

mysql> SHOW PROCESSLIST\G
*************************** 1\. row ***************************
     Id: 5
   User: event_scheduler
   Host: localhost
     db: NULL
Command: Daemon
   Time: 2756681
  State: Waiting on empty queue
   Info: NULL
*************************** 2\. row ***************************
     Id: 20
   User: me
   Host: localhost:52943
     db: test
Command: Query
   Time: 0
  State: starting
   Info: SHOW PROCESSLIST

可以使用 KILL 语句终止线程。参见 第 15.7.8.4 节,“KILL 语句”。

10.14.1 访问进程列表

原文:dev.mysql.com/doc/refman/8.0/en/processlist-access.html

以下讨论列举了进程信息的来源,查看进程信息所需的权限,并描述了进程列表条目的内容。

  • 进程信息来源

  • 访问进程列表所需的权限

  • 进程列表条目内容

进程信息来源

进程信息可从以下来源获取:

  • SHOW PROCESSLIST 语句:Section 15.7.7.29, “SHOW PROCESSLIST 语句”

  • mysqladmin processlist 命令:Section 6.5.2, “mysqladmin — MySQL 服务器管理程序”

  • INFORMATION_SCHEMA PROCESSLIST 表:Section 28.3.23, “INFORMATION_SCHEMA PROCESSLIST 表”

  • Performance Schema processlist 表:Section 29.12.21.7, “processlist 表”

  • Performance Schema threads 表列名以 PROCESSLIST_ 为前缀:Section 29.12.21.8, “threads 表”

  • sys schema processlistsession 视图:Section 30.4.3.22, “processlist 和 x\(processlist 视图”, 和 Section 30.4.3.33, “session 和 x\)session 视图”

threads 表与 SHOW PROCESSLIST, INFORMATION_SCHEMA PROCESSLIST, 和 mysqladmin processlist 相比如下:

  • 访问threads表不需要互斥锁,并且对服务器性能影响很小。其他来源会产生负面性能影响,因为它们需要互斥锁。

    注意

    截至 MySQL 8.0.22 版本,基于性能模式processlist表的另一种SHOW PROCESSLIST实现可用,类似于threads表,不需要互斥锁,并具有更好的性能特性。详情请参见第 29.12.21.7 节,“The processlist Table”。

  • threads表显示后台线程,其他来源不显示。它还为每个线程提供其他来源不提供的附加信息,例如线程是前台线程还是后台线程,以及与线程关联的服务器内部位置。这意味着threads表可用于监控其他来源无法监控的线程活动。

  • 您可以启用或禁用性能模式线程监控,如第 29.12.21.8 节,“The threads Table”所述。

出于这些原因,使用其他线程信息源之一进行服务器监控的数据库管理员可能希望改为使用threads表进行监控。

sys模式processlist视图以更易访问的格式呈现性能模式threads表中的信息。sys模式session视图呈现有关用户会话的信息,类似于sys模式processlist视图,但过滤掉后台进程。

访问进程列表所需的权限

对于大多数进程信息来源,如果具有PROCESS权限,则可以查看所有线程,即使属于其他用户。否则(没有PROCESS权限),非匿名用户可以访问有关自己线程的信息,但不能访问其他用户的线程,匿名用户无法访问线程信息。

性能模式threads表还提供线程信息,但表访问使用不同的权限模型。请参阅第 29.12.21.8 节,“threads 表”。

进程列表条目内容

每个进程列表条目包含几个信息片段。以下列表使用SHOW PROCESSLIST输出中的标签描述它们。其他进程信息源使用类似的标签。

  • Id是与线程关联的客户端的连接标识符。

  • UserHost表示与线程关联的帐户。

  • db是线程的默认数据库,如果没有选择任何数据库,则为NULL

  • CommandState表示线程正在执行的操作。

    大多数状态对应非常快速的操作。如果线程在给定状态下停留了很多秒钟,可能存在需要调查的问题。

    以下各节列出了可能的Command值,以及按类别分组的State值。对于其中一些值,其含义是不言自明的。对于其他值,提供了额外的描述。

    注意

    检查进程列表信息的应用程序应该注意,命令和状态可能会发生变化。

  • Time表示线程在当前状态下已经存在的时间。在某些情况下,线程的当前时间概念可能会发生变化:线程可以使用SET TIMESTAMP = *value*更改时间。对于复制 SQL 线程,该值是最后一个复制事件的时间戳与复制主机的实际时间之间的秒数。请参阅第 19.2.3 节,“复制线程”。

  • Info表示线程正在执行的语句,如果没有执行任何语句,则为NULL。对于SHOW PROCESSLIST,此值仅包含语句的前 100 个字符。要查看完整的语句,请使用SHOW FULL PROCESSLIST(或查询不同的进程信息源)。

10.14.2 线程命令值

原文:dev.mysql.com/doc/refman/8.0/en/thread-commands.html

线程可以具有以下任一命令值:

  • 二进制日志转储

    这是一个在复制源上的线程,用于向复制发送二进制日志内容。

  • 更改用户

    线程正在执行更改用户操作。

  • 关闭语句

    线程正在关闭准备语句。

  • 连接

    由连接到源的复制接收线程和复制工作线程使用。

  • 连接输出

    复制正在连接到其源。

  • 创建数据库

    线程正在执行创建数据库操作。

  • 守护进程

    此线程是服务器内部的,不是为客户端连接提供服务的线程。

  • 调试

    线程正在生成调试信息。

  • 延迟插入

    线程是延迟插入处理程序。

  • 删除数据库

    线程正在执行删除数据库操作。

  • 错误

  • 执行

    线程正在执行准备语句。

  • 获取

    线程正在获取执行准备语句的结果。

  • 字段列表

    线程正在检索表列信息。

  • 初始化数据库

    线程正在选择默认数据库。

  • 终止

    线程正在终止另一个线程。

  • 长数据

    线程在执行准备语句后检索长数据结果。

  • ping

    线程正在处理服务器 ping 请求。

  • 准备

    线程正在准备一个准备语句。

  • 进程列表

    线程正在生成有关服务器线程的信息。

  • 查询

    在单线程复制应用程序线程执行查询时为用户客户端使用,以及由复制协调器线程使用。

  • 退出

    线程正在终止。

  • 刷新

    线程正在刷新表、日志或缓存,或重置状态变量或复制服务器信息。

  • 注册从属

    线程正在注册一个复制服务器。

  • 重置语句

    线程正在重置准备语句。

  • 设置选项

    线程正在设置或重置客户端语句执行选项。

  • 关闭

    线程正在关闭服务器。

  • 休眠

    线程正在等待客户端发送新语句。

  • 统计

    线程正在生成服务器状态信息。

  • 时间

    未使用。

10.14.3 常规线程状态

原文:dev.mysql.com/doc/refman/8.0/en/general-thread-states.html

以下列表描述了与一般查询处理相关的线程State值,而���是更专业的活动,如复制。其中许多仅用于在服务器中查找错误。

  • 创建后

    当线程创建表(包括内部临时表)时,会发生这种情况,在创建表的函数结束时。即使由于某些错误而无法创建表,也会使用此状态。

  • 修改表

    服务器正在执行原地ALTER TABLE的过程中。

  • 分析中

    线程正在计算MyISAM表的键分布(例如,对于ANALYZE TABLE)。

  • 检查权限

    线程正在检查服务器是否具有执行该语句所需的权限。

  • 检查表

    线程正在执行表检查操作。

  • 清理中

    线程已处理完一个命令,并准备释放内存并重置某些状态变量。

  • 关闭表

    线程正在将更改的表数据刷新到磁盘并关闭已使用的表。这应该是一个快速操作。如果不是,请验证您的磁盘没有满,并且磁盘没有被大量使用。

  • 提交更改表到存储引擎

    服务器已完成一个原地ALTER TABLE并正在提交结果。

  • 将 HEAP 转换为磁盘

    线程正在将内部临时表从MEMORY表转换为磁盘上的表。

  • 复制到临时表

    线程正在处理一个ALTER TABLE语句。此状态发生在具有新结构的表已创建但在将行复制到其中之前。

    对于处于此状态的线程,性能模式可用于获取有关复制操作进度的信息。请参阅第 29.12.5 节,“性能模式阶段事件表”。

  • 复制到组表

    如果语句具有不同的ORDER BYGROUP BY标准,则按组排序行并复制到临时表。

  • 复制到临时表

    服务器正在将数据复制到内存中的临时表。

  • 复制到磁盘上的临时表

    服务器正在将数据复制到磁盘上的临时表。临时结果集变得太大(请参阅第 10.4.4 节,“MySQL 中的内部临时表使用”)。因此,线程正在将临时表从内存转换为基于磁盘的格式以节省内存。

  • 创建索引

    线程正在为MyISAM表处理ALTER TABLE ... ENABLE KEYS

  • 创建排序索引

    线程正在处理使用内部临时表解析的SELECT

  • 创建表

    线程正在创建表。这包括创建临时表。

  • 创建临时表

    线程正在内存或磁盘上创建临时表。如果表是在内存中创建的,但后来转换为磁盘表,那么在该操作期间的状态是复制到磁盘上的临时表

  • 从主表中删除

    服务器正在执行多表删除的第一部分。仅从第一个表中删除,并保存列和偏移量以用于从其他(参考)表中删除。

  • 从参考表中删除

    服务器正在执行多表删除的第二部分,并从其他表中删除匹配的行。

  • discard_or_import_tablespace

    线程正在处理ALTER TABLE ... DISCARD TABLESPACEALTER TABLE ... IMPORT TABLESPACE语句。

  • end

    在执行ALTER TABLECREATE VIEWDELETEINSERTSELECTUPDATE语句的清理之前发生。

    对于end状态,可能正在进行以下操作:

    • 将事件写入二进制日志

    • 释放内存缓冲区,包括 blob

  • 执行中

    线程已开始执行语句。

  • 执行 init_command

    线程正在执行init_command系统变量值中的语句。

  • 释放项目

    线程已执行命令。通常在此状态之后是清理

  • FULLTEXT 初始化

    服务器正在准备执行自然语言全文搜索。

  • 初始化

    在执行ALTER TABLEDELETEINSERTSELECTUPDATE语句的初始化之前发生。服务器在此状态下采取的操作包括刷新二进制日志和InnoDB日志。

  • 已终止

    有人向线程发送了KILL语句,并且线程应该在下次检查终止标志时中止。在 MySQL 的每个主要循环中检查该标志,但在某些情况下,线程可能仍需要一段时间才能终止。如果线程被其他线程锁定,那么终止将在其他线程释放锁定时立即生效。

  • 锁定系统表

    线程正在尝试锁定系统表(例如,时区或日志表)。

  • 记录慢查询

    线程正在将语句写入慢查询日志。

  • 登录

    连接线程的初始状态,直到客户端成功验证。

  • 管理键

    服务器正在启用或禁用表索引。

  • 打开系统表

    线程正在尝试打开一个系统表(例如,时区或日志表)。

  • 打开表

    线程正在尝试打开一个表。这应该是一个非常快速的过程,除非有什么阻止打开。例如,一个ALTER TABLELOCK TABLE语句可能会阻止打开一个表,直到语句执行完毕。还值得检查您的table_open_cache��是否足够大。

    对于系统表,使用打开系统表状态。

  • 优化

    服务器正在为查询执行初始优化。

  • 准备中

    这种状态发生在查询优化期间。

  • 准备修改表

    服务器正在准备执行一个原地ALTER TABLE

  • 清除旧的中继日志

    线程正在删除不需要的中继日志文件。

  • 查询结束

    这种状态发生在处理查询之后但在释放项目状态之前。

  • 从客户端接收

    服务器正在从客户端读取一个数据包。

  • 移除重复项

    查询使用SELECT DISTINCT的方式使得 MySQL 无法在早期阶段优化去除重复操作。因此,MySQL 需要额外的阶段在将结果发送给客户端之前去除所有重复行。

  • 移除临时表

    线程在处理SELECT语句后正在移除内部临时表。如果没有创建临时表,则不使用此状态。

  • 重命名

    线程正在重命名一个表。

  • 重命名结果表

    线程正在处理一个ALTER TABLE语句,已创建新表,并正在将其重命名以替换原始表。

  • 重新打开表

    线程为表获取了锁,但在获取锁后注意到底层表结构发生了变化。它已释放锁,关闭表,并尝试重新打开它。

  • 排序修复

    修复代码正在使用排序来创建索引。

  • 修复完成

    线程已完成对MyISAM表的多线程修复。

  • 使用键缓存进行修复

    修复代码正在通过键缓存逐个创建键。这比排序修复要慢得多。

  • 回滚

    线程正在回滚一个事务。

  • 保存状态

    对于MyISAM表操作,如修复或分析,线程正在将新表状态保存到.MYI文件头。状态包括诸如行数、AUTO_INCREMENT计数器和键分布等信息。

  • 搜索要更新的行

    线程在更新之前执行第一阶段以查找所有匹配的行。如果UPDATE正在更改用于查找相关行的索引,则必须执行此操作。

  • 发送数据

    MySQL 8.0.17 之前:线程正在读取和处理SELECT语句的行,并将数据发送给客户端。因为在此状态下发生的操作往往执行大量的磁盘访问(读取),所以通常是给定查询的生命周期中运行时间最长的状态。MySQL 8.0.17 及更高版本:此状态不再单独指示,而是包含在执行状态中。

  • 发送给客户端

    服务器正在向客户端写入数据包。

  • 设置

    线程正在开始一个ALTER TABLE操作。

  • 为分组排序

    线程正在进行排序以满足GROUP BY

  • 为订单排序

    线程正在进行排序以满足ORDER BY

  • 排序索引

    线程正在为更高效地访问MyISAM表优化操作中的索引页面进行排序。

  • 排序结果

    对于SELECT语句,这类似于创建排序索引,但适用于非临时表。

  • 开始

    语句执行开始时的第一阶段。

  • 统计

    服务器正在计算统计信息以制定查询执行计划。如果线程在此状态下长时间停留,服务器可能正在执行其他工作而受到磁盘限制。

  • 系统锁

    线程已调用mysql_lock_tables(),并且线程状态尚未更新。这是一个非常普遍的状态,可能出现的原因很多。

    例如,线程将请求或正在等待表格的内部或外部系统锁。这可能发生在InnoDB在执行LOCK TABLES期间等待表级锁时。如果此状态是由于对外部锁的请求而引起的,并且您没有使用访问相同MyISAM表的多个mysqld服务器,您可以使用--skip-external-locking选项禁用外部系统锁。但是,默认情况下已禁用外部锁定,因此这个选项可能没有效果。对于SHOW PROFILE,此状态表示线程正在请求锁(而不是等待锁)。

    对于系统表,使用锁定系统表状态。

  • 更新

    线程正在准备开始更新表格。

  • 更新中

    线程正在搜索要更新的行并更新它们。

  • 更新主表

    服务器正在执行多表更新的第一部分。它只更新第一个表,并保存列和偏移量以用于更新其他(参考)表。

  • 更新参考表

    服务器正在执行多表更新的第二部分,并更新其他表中匹配的行。

  • 用户锁

    该线程将请求或正在等待使用GET_LOCK()函数请求的咨询锁。对于SHOW PROFILE,此状态表示线程正在请求锁(而不是等待锁)。

  • 用户休眠

    该线程已调用SLEEP()函数。

  • 等待提交锁

    FLUSH TABLES WITH READ LOCK正在等待提交锁。

  • 等待处理程序提交

    该线程正在等待事务提交,而不是查询处理的其他部分。

  • 等待表

    线程收到通知,表的基础结构已更改,需要重新打开表以获取新结构。但是,为了重新打开表,必须等到所有其他线程关闭了相关表。

    如果另一个线程在相关表上使用了FLUSH TABLES或以下语句之一:FLUSH TABLES *tbl_name*ALTER TABLERENAME TABLEREPAIR TABLEANALYZE TABLEOPTIMIZE TABLE,则会发生此通知。

  • 等待表刷新

    该线程正在执行FLUSH TABLES并正在等待所有线程关闭其表,或者该线程收到通知,表的基础结构已更改,需要重新打开表以获取新结构。但是,为了重新打开表,必须等到所有其他线程关闭了相关表。

    如果另一个线程在相关表上使用了FLUSH TABLES或以下语句之一:FLUSH TABLES *tbl_name*ALTER TABLERENAME TABLEREPAIR TABLEANALYZE TABLEOPTIMIZE TABLE,则会发生此通知。

  • 等待 *lock_type* 锁

    服务器正在等待获取THR_LOCK锁或来自元数据锁定子系统的锁,其中lock_type表示锁的类型。

    此状态表示等待THR_LOCK

    • 等待表级锁

    这些状态表示正在等待元数据锁:

    • 等待事件元数据锁

    • 等待全局读锁

    • 等待模式元数据锁

    • 等待存储函数元数据锁

    • 等待存储过程元数据锁

    • 等待表元数据锁

    • 等待触发器元数据锁

    有关表锁指示器的信息,请参见第 10.11.1 节,“内部锁定方法”。有关元数据锁定的信息,请参见第 10.11.4 节,“元数据锁定”。要查看哪些锁正在阻止锁请求,请使用第 29.12.13 节,“性能模式锁定表”中描述的性能模式锁定表。

  • 等待条件

    一种通用状态,线程正在等待条件变为真。没有可用的具体状态信息。

  • 写入网络

    服务器正在向网络写入数据包。

10.14.4 复制源线程状态

原文:dev.mysql.com/doc/refman/8.0/en/source-thread-states.html

以下列表显示了在复制源的Binlog Dump线程的State列中可能看到的最常见状态。如果在源上看不到Binlog Dump线程,则意味着复制未运行;也就是说,当前没有副本连接。

在 MySQL 8.0.26 中,对仪表命名进行了不兼容的更改,包括线程阶段的名称,包含术语“master”,已更改为“source”,“slave”,已更改为“replica”,以及“mts”(用于“多线程从属”),已更改为“mta”(用于“多线程应用程序”)。使用这些仪表命名的监控工具可能会受到影响。如果不兼容的更改对您产生影响,请将terminology_use_previous系统变量设置为BEFORE_8_0_26,以使 MySQL Server 使用前面列表中指定对象的旧版本名称。这样可以使依赖旧名称的监控工具继续工作,直到它们可以更新为使用新名称。

使用会话范围设置terminology_use_previous系统变量以支持个别功能,或者使用全局范围以成为所有新会话的默认值。当使用全局范围时,慢查询日志包含旧版本的名称。

  • 完成读取一个二进制日志;切换到下一个二进制日志

    线程已经完成了读取一个二进制日志文件,并正在打开下一个要发送到副本的文件。

  • 主已将所有二进制日志发送到从属;等待更多更新

    来自 MySQL 8.0.26:源已将所有二进制日志发送到副本;等待更多更新

    线程已经从二进制日志中读取了所有剩余的更新并将它们发送到副本。线程现在处于空闲状态,等待二进制日志中出现新事件,这些事件是源上发生的新更新的结果。

  • 发送二进制日志事件到从属

    来自 MySQL 8.0.26:发送二进制日志事件到副本

    二进制日志由事件组成,其中一个事件通常是一个更新加上一些其他信息。线程已经从二进制日志中读取了一个事件,现在正在将其发送到副本。

  • 等待完成终止

    作为线程停止时发生的一个非常简短的状态。

10.14.5 复制 I/O(接收器)线程状态

原文:dev.mysql.com/doc/refman/8.0/en/replica-io-thread-states.html

以下列表显示了在副本服务器上的复制 I/O(接收器)线程的State列中最常见的状态。此状态还显示在SHOW REPLICA STATUS(或在 MySQL 8.0.22 之前,SHOW REPLICA STATUS)中显示的Replica_IO_State列中,因此您可以通过使用该语句来了解正在发生的情况。

在 MySQL 8.0.26 中,对仪表命名进行了不兼容的更改,包括线程阶段的名称,包含术语“主”,更改为“源”,“从”,更改为“副本”,以及“mts”(用于“多线程从服务器”),更改为“mta”(用于“多线程应用程序”)。使用这些仪表命名的监控工具可能会受到影响。如果不兼容的更改对您产生影响,请将terminology_use_previous系统变量设置为BEFORE_8_0_26,以使 MySQL 服务器使用前面列表中指定对象的旧版本名称。这使依赖旧名称的监控工具可以继续工作,直到它们可以更新为使用新名称。

使用会话范围设置terminology_use_previous系统变量以支持个别功能,或者使用全局范围作为所有新会话的默认值。当使用全局范围时,慢查询日志包含旧版本的名称。

  • 检查主版本

    来自 MySQL 8.0.26:检查源版本

    在与源的连接建立后非常短暂地发生的状态。

  • 连接到主服务器

    来自 MySQL 8.0.26:连接到源

    线程正在尝试连接到源。

  • 将主事件排队到中继日志

    来自 MySQL 8.0.26:将源事件排队到中继日志

    线程已读取一个事件,并将其复制到中继日志,以便 SQL 线程可以处理它。

  • 在失败的 binlog 转储请求后重新连接

    线程正在尝试重新连接到源。

  • 在失败的主事件读取后重新连接

    来自 MySQL 8.0.26:在失败的源事件读取后重新连接

    线程正在尝试重新连接到源。当重新建立连接时,状态变为等待主服务器发送事件

  • 在主服务器上注册从服务器

    来自 MySQL 8.0.26:在源上注册副本

    在与源的连接建立后非常短暂地发生的状态。

  • 请求 binlog 转储

    在与源建立连接后短暂发生的状态。 线程向源发送请求,请求其二进制日志的内容,从请求的二进制日志文件名和位置开始。

  • 等待轮到提交

    当启用 replica_preserve_commit_orderslave_preserve_commit_order 时,副本线程等待旧的工作线程提交时发生的状态。

  • 等待主发送事件

    从 MySQL 8.0.26: 等待源发送事件

    线程已连接到源并等待二进制日志事件到达。 如果源处于空闲状态,这可能持续很长时间。 如果等待持续了 replica_net_timeoutslave_net_timeout 秒,将发生超时。 在那时,线程认为连接已中断并尝试重新连接。

  • 等待主更新

    从 MySQL 8.0.26: 等待源更新

    连接到主连接到源 之前的初始状态。

  • 等待副本互斥体退出

    从 MySQL 8.0.26: 等待副本互斥体退出

    线程停止时短暂发生的状态。

  • 等待从 SQL 线程释放足够的中继日志空间

    从 MySQL 8.0.26: 等待副本 SQL 线程释放足够的中继日志空间

    您正在使用非零的 relay_log_space_limit 值,并且中继日志已经增长到足够大,使其总大小超过此值。 I/O(接收器)线程正在等待,直到 SQL(应用程序)线程通过处理中继日志内容释放足够的空间,以便可以删除一些中继日志文件。

  • 在失败的二进制日志转储请求后等待重新连接

    如果二进制日志转储请求失败(由于断开连接),线程在睡眠时进入此状态,然后定期尝试重新连接。 可以使用 CHANGE REPLICATION SOURCE TO 语句(从 MySQL 8.0.23 开始)或 CHANGE MASTER TO 语句(在 MySQL 8.0.23 之前)指定重试间隔。

  • 在失败的主事件读取后等待重新连接

    从 MySQL 8.0.26: 在失败的源事件读取后等待重新连接

    在读取时发生错误(由于断开连接)。线程将根据CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(MySQL 8.0.23 之前)设置的秒数(默认为 60)睡眠,然后尝试重新连接。

10.14.6 复制 SQL 线程状态

原文:dev.mysql.com/doc/refman/8.0/en/replica-sql-thread-states.html

以下列表显示了在复制品服务器上的复制 SQL 线程的State列中可能看到的最常见状态。

在 MySQL 8.0.26 中,对仪表化名称进行了不兼容的更改,包括包含术语“master”的线程阶段名称,更改为“source”,“slave”更改为“replica”,以及“mts”(用于“多线程复制品”)更改为“mta”(用于“多线程应用程序”)。使用这些仪表化名称的监控工具可能会受到影响。如果不兼容的更改对您产生影响,请将terminology_use_previous系统变量设置为BEFORE_8_0_26,以使 MySQL Server 使用前面列表中指定对象的旧名称。这使依赖旧名称的监控工具可以继续工作,直到它们可以更���为使用新名称。

terminology_use_previous系统变量设置为会话范围以支持个别功能,或者设置为全局范围以成为所有新会话的默认值。当使用全局范围时,慢查询日志包含旧版本的名称。

  • Making temporary file (append) before replaying LOAD DATA INFILE

    线程正在执行一个LOAD DATA语句,并将数据附加到一个临时文件中,该文件包含复制品读取行的数据。

  • Making temporary file (create) before replaying LOAD DATA INFILE

    线程正在执行一个LOAD DATA语句,并创建一个包含复制品读取行的数据的临时文件。只有在原始LOAD DATA语句由运行版本低于 MySQL 5.0.3 的源记录时,才会遇到此状态。

  • Reading event from the relay log

    线程已从中继日志中读取事件,以便可以处理该事件。

  • Slave has read all relay log; waiting for more updates

    从 MySQL 8.0.26 开始:Replica has read all relay log; waiting for more updates

    线程已处理完中继日志文件中的所有事件,现在正在等待 I/O(接收器)线程将新事件写入中继日志。

  • Waiting for an event from Coordinator

    使用多线程复制品(replica_parallel_workersslave_parallel_workers大于 1),其中一个复制品工作线程正在等待来自协调器线程的事件。

  • Waiting for slave mutex on exit

    从 MySQL 8.0.26 开始:Waiting for replica mutex on exit

    作为线程停止时发生的非常简短的状态。

  • 等待从属工作者释放待处理事件

    从 MySQL 8.0.26 开始:等待复制工作者释放待处理事件

    当工作者处理的事件总大小超过replica_pending_jobs_size_maxslave_pending_jobs_size_max系统变量的大小时,会发生这种等待动作。当大小低于此限制时,协调器会恢复调度。只有当replica_parallel_workersslave_parallel_workers设置大于 0 时才会发生这种状态。

  • 等待在中继日志中的下一个事件

    从中继日志中读取事件之前的初始状态。

  • 等待直到主服务器执行事件后的 MASTER_DELAY 秒

    从 MySQL 8.0.26 开始:等待直到主服务器执行事件后的 SOURCE_DELAY 秒

    SQL 线程已经读取了一个事件,但正在等待复制延迟结束。这个延迟是通过CHANGE REPLICATION SOURCE TO语句(MySQL 8.0.23 之后)或CHANGE MASTER TO语句(MySQL 8.0.23 之前)中的SOURCE_DELAY | MASTER_DELAY选项设置的。

SQL 线程的Info列也可能显示一个语句的文本。这表示线程已经从中继日志中读取了一个事件,提取了其中的语句,并可能正在执行它。

10.14.7 复制连接线程状态

原文:dev.mysql.com/doc/refman/8.0/en/replica-connection-thread-states.html

这些线程状态发生在副本服务器上,但与连接线程相关,而不是与 I/O 或 SQL 线程相关。

在 MySQL 8.0.26 中,对仪表名称进行了不兼容的更改,包括线程阶段的名称,包含术语“master”,更改为“source”,“slave”,更改为“replica”,以及“mts”(用于“多线程从属”),更改为“mta”(用于“多线程应用程序”)。使用这些仪表名称的监控工具可能会受到影响。如果不兼容的更改对您产生影响,请将terminology_use_previous系统变量设置为BEFORE_8_0_26,以使 MySQL Server 使用上述列表中指定对象的旧版本名称。这样可以使依赖旧名称的监控工具继续工作,直到它们可以更新为使用新名称。

terminology_use_previous系统变量设置为会话范围以支持个别功能,或者设置为全局范围以成为所有新会话的默认值。当使用全局范围时,慢查询日志包含旧版本的名称。

  • Changing master

    从 MySQL 8.0.26 开始:Changing replication source

    线程正在处理CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(在 MySQL 8.0.23 之前)。

  • Killing slave

    线程正在处理STOP REPLICA语句。

  • Opening master dump table

    此状态发生在Creating table from master dump之后。

  • Reading master dump table data

    此状态发生在Opening master dump table之后。

  • Rebuilding the index on master dump table

    此状态发生在Reading master dump table data之后。

10.14.8 NDB 集群线程状态

原文:dev.mysql.com/doc/refman/8.0/en/mysql-cluster-thread-states.html

  • 将事件提交到 binlog

  • 打开 mysql.ndb_apply_status

  • 处理事件

    该线程正在处理用于二进制日志记录的事件。

  • 处理模式表中的事件

    该线程正在进行模式复制的工作。

  • 正在关闭

  • 同步 ndb 表模式操作和 binlog

    这用于对 NDB 的模式操作进行正确的二进制日志记录。

  • 等待允许获取 ndbcluster 全局模式锁

    该线程正在等待获取全局模式锁的许可。

  • 等待来自 ndbcluster 的事件

    服务器作为 NDB 集群中的 SQL 节点,连接到集群管理节点。

  • 等待来自 ndbcluster 的第一个事件

  • 等待 ndbcluster 二进制日志更新到当前位置

  • 等待 ndbcluster 全局模式锁

    该线程正在等待另一个线程持有的全局模式锁被释放。

  • 等待 ndbcluster 启动

  • 等待模式时代

    该线程正在等待模式时代(即全局检查点)。

10.14.9 事件调度器线程状态

原文:dev.mysql.com/doc/refman/8.0/en/event-scheduler-thread-states.html

这些状态发生在事件调度器线程、用于执行计划事件的线程,或终止调度器的线程。

  • 清除

    调度器线程或正在执行事件的线程正在终止并即将结束。

  • 已初始化

    调度器线程或执行事件的线程已被初始化。

  • 等待下一次激活

    调度器有一个非空的事件队列,但下一次激活在未来。

  • 等待调度器停止

    该线程发出了SET GLOBAL event_scheduler=OFF命令,并正在等待调度器停止。

  • 等待空队列

    调度器的事件队列为空,正在休眠。

第十一章 语言结构

原文:dev.mysql.com/doc/refman/8.0/en/language-structure.html

目录

11.1 文字面值

11.1.1 字符串文字面值

11.1.2 数字文字面值

11.1.3 日期和时间文字面值

11.1.4 十六进制文字面值

11.1.5 位值文字面值

11.1.6 布尔文字面值

11.1.7 NULL 值

11.2 模式对象名称

11.2.1 标识符长度限制

11.2.2 标识符限定符

11.2.3 标识符大小写敏感性

11.2.4 标识符到文件名的映射

11.2.5 函数名称解析和解析

11.3 关键字和保留字

11.4 用户定义变量

11.5 表达式

11.6 查询属性

11.7 注释

本章讨论在使用 MySQL 时编写以下 SQL 语句的规则:

  • 诸如字符串和数字之类的文字面值

  • 诸如数据库、表和列名称之类的标识符

  • 关键字和保留字

  • 用户定义和系统变量

  • 表达式

  • 查询属性

  • 注释

11.1 字面值

原文:dev.mysql.com/doc/refman/8.0/en/literals.html

11.1.1 字符串字面值

11.1.2 数字字面值

11.1.3 日期和时间字面值

11.1.4 十六进制字面值

11.1.5 位值字面值

11.1.6 布尔字面值

11.1.7 NULL 值

本节描述了如何在 MySQL 中编写字面值。这些包括字符串、数字、十六进制和位值、布尔值以及NULL。本节还涵盖了在处理这些基本类型时可能遇到的各种细微差别。

11.1.1 字符串文字

原文:dev.mysql.com/doc/refman/8.0/en/string-literals.html

字符串是一系列字节或字符,包含在单引号(')或双引号(")字符之间。例子:

'a string'
"another string"

相邻的引用字符串会被连接成一个单独的字符串。以下行是等效的:

'a string'
'a' ' ' 'string'

如果启用了ANSI_QUOTES SQL 模式,则字符串文字只能在单引号内引用,因为在双引号内引用的字符串会被解释为标识符。

二进制字符串是一串字节。每个二进制字符串都有一个名为binary的字符集和排序规则。非二进制字符串是一串字符。它具有不同于binary的字符集和与字符集兼容的排序规则。

对于这两种类型的字符串,比较是基于字符串单元的数值。对于二进制字符串,单元是字节;比较使用数值字节值。对于非二进制字符串,单元是字符,一些字符集支持多字节字符;比较使用数值字符编码值。字符编码排序是字符串排序规则的一个函数。(有关更多信息,请参见第 12.8.5 节,“二进制排序与 _bin 排序的比较”。)

注意

mysql客户端中,二进制字符串显示为十六进制表示,取决于--binary-as-hex的值。有关该选项的更多信息,请参见第 6.5.1 节,“mysql — MySQL 命令行客户端”。

字符串文字可能具有可选的字符集引导符和COLLATE子句,以指定它作为使用特定字符集和排序规则的字符串:

[_*charset_name*]'*string*' [COLLATE *collation_name*]

例子:

SELECT _latin1'*string*';
SELECT _binary'*string*';
SELECT _utf8mb4'*string*' COLLATE utf8mb4_danish_ci;

您可以使用N'*literal*'(或n'*literal*')来创建一个使用国家字符集的字符串。这些语句是等效的:

SELECT N'some text';
SELECT n'some text';
SELECT _utf8'some text';

有关这些字符串语法形式的信息,请参见第 12.3.7 节,“国家字符集”和第 12.3.8 节,“字符集引导符”。

在字符串中,除非启用了NO_BACKSLASH_ESCAPES SQL 模式,否则某些序列具有特殊含义。这些序列以反斜杠(\)开头,称为转义字符。MySQL 识别表 11.1,“特殊字符转义序列”中显示的转义序列。对于所有其他转义序列,反斜杠将被忽略。也就是说,转义字符被解释为未转义。例如,\x就是x。这些序列区分大小写。例如,\b被解释为退格,但\B被解释为B。转义处理根据character_set_connection系统变量指示的字符集进行。即使对于由指示不同字符集的引导符引导的字符串,也是如此,如第 12.3.6 节,“字符字符串文字的字符集和排序规则”中所讨论的。

表 11.1 特殊字符转义序列

转义序列 序列表示的字符
\0 ASCII NUL(X'00')字符
\' 单引号(')字符
\" 双引号(")字符
\b 退格字符
\n 换行(换行)字符
\r 回车字符
\t 制表符字符
\Z ASCII 26(Control+Z);请参阅表后的注释
\\ 反斜杠(\)字符
\% %字符;请参阅表后的注释
\_ 下划线字符;请参阅表后的注释
转义序列 序列表示的字符

ASCII 26 字符可以编码为\Z,以便您解决 Windows 上 ASCII 26 代表文件结尾的问题。如果文件中有 ASCII 26,则在尝试使用mysql *db_name* < *file_name*时会出现问题。

\%\_序列用于在模式匹配上下文中搜索%_的字面实例,否则它们将被解释为通配符字符。请参阅第 14.8.1 节,“字符串比较函数和运算符”中的LIKE运算符的描述。如果在模式匹配上下文之外使用\%\_,它们将被解释为字符串\%\_,而不是%_

有几种方法可以在字符串中包含引号字符:

  • 在用'引用的字符串中的'可以写为''

  • 在用"引用的字符串中的"可以写为""

  • 通过转义字符(\)在引号字符之前加上转义字符。

  • 在用"引用的字符串中的'不需要特殊处理,也不需要加倍或转义。同样,用'引用的字符串中的"也不需要特殊处理。

以下SELECT语句演示了引用和转义的工作方式:

mysql> SELECT 'hello', '"hello"', '""hello""', 'hel''lo', '\'hello';
+-------+---------+-----------+--------+--------+
| hello | "hello" | ""hello"" | hel'lo | 'hello |
+-------+---------+-----------+--------+--------+

mysql> SELECT "hello", "'hello'", "''hello''", "hel""lo", "\"hello";
+-------+---------+-----------+--------+--------+
| hello | 'hello' | ''hello'' | hel"lo | "hello |
+-------+---------+-----------+--------+--------+

mysql> SELECT 'This\nIs\nFour\nLines';
+--------------------+
| This
Is
Four
Lines |
+--------------------+

mysql> SELECT 'disappearing\ backslash';
+------------------------+
| disappearing backslash |
+------------------------+

要将二进制数据插入字符串列(如BLOB列),你应该用转义序列表示某些字符。反斜杠(\)和用于引用字符串的引号字符必须转义。在某些客户端环境中,可能还需要转义NUL或 Control+Z。mysql客户端如果未转义包含NUL字符的引用字符串,则会截断它们,如果未转义,则 Control+Z 可能被视为 Windows 上的 END-OF-FILE。有关表示每个字符的转义序列,请参见表 11.1,“特殊字符转义序列”。

在编写应用程序时,任何可能包含这些特殊字符的字符串在用作发送到 MySQL 服务器的 SQL 语句中的数据值之前必须正确转义。你可以通过两种方式实现这一点:

  • 使用一个函数处理字符串,转义特殊字符。在 C 程序中,你可以使用mysql_real_escape_string_quote() C API 函数来转义字符。参见 mysql_real_escape_string_quote()。在构造其他 SQL 语句的 SQL 语句中,你可以使用QUOTE()函数。Perl DBI 接口提供了一个quote方法,将特殊字符转换为适当的转义序列。参见第 31.9 节,“MySQL Perl API”。其他语言接口可能提供类似的功能。

  • 作为显式转义特殊字符的替代方案,许多 MySQL API 提供了一个占位符功能,使你能够在语句字符串中插入特殊标记,然后在发出语句时将数据值绑定到它们。在这种情况下,API 会为你处理值中的特殊字符的转义。

11.1.2 数值文字

原文:dev.mysql.com/doc/refman/8.0/en/number-literals.html

数字文字包括精确值(整数和 DECIMAL - DECIMAL, NUMERIC"))文字和近似值(浮点)文字。

整数表示为一系列数字。数字可以包括 . 作为小数分隔符。数字可以以 -+ 开头,表示负值或正值。用尾数和指数表示的数字是近似值数字。

精确值数值文字具有整数部分或小数部分,或两者兼有。它们可以带符号。例如:1.23.4-5-6.78+9.10

近似值数值文字用科学计数法表示,具有尾数和指数。尾数和/或指数部分可以带符号。例如:1.2E31.2E-3-1.2E3-1.2E-3

看起来相似的两个数字可能被视为不同。例如,2.34 是一个精确值(固定点)数字,而 2.34E0 是一个近似值(浮点)数字。

DECIMAL - DECIMAL, NUMERIC") 数据类型是一个固定点类型,计算是精确的。在 MySQL 中,DECIMAL - DECIMAL, NUMERIC") 类型有几个同义词:NUMERIC - DECIMAL, NUMERIC"),DEC - DECIMAL, NUMERIC"),FIXED - DECIMAL, NUMERIC")。整数类型也是精确值类型。有关精确值计算的更多信息,请参见 第 14.24 节,“精度数学”。

FLOAT - FLOAT, DOUBLE") 和 DOUBLE - FLOAT, DOUBLE") 数据类型是浮点类型,计算是近似的。在 MySQL 中,与 FLOAT - FLOAT, DOUBLE") 或 DOUBLE - FLOAT, DOUBLE") 同义的类型有 DOUBLE PRECISION - FLOAT, DOUBLE") 和 REAL - FLOAT, DOUBLE")。

整数可以在浮点上下文中使用;它被解释为等效的浮点数。

11.1.3 日期和时间文字

原文:dev.mysql.com/doc/refman/8.0/en/date-and-time-literals.html

  • 标准 SQL 和 ODBC 日期和时间文字

  • 日期和时间上下文中的字符串和数字文字

日期和时间值可以用几种格式表示,例如带引号的字符串或数字,具体取决于值的确切类型和其他因素。例如,在 MySQL 期望日期的上下文中,它会将 '2015-07-21''20150721'20150721 中的任何一个解释为日期。

本节描述了日期和时间文字的可接受格式。有关时间数据类型的更多信息,例如允许值的范围,请参阅 第 13.2 节,“日期和时间数据类型”。

标准 SQL 和 ODBC 日期和时间文字

标准 SQL 要求使用类型关键字和字符串指定时间文字。关键字和字符串之间的空格是可选的。

DATE '*str*'
TIME '*str*'
TIMESTAMP '*str*'

MySQL 识别但不像标准 SQL 那样需要类型关键字。要符合标准的应用程序应该为时间文字包含类型关键字。

MySQL 也识别与标准 SQL 语法对应的 ODBC 语法:

{ d '*str*' }
{ t '*str*' }
{ ts '*str*' }

MySQL 使用类型关键字和 ODBC 结构来生成 DATETIMEDATETIME 值,包括指定的尾部分数秒部分。TIMESTAMP 语法在 MySQL 中生成一个 DATETIME 值,因为 DATETIME 的范围更接近标准 SQL TIMESTAMP 类型,其年份范围从 00019999。(MySQL TIMESTAMP 的年份范围是 19702038。)

日期和时间上下文中的字符串和数字文字

MySQL 识别以这些格式表示的 DATE 值:

  • 作为字符串,格式为'*YYYY-MM-DD*''*YY-MM-DD*'。允许使用“宽松”语法,但已被弃用:任何标点字符都可以用作日期部分之间的分隔符。例如,'2012-12-31''2012/12/31''2012¹²³¹''2012@12@31'是等效的。从 MySQL 8.0.29 开始,使用除了破折号(-)作为分隔符之外的任何字符会引发警告,如下所示:

    mysql> SELECT DATE'2012@12@31';
    +------------------+
    | DATE'2012@12@31' |
    +------------------+
    | 2012-12-31       |
    +------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1\. row ***************************
      Level: Warning
       Code: 4095
    Message: Delimiter '@' in position 4 in datetime value '2012@12@31' at row 1 is
    deprecated. Prefer the standard '-'. 1 row in set (0.00 sec)
    
  • 作为没有分隔符的字符串,格式为'*YYYYMMDD*''*YYMMDD*',前提是字符串作为日期是有意义的。例如,'20070523''070523'被解释为'2007-05-23',但'071332'是非法的(具有无意义的月份和日期部分),变为'0000-00-00'

  • 作为数字,格式为YYYYMMDDYYMMDD,前提是数字作为日期是有意义的。例如,19830905830905被解释为'1983-09-05'

MySQL 在这些格式中识别DATETIMETIMESTAMP值:

  • 作为字符串,格式为'*YYYY-MM-DD hh:mm:ss*''*YY-MM-DD hh:mm:ss*'。MySQL 还允许在此处使用“宽松”语法,尽管这已被弃用:任何标点字符都可以用作日期部分或时间部分之间的分隔符。例如,'2012-12-31 11:30:45''2012¹²³¹ 11+30+45''2012/12/31 11*30*45''2012@12@31 11³⁰⁴⁵'是等效的。从 MySQL 8.0.29 开始,在这些值中使用除了破折号(-)用于日期部分和冒号(:)用于时间部分之外的任何字符作为分隔符会引发警告,如下所示:

    mysql> SELECT TIMESTAMP'2012¹²³¹ 11*30*45';
    +--------------------------------+
    | TIMESTAMP'2012¹²³¹ 11*30*45' |
    +--------------------------------+
    | 2012-12-31 11:30:45            |
    +--------------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1\. row ***************************
      Level: Warning
       Code: 4095
    Message: Delimiter '^' in position 4 in datetime value '2012¹²³¹ 11*30*45' at
    row 1 is deprecated. Prefer the standard '-'. 1 row in set (0.00 sec)
    

    日期和时间部分与小数秒部分之间唯一识别的分隔符是小数点。

    日期和时间部分可以用T而不是空格分隔。例如,'2012-12-31 11:30:45''2012-12-31T11:30:45'是等效的。

    以前,MySQL 支持日期和时间值中任意数量的前导和尾随空格字符,以及在DATETIMETIMESTAMP值的日期和时间部分之间的空格字符。在 MySQL 8.0.29 及更高版本中,此行为已被弃用,多余的空格字符会触发警告,如下所示:

    mysql> SELECT TIMESTAMP'2012-12-31   11-30-45';
    +----------------------------------+
    | TIMESTAMP'2012-12-31   11-30-45' |
    +----------------------------------+
    | 2012-12-31 11:30:45              |
    +----------------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1\. row ***************************
      Level: Warning
       Code: 4096
    Message: Delimiter ' ' in position 11 in datetime value '2012-12-31   11-30-45'
    at row 1 is superfluous and is deprecated. Please remove. 1 row in set (0.00 sec)
    

    从 MySQL 8.0.29 开始,当使用空格字符以外的空格字符时,会引发警告,如下所示:

    mysql> SELECT TIMESTAMP'2021-06-06
        '> 11:15:25';
    +--------------------------------+
    | TIMESTAMP'2021-06-06
     11:15:25'                       |
    +--------------------------------+
    | 2021-06-06 11:15:25            |
    +--------------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1\. row ***************************
      Level: Warning
       Code: 4095
    Message: Delimiter '\n' in position 10 in datetime value '2021-06-06
    11:15:25' at row 1 is deprecated. Prefer the standard ' '.
    1 row in set (0.00 sec)
    

    每个时间值只会引发一次此类警告,即使分隔符、空格或两者都存在多个问题,如下面的一系列语句所示:

    mysql> SELECT TIMESTAMP'2012!-12-31  11:30:45';
    +----------------------------------+
    | TIMESTAMP'2012!-12-31  11:30:45' |
    +----------------------------------+
    | 2012-12-31 11:30:45              |
    +----------------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1\. row ***************************
      Level: Warning
       Code: 4095
    Message: Delimiter '!' in position 4 in datetime value '2012!-12-31  11:30:45'
    at row 1 is deprecated. Prefer the standard '-'. 1 row in set (0.00 sec)
    
    mysql> SELECT TIMESTAMP'2012-12-31  11:30:45';
    +---------------------------------+
    | TIMESTAMP'2012-12-31  11:30:45' |
    +---------------------------------+
    | 2012-12-31 11:30:45             |
    +---------------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1\. row ***************************
      Level: Warning
       Code: 4096
    Message: Delimiter ' ' in position 11 in datetime value '2012-12-31  11:30:45'
    at row 1 is superfluous and is deprecated. Please remove. 1 row in set (0.00 sec)
    
    mysql> SELECT TIMESTAMP'2012-12-31 11:30:45';
    +--------------------------------+
    | TIMESTAMP'2012-12-31 11:30:45' |
    +--------------------------------+
    | 2012-12-31 11:30:45            |
    +--------------------------------+
    1 row in set (0.00 sec)
    
  • 作为没有分隔符的字符串,格式为'*YYYYMMDDhhmmss*''*YYMMDDhhmmss*',前提是字符串作为日期是有意义的。例如,'20070523091528''070523091528'被解释为'2007-05-23 09:15:28',但'071122129015'是非法的(具有无意义的分钟部分),变为'0000-00-00 00:00:00'

  • 作为YYYYMMDDhhmmssYYMMDDhhmmss格式的数字,前提是该数字作为日期是有意义的。例如,19830905132800830905132800被解释为'1983-09-05 13:28:00'

DATETIMETIMESTAMP值可以包括高达微秒(6 位数字)精度的尾部小数部分。小数部分应始终与其余时间部分用小数点分隔;不识别其他小数秒分隔符。有关 MySQL 中小数秒支持的信息,请参见第 13.2.6 节,“时间值中的小数秒”。

包含两位数年份值的日期是模棱两可的,因为世纪未知。MySQL 使用以下规则解释两位数年份值:

  • 年份范围在70-99之间的值变为1970-1999

  • 年份范围在00-69之间的值变为2000-2069

另请参阅第 13.2.9 节,“日期中的 2 位数年份”。

对于指定为包含日期部分分隔符的字符串值,无需为小于10的月份或日期值指定两位数。'2015-6-9''2015-06-09'相同。同样,对于指定为包含时间部分分隔符的字符串值,无需为小于10的小时、分钟或秒值指定两位数。'2015-10-30 1:2:3''2015-10-30 01:02:03'相同。

指定为数字的值应为 6、8、12 或 14 位数字长。如果数字为 8 或 14 位长,则假定为YYYYMMDDYYYYMMDDhhmmss格式,并且年份由前 4 位数字给出。如果数字为 6 或 12 位长,则假定为YYMMDDYYMMDDhhmmss格式,并且年份由前 2 位数字给出。长度不是这些之一的数字被解释为用前导零填充到最接近的长度。

指定为无分隔符字符串的值根据其长度进行解释。对于 8 或 14 个字符长的字符串,假定年份由前 4 个字符给出。否则,假定年份由前 2 个字符给出。字符串从左到右解释以找到年、月、日、小时、分钟和秒值,对于字符串中存在的部分。这意味着您不应使用少于 6 个字符的字符串。例如,如果您指定'9903',认为代表 1999 年 3 月,MySQL 会将其转换为“零”日期值。这是因为年份和月份值为9903,但日期部分完全缺失。但是,您可以明确指定零值表示缺失的月份或日期部分。例如,要插入值'1999-03-00',请使用'990300'

MySQL 识别这些格式的TIME值:

  • 作为'D hh:mm:ss'格式的字符串。您还可以使用以下“宽松”语法之一:'hh:mm:ss''hh:mm''D hh:mm''D hh''ss'。这里D代表天数,可以取值从 0 到 34。

  • 作为没有分隔符的字符串,采用'hhmmss'格式,只要它作为时间是有意义的。例如,'101112'被理解为'10:11:12',但'109712'是非法的(它有一个荒谬的分钟部分),变成了'00:00:00'

  • 作为hhmmss格式的数字,只要它作为时间是有意义的。例如,101112被理解为'10:11:12'。还可以理解以下替代格式:ssmmsshhmmss

'D hh:mm:ss.fraction''hh:mm:ss.fraction''hhmmss.fraction'hhmmss.fraction时间格式中识别尾随的小数秒部分,其中fraction是高达微秒(6 位数字)精度的小数部分。小数部分应始终用小数点与时间的其余部分分隔开;不识别其他小数秒分隔符。有关 MySQL 中小数秒支持的信息,请参见 Section 13.2.6, “Fractional Seconds in Time Values”。

对于作为包含时间部分分隔符的字符串指定的TIME值,不需要为小时、分钟或秒值少于10的两位数指定两位数。'8:3:2'等同于'08:03:02'

从 MySQL 8.0.19 开始,您可以在将TIMESTAMPDATETIME值插入表时指定时区偏移量。偏移量附加到日期时间文字的时间部分,没有空格,并使用用于设置time_zone系统变量的相同格式,以下是一些例外情况:

  • 对于小时值小于 10 的情况,需要前导零。

  • '-00:00'被拒绝。

  • 诸如'EET''Asia/Shanghai'之类的时区名称不能使用;在这种情况下也不能使用'SYSTEM'

插入的值不能在月份部分、日期部分或两部分中有零。从 MySQL 8.0.22 开始执行此规则,无论服务器 SQL 模式设置如何。

这个例子演示了使用不同的time_zone设置将带有时区偏移的日期时间值插入TIMESTAMPDATETIME列,然后检索它们:

mysql> CREATE TABLE ts (
 ->     id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
 ->     col TIMESTAMP NOT NULL
 -> ) AUTO_INCREMENT = 1;

mysql> CREATE TABLE dt (
 ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 ->     col DATETIME NOT NULL
 -> ) AUTO_INCREMENT = 1;

mysql> SET @@time_zone = 'SYSTEM';

mysql> INSERT INTO ts (col) VALUES ('2020-01-01 10:10:10'),
 ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = '+00:00';

mysql> INSERT INTO ts (col) VALUES ('2020-01-01 10:10:10'),
 ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = 'SYSTEM';

mysql> INSERT INTO dt (col) VALUES ('2020-01-01 10:10:10'),
 ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = '+00:00';

mysql> INSERT INTO dt (col) VALUES ('2020-01-01 10:10:10'),
 ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = 'SYSTEM';

mysql> SELECT @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| EST                |
+--------------------+

mysql> SELECT col, UNIX_TIMESTAMP(col) FROM dt ORDER BY id;
+---------------------+---------------------+
| col                 | UNIX_TIMESTAMP(col) |
+---------------------+---------------------+
| 2020-01-01 10:10:10 |          1577891410 |
| 2019-12-31 23:40:10 |          1577853610 |
| 2020-01-01 13:10:10 |          1577902210 |
| 2020-01-01 10:10:10 |          1577891410 |
| 2020-01-01 04:40:10 |          1577871610 |
| 2020-01-01 18:10:10 |          1577920210 |
+---------------------+---------------------+

mysql> SELECT col, UNIX_TIMESTAMP(col) FROM ts ORDER BY id;
+---------------------+---------------------+
| col                 | UNIX_TIMESTAMP(col) |
+---------------------+---------------------+
| 2020-01-01 10:10:10 |          1577891410 |
| 2019-12-31 23:40:10 |          1577853610 |
| 2020-01-01 13:10:10 |          1577902210 |
| 2020-01-01 05:10:10 |          1577873410 |
| 2019-12-31 23:40:10 |          1577853610 |
| 2020-01-01 13:10:10 |          1577902210 |
+---------------------+---------------------+

即使在插入时使用了偏移量,选择日期时间值时也不显示偏移量。

支持的偏移值范围是-13:59+14:00,包括边界值。

包含时区偏移的日期时间文字可以作为预处理语句的参数值接受。

11.1.4 十六进制文字

译文:dev.mysql.com/doc/refman/8.0/en/hexadecimal-literals.html

十六进制文字值使用 X'*val*'0x*val* 表示法编写,其中 val 包含十六进制数字(0..9A..F)。数字的大小写和任何前导 X 的大小写不重要。前导 0x 区分大小写,不能写为 0X

合法的十六进制文字:

X'01AF'
X'01af'
x'01AF'
x'01af'
0x01AF
0x01af

非法的十六进制文字:

X'0G'   (G is not a hexadecimal digit)
0X01AF  (0X must be written as 0x)

使用 X'*val*' 表示法编写的值必须包含偶数个数字,否则会出现语法错误。要纠正问题,请在值前面填充一个前导零:

mysql> SET @s = X'FFF';
ERROR 1064 (42000): You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server
version for the right syntax to use near 'X'FFF'' 
mysql> SET @s = X'0FFF';
Query OK, 0 rows affected (0.00 sec)

使用包含奇数个数字的 0x*val* 表示法编写的值被视为具有额外的前导 0。例如,0xaaa 被解释为 0x0aaa

默认情况下,十六进制文字是一个二进制字符串,其中每对十六进制数字表示一个字符:

mysql> SELECT X'4D7953514C', CHARSET(X'4D7953514C');
+---------------+------------------------+
| X'4D7953514C' | CHARSET(X'4D7953514C') |
+---------------+------------------------+
| MySQL         | binary                 |
+---------------+------------------------+
mysql> SELECT 0x5461626c65, CHARSET(0x5461626c65);
+--------------+-----------------------+
| 0x5461626c65 | CHARSET(0x5461626c65) |
+--------------+-----------------------+
| Table        | binary                |
+--------------+-----------------------+

十六进制文字可能具有可选的字符集引导符和 COLLATE 子句,以指定其为使用特定字符集和排序规则的字符串:

[_*charset_name*] X'*val*' [COLLATE *collation_name*]

示例:

SELECT _latin1 X'4D7953514C';
SELECT _utf8mb4 0x4D7953514C COLLATE utf8mb4_danish_ci;

示例使用 X'*val*' 表示法,但 0x*val* 表示法也允许引导符。有关引导符的信息,请参阅 Section 12.3.8, “Character Set Introducers”。

在数值上下文中,MySQL 将十六进制文字视为 BIGINT UNSIGNED(64 位无符号整数)。为确保十六进制文字的数值处理,请在数值上下文中使用它。实现这一目的的方法包括添加 0 或使用 CAST(... AS UNSIGNED)。例如,将十六进制文字分配给用户定义的变量时,默认情况下是二进制字符串。要将值分配为数字,请在数值上下文中使用它:

mysql> SET @v1 = X'41';
mysql> SET @v2 = X'41'+0;
mysql> SET @v3 = CAST(X'41' AS UNSIGNED);
mysql> SELECT @v1, @v2, @v3;
+------+------+------+
| @v1  | @v2  | @v3  |
+------+------+------+
| A    |   65 |   65 |
+------+------+------+

空的十六进制值(X'')评估为零长度的二进制字符串。转换为数字后,它产生 0:

mysql> SELECT CHARSET(X''), LENGTH(X'');
+--------------+-------------+
| CHARSET(X'') | LENGTH(X'') |
+--------------+-------------+
| binary       |           0 |
+--------------+-------------+
mysql> SELECT X''+0;
+-------+
| X''+0 |
+-------+
|     0 |
+-------+

X'*val*' 表示法基于标准 SQL。0x 表示法基于 ODBC,其中十六进制字符串通常用于为 BLOB 列提供值。

要将字符串或数字转换为十六进制格式的字符串,请使用 HEX() 函数:

mysql> SELECT HEX('cat');
+------------+
| HEX('cat') |
+------------+
| 636174     |
+------------+
mysql> SELECT X'636174';
+-----------+
| X'636174' |
+-----------+
| cat       |
+-----------+

对于十六进制文字,位操作被视为数值上下文,但在 MySQL 8.0 及更高版本中,位操作允许数值或二进制字符串参数。要明确指定十六进制文字的二进制字符串上下文,请至少在一个参数中使用 _binary 引导符:

mysql> SET @v1 = X'000D' | X'0BC0';
mysql> SET @v2 = _binary X'000D' | X'0BC0';
mysql> SELECT HEX(@v1), HEX(@v2);
+----------+----------+
| HEX(@v1) | HEX(@v2) |
+----------+----------+
| BCD      | 0BCD     |
+----------+----------+

显示的结果对于位操作都是相似的,但没有 _binary 的结果是一个 BIGINT 值,而带有 _binary 的结果是一个二进制字符串。由于结果类型的差异,显示的值也不同:数值结果不显示高阶 0 数字。

11.1.5 位值字面量

原文:dev.mysql.com/doc/refman/8.0/en/bit-value-literals.html

位值字面量使用b'*val*'0b*val*表示法编写。val是使用零和一写成的二进制值。任何前导b的大小写不重要。前导0b区分大小写,不能写成0B

合法的位值字面量:

b'01'
B'01'
0b01

非法的位值字面量:

b'2'    (2 is not a binary digit)
0B01    (0B must be written as 0b)

默认情况下,位值字面量是二进制字符串:

mysql> SELECT b'1000001', CHARSET(b'1000001');
+------------+---------------------+
| b'1000001' | CHARSET(b'1000001') |
+------------+---------------------+
| A          | binary              |
+------------+---------------------+
mysql> SELECT 0b1100001, CHARSET(0b1100001);
+-----------+--------------------+
| 0b1100001 | CHARSET(0b1100001) |
+-----------+--------------------+
| a         | binary             |
+-----------+--------------------+

位值字面量可以具有可选的字符集引导符和COLLATE子句,以将其指定为使用特定字符集和排序规则的字符串:

[_*charset_name*] b'*val*' [COLLATE *collation_name*]

示例:

SELECT _latin1 b'1000001';
SELECT _utf8mb4 0b1000001 COLLATE utf8mb4_danish_ci;

示例中使用b'*val*'表示法,但0b*val*表示法也允许引导符。有关引导符的信息,请参见第 12.3.8 节,“字符集引导符”。

在数字上下文中,MySQL 将位字面量视为整数。为确保位字面量的数字处理,请在数字上下文中使用它。实现这一点的方法包括添加 0 或使用CAST(... AS UNSIGNED)。例如,将位字面量分配给用户定义变量默认情况下是一个二进制字符串。要将该值分配为数字,请在数字上下文中使用它:

mysql> SET @v1 = b'1100001';
mysql> SET @v2 = b'1100001'+0;
mysql> SET @v3 = CAST(b'1100001' AS UNSIGNED);
mysql> SELECT @v1, @v2, @v3;
+------+------+------+
| @v1  | @v2  | @v3  |
+------+------+------+
| a    |   97 |   97 |
+------+------+------+

空位值(b'')评估为零长度的二进制字符串。转换为数字后,它产生 0:

mysql> SELECT CHARSET(b''), LENGTH(b'');
+--------------+-------------+
| CHARSET(b'') | LENGTH(b'') |
+--------------+-------------+
| binary       |           0 |
+--------------+-------------+
mysql> SELECT b''+0;
+-------+
| b''+0 |
+-------+
|     0 |
+-------+

位值表示法方便指定要分配给BIT列的值:

mysql> CREATE TABLE t (b BIT(8));
mysql> INSERT INTO t SET b = b'11111111';
mysql> INSERT INTO t SET b = b'1010';
mysql> INSERT INTO t SET b = b'0101';

结果集中的位值以二进制形式返回,可能显示不佳。要将位值转换为可打印形式,请在数字上下文中使用它或使用诸如BIN()HEX()之类的转换函数。转换后的值中不显示高阶 0 位。

mysql> SELECT b+0, BIN(b), OCT(b), HEX(b) FROM t;
+------+----------+--------+--------+
| b+0  | BIN(b)   | OCT(b) | HEX(b) |
+------+----------+--------+--------+
|  255 | 11111111 | 377    | FF     |
|   10 | 1010     | 12     | A      |
|    5 | 101      | 5      | 5      |
+------+----------+--------+--------+

对于位字面量,位操作被视为数字上下文,但在 MySQL 8.0 及更高版本中,位操作允许数字或二进制字符串参数。要明确指定位字面量的二进制字符串上下文,请至少在一个参数中使用_binary引导符:

mysql> SET @v1 = b'000010101' | b'000101010';
mysql> SET @v2 = _binary b'000010101' | _binary b'000101010';
mysql> SELECT HEX(@v1), HEX(@v2);
+----------+----------+
| HEX(@v1) | HEX(@v2) |
+----------+----------+
| 3F       | 003F     |
+----------+----------+

显示的结果对于位操作来说看起来相似,但没有_binary的结果是一个BIGINT值,而有_binary的结果是一个二进制字符串。由于结果类型的差异,显示的值不同:转换后的值中不显示高阶 0 位。

11.1.6 布尔字面值

原文:dev.mysql.com/doc/refman/8.0/en/boolean-literals.html

常量TRUEFALSE分别求值为10。常量名称可以以任何大小写形式编写。

mysql> SELECT TRUE, true, FALSE, false;
 -> 1, 1, 0, 0

11.1.7 NULL Values

原文:dev.mysql.com/doc/refman/8.0/en/null-values.html

NULL 值表示“无数据”。NULL 可以以任何大小写形式编写。

请注意,NULL 值与数值类型的 0 或字符串类型的空字符串等值是不同的。有关更多信息,请参见 Section B.3.4.3, “Problems with NULL Values”。

对于使用 LOAD DATASELECT ... INTO OUTFILE 执行的文本文件导入或导出操作,NULL\N 序列表示。参见 Section 15.2.9, “LOAD DATA Statement”。

对于使用 ORDER BY 进行排序,NULL 值在升序排序时排在其他值之前,在降序排序时排在其他值之后。

11.2 模式对象名称

原文:dev.mysql.com/doc/refman/8.0/en/identifiers.html

11.2.1 标识符长度限制

11.2.2 标识符限定符

11.2.3 标识符大小写敏感性

11.2.4 标识符映射到文件名

11.2.5 函数名称解析和解析

MySQL 中的某些对象,包括数据库、表、索引、列、别名、视图、存储过程、分区、表空间、资源组和其他对象名称被称为标识符。本节描述了 MySQL 中标识符的允许语法。第 11.2.1 节,“标识符长度限制”指示了每种类型标识符的最大长度。第 11.2.3 节,“标识符大小写敏感性”描述了哪些类型的标识符是区分大小写的以及在什么条件下。

标识符可以被引用或未引用。如果标识符包含特殊字符或是保留字,您在引用时必须对其进行引用。(例外情况:在限定名称中跟在句点后面的保留字必须是标识符,因此不需要引用。)保留字列在第 11.3 节,“关键字和保留字”中。

在内部,标识符被转换为并存储为 Unicode(UTF-8)。标识符中允许的 Unicode 字符是基本多文种平面(BMP)中的字符。不允许使用补充字符。因此,标识符可能包含这些字符:

  • 未引用标识符中允许的字符:

    • ASCII:[0-9,a-z,A-Z$_](基本拉丁字母、数字 0-9、美元符号、下划线)

    • 扩展字符集:U+0080 .. U+FFFF

  • 引用标识符中允许的字符包括完整的 Unicode 基本多文种平面(BMP),除了 U+0000:

    • ASCII:U+0001 .. U+007F

    • 扩展字符集:U+0080 .. U+FFFF

  • ASCII NUL(U+0000)和补充字符(U+10000 及更高)不允许在引用或未引用的标识符中使用。

  • 标识符可以以数字开头,但除非引用,不能完全由数字组成。

  • 数据库、表和列名称不能以空格字符结尾。

  • 从 MySQL 8.0.32 开始,将美元符号作为未引用的数据库、表、视图、列、存储程序或别名名称的第一个字符已被弃用,并会产生警告。这包括与限定符一起使用的名称(参见第 11.2.2 节,“标识符限定符”)。当按照本节后面给出的规则引用时,美元符号仍然可以用作此类标识符的前导字符。

��识符引用字符是反引号(```sql):


mysql> SELECT * FROM `select` WHERE `select`.id > 100;

```sql

If the `ANSI_QUOTES` SQL mode is enabled, it is also permissible to quote identifiers within double quotation marks:

mysql> CREATE TABLE "test" (col INT);

错误 1064:您的 SQL 语法中存在错误...

mysql> SET sql_mode='ANSI_QUOTES';

mysql> CREATE TABLE "test" (col INT);

查询成功,影响行数为 0(0.00 秒)


The `ANSI_QUOTES` mode causes the server to interpret double-quoted strings as identifiers. Consequently, when this mode is enabled, string literals must be enclosed within single quotation marks. They cannot be enclosed within double quotation marks. The server SQL mode is controlled as described in Section 7.1.11, “Server SQL Modes”.

Identifier quote characters can be included within an identifier if you quote the identifier. If the character to be included within the identifier is the same as that used to quote the identifier itself, then you need to double the character. The following statement creates a table named `a`b` that contains a column named `c"d`:

mysql> CREATE TABLE a``b (c"d INT);


In the select list of a query, a quoted column alias can be specified using identifier or string quoting characters:

mysql> SELECT 1 AS one, 2 AS 'two';

+-----+-----+

one two

+-----+-----+

1 2

+-----+-----+


在语句的其他地方,对别名的引用必须使用标识符引用,否则引用将被视为字符串文字。

建议不要使用以 `*`M`*e` 或 `*`M`*e*`N`*` 开头的名称,其中 *`M`* 和 *`N`* 是整数。例如,避免使用 `1e` 作为标识符,因为诸如 `1e+3` 这样的表达式是模棱两可的。根据上下文,它可能被解释为表达式 `1e + 3` 或数字 `1e+3`。

在使用 `MD5()` 生成表名时要小心,因为它可能生成类似于刚才描述的非法或模棱两可的格式的名称。

还建议不要使用以 `!hidden!` 开头的列名,以确保新名称不会与现有隐藏列用于功能索引的名称发生冲突。

用户变量不能直接用作 SQL 语句中的标识符或标识符的一部分。请参阅第 11.4 节,“用户定义变量”,了解更多信息和解决方法的示例。

数据库和表名中的特殊字符在相应的文件系统名称中进行编码,如第 11.2.4 节,“标识符映射到文件名”中所述。


# 11.2.1 标识符长度限制

> 原文:[`dev.mysql.com/doc/refman/8.0/en/identifier-length.html`](https://dev.mysql.com/doc/refman/8.0/en/identifier-length.html)

以下表描述了每种标识符类型的最大长度。

| 标识符类型 | 最大长度(字符) |
| --- | --- |
| 数据库 | 64(包括 NDB Cluster 8.0.18 及更高版本) |
| 表 | 64(包括 NDB Cluster 8.0.18 及更高版本) |
| 列 | 64 |
| 索引 | 64 |
| 约束 | 64 |
| 存储过程 | 64 |
| 视图 | 64 |
| 表空间 | 64 |
| 服务器 | 64 |
| 日志文件组 | 64 |
| 别名 | 256(见表后的例外) |
| 复合语句标签 | 16 |
| 用户定义变量 | 64 |
| 资源组 | 64 |
| 标识符类型 | 最大长度(字符) |

在`CREATE VIEW`语句中,列名的别名将与最大列长度为 64 个字符进行检查(而不是最大别名长度为 256 个字符)。

对于不包含约束名称的约束定义,服务器内部生成一个从关联表名派生的名称。例如,内部生成的外键和`CHECK`约束名称由表名加上 `_ibfk_` 或 `_chk_` 和一个数字组成。如果表名接近约束名称的长度限制,那么约束名称所需的额外字符可能导致该名称超过限制,从而导致错误。

标识符使用 Unicode(UTF-8)进行存储。这适用于表定义中的标识符以及存储在 `mysql` 数据库的授权表中的标识符。授权表中的标识符字符串列的大小以字符为单位。您可以使用多字节字符,而不会减少这些列中存储的值所允许的字符数。

在 NDB 8.0.18 之前,NDB Cluster 对数据库和表的名称 imposed 了最大长度为 63 个字符的限制。从 NDB 8.0.18 开始,此限制被移除。请参阅 Section 25.2.7.11, “Previous NDB Cluster Issues Resolved in NDB Cluster 8.0”。

MySQL 账户名称中的用户名称和主机名等值是字符串,而不是标识符。有关存储在授权表中的这些值的最大长度的信息,请参阅 Grant Table Scope Column Properties。


# 11.2.2 标识符限定符

> 原文:[`dev.mysql.com/doc/refman/8.0/en/identifier-qualifiers.html`](https://dev.mysql.com/doc/refman/8.0/en/identifier-qualifiers.html)

对象名称可以是未限定的或限定的。在名称解释明确的情况下,可以使用未限定名称。限定名称包括至少一个限定符,以通过覆盖默认上下文或提供缺失上下文来澄清解释上下文。

例如,此语句使用未限定名称`t1`创建表:

```sql
CREATE TABLE t1 (i INT);

因为t1不包含指定数据库的限定符,该语句在默认数据库中创建表。如果没有默认数据库,则会出现错误。

此语句使用限定名db1.t1创建表:

CREATE TABLE db1.t1 (i INT);

因为db1.t1包含数据库限定符db1,该语句在名为db1的数据库中创建t1,而不管默认数据库如何。如果没有默认数据库,则必须指定限定符。如果有默认数据库,则可以指定限定符,以指定与默认数据库不同的数据库,或者如果默认数据库与指定的数据库相同,则明确指定数据库。

限定符具有以下特点:

  • 未限定名称由单个标识符组成。限定名称由多个标识符组成。

  • 多部分名称的组件必须用句点(.)字符分隔。多部分名称的初始部分充当限定符,影响解释最终标识符的上下文。

  • 限定符字符是一个单独的标记,不需要与相关标识符连续。例如,tbl_name.col_nametbl_name . col_name是等效的。

  • 如果多部分名称的任何组件需要引号,请单独引用它们,而不是整体引用名称。例如,写成my-table`.`my-column,而不是my-table.my-column

  • 在限定名中跟在句点后面的保留字必须是标识符,因此在该上下文中不需要引号。

对象名称的允许限定符取决于对象类型:

  • 数据库名称是完全限定的,不需要限定符:

    CREATE DATABASE db1;
    
  • 表、视图或存储程序名称可以给定数据库名称限定符。在CREATE语句中的未限定和限定名称示例:

    CREATE TABLE mytable ...;
    CREATE VIEW myview ...;
    CREATE PROCEDURE myproc ...;
    CREATE FUNCTION myfunc ...;
    CREATE EVENT myevent ...;
    
    CREATE TABLE mydb.mytable ...;
    CREATE VIEW mydb.myview ...;
    CREATE PROCEDURE mydb.myproc ...;
    CREATE FUNCTION mydb.myfunc ...;
    CREATE EVENT mydb.myevent ...;
    
  • 触发器与表相关联,因此任何限定符都适用于表名:

    CREATE TRIGGER mytrigger ... ON mytable ...;
    
    CREATE TRIGGER mytrigger ... ON mydb.mytable ...;
    
  • 列名可以给定多个限定符,以指示在引用它的语句中的上下文,如下表所示。

    列引用 含义
    col_name 语句中使用的任何表中包含具有该名称的列的列col_name
    tbl_name.col_name 来自默认数据库表tbl_name的列col_name
    db_name.tbl_name.col_name 来自数据库db_name的表tbl_name的列col_name

    换句话说,列名可以被赋予表名限定符,而表名本身可以被赋予数据库名限定符。在SELECT语句中的未限定和限定列引用的示例:

    SELECT c1 FROM mytable
    WHERE c2 > 100;
    
    SELECT mytable.c1 FROM mytable
    WHERE mytable.c2 > 100;
    
    SELECT mydb.mytable.c1 FROM mydb.mytable
    WHERE mydb.mytable.c2 > 100;
    

除非未限定引用是模棱两可的,否则在语句中不需要为对象引用指定限定符。假设列c1仅出现在表t1中,c2仅在t2中,ct1t2中都有。在引用这两个表的语句中,对c的任何未限定引用都是模棱两可的,必须限定为t1.ct2.c以指示你指的是哪个表:

SELECT c1, c2, t1.c FROM t1 INNER JOIN t2
WHERE t2.c > 100;

同样地,在同一语句中从数据库db1的表t和数据库db2的表t中检索数据时,必须对表引用进行限定:对于这些表中的列名的引用,只有在两个表中都出现的列名需要限定。假设列c1仅出现在表db1.t中,c2仅在db2.t中,cdb1.tdb2.t中都有。在这种情况下,c是模棱两可的,必须进行限定,但c1c2则不需要:

SELECT c1, c2, db1.t.c FROM db1.t INNER JOIN db2.t
WHERE db2.t.c > 100;

表别名使得可以更简单地编写限定列引用:

SELECT c1, c2, t1.c FROM db1.t AS t1 INNER JOIN db2.t AS t2
WHERE t2.c > 100;

11.2.3 标识符大小写敏感性

原文:dev.mysql.com/doc/refman/8.0/en/identifier-case-sensitivity.html

在 MySQL 中,数据库对应于数据目录中的目录。数据库中的每个表对应于数据库目录中的至少一个文件(根据存储引擎可能还有更多)。触发器也对应于文件。因此,底层操作系统的大小写敏感性在数据库、表和触发器名称的大小写敏感性中起作用。这意味着在 Windows 中这些名称不区分大小写,但在大多数 Unix 变体中是区分大小写的。一个值得注意的例外是 macOS,它基于 Unix 但使用的默认文件系统类型(HFS+)不区分大小写。但是,macOS 也支持大小写敏感的 UFS 卷,就像在任何 Unix 上一样。参见第 1.6.1 节,“MySQL 对标准 SQL 的扩展”。lower_case_table_names系统变量还影响服务器处理标识符大小写敏感性的方式,如本节后面所述。

注意

虽然在某些平台上数据库、表和触发器名称不区分大小写,但在同一语句中不应该使用不同大小写来引用其中之一。以下语句不起作用,因为它既引用了表my_table又引用了MY_TABLE

mysql> SELECT * FROM my_table WHERE MY_TABLE.col=1;

分区、子分区、列、索引、存储过程、事件和资源组名称在任何平台上都不区分大小写,列别名也是如此。

然而,日志文件组的名称是区分大小写的。这与标准 SQL 不同。

默认情况下,在 Unix 上表别名区分大小写,但在 Windows 或 macOS 上不是。以下语句在 Unix 上不起作用,因为它既引用了别名a又引用了A

mysql> SELECT *col_name* FROM *tbl_name* AS a
       WHERE a.*col_name* = 1 OR A.*col_name* = 2;

然而,在 Windows 上允许相同的语句。为避免由这些差异引起的问题,最好采用一致的约定,例如始终使用小写名称创建和引用数据库和表。这种约定建议用于最大的可移植性和易用性。

MySQL 中表和数据库名称在磁盘上的存储和使用受到lower_case_table_names系统变量的影响。lower_case_table_names可以采用以下表中显示的值。此变量影响触发器标识符的大小写敏感性。在 Unix 上,lower_case_table_names的默认值为 0。在 Windows 上,默认值为 1。在 macOS 上,默认值为 2。

只能在初始化服务器时配置lower_case_table_names。在服务器初始化后更改lower_case_table_names设置是被禁止的。

含义
0 表和数据库名称在磁盘上使用CREATE TABLECREATE DATABASE语句中指定的大小写形式存储。名称比较区分大小写。如果你在具有不区分大小写文件名的系统上运行 MySQL(如 Windows 或 macOS),不应将此变量设置为 0。如果你在不区分大小写的文件系统上强制将此变量设置为 0,并使用不同大小写访问MyISAM表名,可能会导致索引损坏。
1 表名在磁盘上以小写形式存储,名称比较不区分大小写。MySQL 在存储和查找时将所有表名转换为小写。这种行为也适用于数据库名称和表别名。
2 表和数据库名称在磁盘上使用CREATE TABLECREATE DATABASE语句中指定的大小写形式存储,但 MySQL 在查找时将它们转换为小写。名称比较不区分大小写。这仅适用于不区分大小写的文件系统!InnoDB表名和视图名以小写形式存储,就像lower_case_table_names=1一样。

如果你只在一个平台上使用 MySQL,通常不需要使用除默认值以外的lower_case_table_names设置。但是,如果你想在文件系统大小写敏感性不同的平台之间传输表格时可能会遇到困难。例如,在 Unix 上,你可以有两个不同的表格命名为my_tableMY_TABLE,但在 Windows 上这两个名称被视为相同。为了避免由数据库或表名大小写引起的数据传输问题,你有两个选择:

  • 在所有系统上使用lower_case_table_names=1。这样做的主要缺点是,当你使用SHOW TABLESSHOW DATABASES时,你看不到原始大小写的名称。

  • 在 Unix 上使用lower_case_table_names=0,在 Windows 上使用lower_case_table_names=2。这将保留数据库和表名称的大小写。这样做的缺点是,您必须确保在 Windows 上始终使用正确的大小写引用您的数据库和表名称。如果您将您的语句转移到大小写敏感的 Unix 系统,如果大小写不正确,它们将无法正常工作。

    异常:如果您正在使用InnoDB表,并且正在尝试避免这些数据传输问题,您应该在所有平台上使用lower_case_table_names=1来强制将名称转换为小写。

如果根据二进制排序规则,对象名称的大写形式相等,则可能被视为重复。这适用于游标、条件、存储过程、函数、保存点、存储过程参数、存储程序局部变量和插件的名称。但对于列、约束、数据库、分区、使用PREPARE准备的语句、表、触发器、用户和用户定义变量的名称则不适用。

文件系统的大小写敏感性可能会影响INFORMATION_SCHEMA表中字符串列的搜索。有关更多信息,请参见第 12.8.7 节,“在 INFORMATION_SCHEMA 搜索中使用排序规则”。

11.2.4 标识符到文件名的映射

原文:dev.mysql.com/doc/refman/8.0/en/identifier-mapping.html

数据库和表标识符与文件系统中的名称之间存在对应关系。对于基本结构,MySQL 将每个数据库表示为数据目录中的一个目录,根据存储引擎的不同,每个表可能由适当数据库目录中的一个或多个文件表示。

对于数据和索引文件,在磁盘上的确切表示是特定于存储引擎的。这些文件可以存储在���据库目录中,或者���息可以存储在单独的文件中。InnoDB 数据存储在 InnoDB 数据文件中。如果您正在使用 InnoDB 的表空间,则将使用您创建的特定表空间文件。

除 ASCII NUL (X'00') 外,数据库或表标识符中的任何字符都是合法的。当 MySQL 创建数据库目录或表文件时,会对任何在相应文件系统对象中有问题的字符进行编码:

  • 基本拉丁字母(a..zA..Z)、数字(0..9)和下划线(_)按原样编码。因此,它们的大小写敏感性直接取决于文件系统特性。

  • 所有其他具有大写/小写映射的字母表的国家字母都按照以下表格中所示的方式进行编码。代码范围列中的值是 UCS-2 值。

    Code Range Pattern Number Used Unused Blocks
    00C0..017F [@][0..4][g..z] 5*20= 100 97 3 拉丁-1 补充 + 拉丁扩展-A
    0370..03FF [@][5..9][g..z] 5*20= 100 88 12 希腊语和科普特语
    0400..052F [@][g..z][0..6] 20*7= 140 137 3 西里尔字母 + 西里尔字母补充
    0530..058F [@][g..z][7..8] 20*2= 40 38 2 亚美尼亚语
    2160..217F [@][g..z][9] 20*1= 20 16 4 数字形式
    0180..02AF [@][g..z][a..k] 20*11=220 203 17 拉丁扩展-B + 国际音标扩展
    1E00..1EFF [@][g..z][l..r] 20*7= 140 136 4 拉丁语扩展附加
    1F00..1FFF [@][g..z][s..z] 20*8= 160 144 16 希腊语扩展
    .... .... [@][a..f][g..z] 6*20= 120 0 120 保留
    24B6..24E9 [@][@][a..z] 26 26 0 封闭字母数字
    FF21..FF5A [@][a..z][@] 26 26 0 半角和全角形式
    Code Range Pattern Number Used Unused Blocks

    序列中的一个字节编码了大小写。例如:LATIN CAPITAL LETTER A WITH GRAVE 被编码为 @0G,而 LATIN SMALL LETTER A WITH GRAVE 被编码为 @0g。这里的第三个字节(Gg)表示大小写。(在不区分大小写的文件系统中,这两个字母被视为相同。)

    对于某些块,如西里尔字母,第二个字节确定大小写。对于其他块,如 Latin1 补充,第三个字节确定大小写。如果序列中的两个字节是字母(如希腊扩展中),最左边的字母字符代表大小写。所有其他字母字节必须是小写。

  • 除了下划线(_)之外的所有非字母字符,以及没有大写/小写映射的字母(如希伯来语)都使用十六进制表示法编码,使用小写字母表示十六进制数字a..f

    0x003F -> @003f
    0xFFFF -> @ffff
    

    十六进制值对应于ucs2双字节字符集中的字符值。

在 Windows 上,一些名称,如nulprnaux,在服务器创建相应的文件或目录时,通过在名称后附加@@@进行编码。这在所有平台上都会发生,以便在平台之间移植相应的数据库对象。

以下名称是保留名称,如果在模式或表名中使用,则附加@@@

  • CON

  • PRN

  • AUX

  • NUL

  • COM1 到 COM9

  • LPT1 到 LPT9

CLOCK$ 也是这组保留名称的成员之一,但不是附加@@@,而是用@0024代替。也就是说,如果将 CLOCK$ 用作模式或表名,它将被写入文件系统为CLOCK@0024。对于模式或表名中任何使用 $(美元符号)的情况也是如此;在文件系统中,它将被替换为@0024

注意

这些名称也以它们的附加形式写入INNODB_TABLES,但以用户输入的未附加形式写入TABLES

11.2.5 函数名称解析和解析

原文:dev.mysql.com/doc/refman/8.0/en/function-resolution.html

MySQL 支持内置(本地)函数、可加载函数和存储函数。本节描述了服务器如何识别内置函数的名称是作为函数调用还是作为标识符使用,以及在存在具有给定名称的不同类型函数时服务器如何确定使用哪个函数。

  • 内置函数名称解析

  • 函数名称解析

内置函数名称解析

解析器使用默认规则解析内置函数的名称。这些规则可以通过启用IGNORE_SPACE SQL 模式来更改。

当解析器遇到内置函数名称时,必须确定该名称是表示函数调用还是非表达式引用标识符(例如表或列名称)。例如,在以下语句中,对count的第一个引用是函数调用,而第二个引用是表名:

SELECT COUNT(*) FROM mytable;
CREATE TABLE count (i INT);

解析器应仅在解析预期为表达式的内容时将内置函数名称识别为表示函数调用。也就是说,在非表达式上下文中,函数名称允许作为标识符。

然而,一些内置函数具有特殊的解析或实现考虑因素,因此解析器默认使用以下规则来区分它们的名称是作为函数调用还是作为非表达式上下文中的标识符:

  • 要在表达式中使用名称作为函数调用,名称和后面的(括号字符之间不能有空格。

  • 相反,要将函数名称用作标识符,必须不紧跟在括号后面。

要求函数调用在名称和括号之间没有空格的规定仅适用于具有特殊考虑因素的内置函数。COUNT就是这样一个名称。sql/lex.h源文件列出了这些特殊函数的名称,后续空格决定了它们的解释:由symbols[]数组中的SYM_FN()宏定义的名称。

下面列出了在 MySQL 8.0 中受IGNORE_SPACE设置影响并在sql/lex.h源文件中列为特殊的函数列表。您可能会发现将无空格要求视为适用于所有函数调用最容易。

  • ADDDATE

  • BIT_AND

  • BIT_OR

  • BIT_XOR

  • CAST

  • COUNT

  • CURDATE

  • CURTIME

  • DATE_ADD

  • DATE_SUB

  • EXTRACT

  • GROUP_CONCAT

  • MAX

  • MID

  • MIN

  • NOW

  • POSITION

  • SESSION_USER

  • STD

  • STDDEV

  • STDDEV_POP

  • STDDEV_SAMP

  • SUBDATE

  • SUBSTR

  • SUBSTRING

  • SUM

  • SYSDATE

  • SYSTEM_USER

  • TRIM

  • VARIANCE

  • VAR_POP

  • VAR_SAMP

对于在sql/lex.h中未列为特殊的函数,空格并不重要。只有在表达式上下文中使用时,它们才会被解释为函数调用,否则可以自由地用作标识符。ASCII就是这样一个名称。然而,对于这些未受影响的函数名称,在表达式上下文中的解释可能有所不同:如果存在具有给定名称的内置函数,则*func_name* ()将被解释为内置函数;如果没有,则*func_name* ()将被解释为可加载函数或存储函数(如果存在具有该名称的函数)。

IGNORE_SPACE SQL 模式可用于修改解析器对对空格敏感的函数名称的处理方式:

  • 禁用IGNORE_SPACE后,当函数名称和后续括号之间没有空格时,解析器将名称解释为函数调用。即使在非表达式上下文中使用函数名称也会发生这种情况:

    mysql> CREATE TABLE count(i INT);
    ERROR 1064 (42000): You have an error in your SQL syntax ...
    near 'count(i INT)'
    

    为了消除错误并使名称被视为标识符,可以在名称后面使用空格或将其写为带引号的标识符(或两者都使用):

    CREATE TABLE count (i INT);
    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);
    
  • 启用IGNORE_SPACE后,解析器放宽了函数名称和后续括号之间不能有空格的要求。这样可以更灵活地编写函数调用。例如,以下任一函数调用都是合法的:

    SELECT COUNT(*) FROM mytable;
    SELECT COUNT (*) FROM mytable;
    

    然而,启用IGNORE_SPACE也会导致解析器将受影响的函数名称视为保留字(请参阅第 11.3 节,“关键字和保留字”)。这意味着名称后面的空格不再表示其用作标识符。该名称可以在带有或不带有后续空格的函数调用中使用,但在非表达式上下文中除非加引号,否则会导致语法错误。例如,启用IGNORE_SPACE后,以下两个语句都会因解析器将count解释为保留字而导致语法错误:

    CREATE TABLE count(i INT);
    CREATE TABLE count (i INT);
    

    要在非表达式上下文中使用函数名称,请将其写为带引号的标识符:

    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);
    

要启用IGNORE_SPACE SQL 模式,请使用以下语句:

SET sql_mode = 'IGNORE_SPACE';

IGNORE_SPACE也被某些其他复合模式启用,例如ANSI将其包含在其值中:

SET sql_mode = 'ANSI';

查看第 7.1.11 节,“服务器 SQL 模式”,以查看哪些复合模式启用了IGNORE_SPACE

为了最大程度地减少 SQL 代码对IGNORE_SPACE设置的依赖,请遵循以下准则:

  • 避免创建与内置函数同名的���加载函数或存储函数。

  • 避免在非表达式上下文中使用函数名。例如,这些语句使用了 count(受 IGNORE_SPACE 影响的函数名之一),因此如果启用了 IGNORE_SPACE,无论名称后是否有空格,它们都会失败:

    CREATE TABLE count(i INT);
    CREATE TABLE count (i INT);
    

    如果必须在非表达式上下文中使用函数名,请将其写为引号标识符:

    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);
    

函数名解析

以下规则描述了服务器如何解析函数名的引用以进行函数创建和调用:

  • 内置函数和可加载函数

    如果尝试创建与内置函数同名的可加载函数,则会出现错误。

    IF NOT EXISTS(从 MySQL 8.0.29 开始提供)在这种情况下没有效果。有关更多信息,请参阅 Section 15.7.4.1, “CREATE FUNCTION Statement for Loadable Functions”。

  • 内置函数和存储函数

    可以创建与内置函数同名的存储函数,但要调用存储函数,必须使用模式名称进行限定。例如,如果在 test 模式中创建了名为 PI 的存储函数,则调用时应为 test.PI(),因为服务器会将不带限定符的 PI() 解析为对内置函数的引用。如果存储函数名与内置函数名冲突,服务器会生成警告。可以使用 SHOW WARNINGS 显示警告。

    IF NOT EXISTS(MySQL 8.0.29 及更高版本)在这种情况下没有效果;请参阅 Section 15.1.17, “CREATE PROCEDURE and CREATE FUNCTION Statements”。

  • 可加载函数和存储函数

    可以创建与现有可加载函数同名的存储函数,反之亦然。如果建议的存储函数名与现有可加载函数名冲突,或者建议的可加载函数名与现有存储函数名相同,则服务器会生成警告。在这两种情况下,一旦两个函数都存在,以后在调用存储函数时必须使用模式名称进行限定;在这种情况下,服务器假定未限定的名称指的是可加载函数。

    从 MySQL 8.0.29 开始,IF NOT EXISTSCREATE FUNCTION 语句中得到支持,但在这种情况下没有效果。

    在 MySQL 8.0.28 之前,可以创建与现有可加载函数同名的存储函数,但反之则不行(Bug #33301931)。

前述函数名解析规则对于升级到实现新内置函数的 MySQL 版本具有影响:

  • 如果您已经创建了一个具有特定名称的可加载函数,并将 MySQL 升级到实现了同名新内置函数的版本,则该可加载函数将变得无法访问。为了纠正这个问题,使用 DROP FUNCTION 删除可加载函数,然后使用 CREATE FUNCTION 以不冲突的不同名称重新创建可加载函数。然后修改任何受影响的代码以使用新名称。

  • 如果 MySQL 的新版本实现了一个与现有存储函数同名的内置函数或可加载函数,你有两个选择:将存储函数重命名为不冲突的名称,或者修改任何尚未这样做的对该函数的调用以使用模式限定符(*schema_name*.*func_name*() 语法)。在任何情况下,相应地修改任何受影响的代码。

11.3 关键字和保留字

原文:dev.mysql.com/doc/refman/8.0/en/keywords.html

关键字是在 SQL 中具有重要意义的单词。 某些关键字,例如SELECTDELETEBIGINT,是保留的,需要特殊处理才能用作标识符,例如表和列名。 对于内置函数的名称也可能是如此。

非保留关键字可以作为标识符而无需引用。 如果引用它们如第 11.2 节,“模式对象名称”中所述,保留字可以作为标识符使用:

mysql> CREATE TABLE interval (begin INT, end INT);
ERROR 1064 (42000): You have an error in your SQL syntax ...
near 'interval (begin INT, end INT)'

BEGINEND 是关键字但不是保留字,因此它们作为标识符的使用不需要引用。 INTERVAL 是一个保留关键字,必须引用才能用作标识符:

mysql> CREATE TABLE `interval` (begin INT, end INT);
Query OK, 0 rows affected (0.01 sec)

例外:在限定名称中跟在句点后面的单词必须是标识符,因此即使它是保留字,也不需要引用:

mysql> CREATE TABLE mydb.interval (begin INT, end INT);
Query OK, 0 rows affected (0.01 sec)

内置函数的名称可以作为标识符,但可能需要小心使用。 例如,COUNT 可以作为列名。 但是,默认情况下,在函数名称和后续(字符之间不允许有空格。 此要求使解析器能够区分名称是在函数调用中使用还是在非函数上下文中使用。 有关函数名称识别的更多详细信息,请参阅第 11.2.5 节,“函数名称解析和解析”。

INFORMATION_SCHEMA.KEYWORDS 表列出了 MySQL 认为是关键字的单词,并指示它们是否是保留字。 请参阅第 28.3.17 节,“INFORMATION_SCHEMA KEYWORDS 表”。

  • MySQL 8.0 关键字和保留字

  • MySQL 8.0 新关键字和保留字

  • MySQL 8.0 已移除的关键字和保留字

MySQL 8.0 关键字和保留字

下面的列表显示了 MySQL 8.0 中的关键字和保留字,以及从版本到版本的单词更改。 保留关键字用(R)标记。此外,_FILENAME 是保留的。

在某个时候,您可能会升级到更高版本,因此查看未来保留字也是一个好主意。您可以在涵盖更高版本 MySQL 的手册中找到这些内容。列���中的大多数保留字被标准 SQL 禁止用作列或表名(例如,GROUP)。有一些是保留的,因为 MySQL 需要它们并使用yacc解析器。

A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

A

  • ACCESSIBLE (R)

  • ACCOUNT

  • ACTION

  • ACTIVE; added in 8.0.14 (nonreserved)

  • ADD (R)

  • ADMIN; became nonreserved in 8.0.12

  • AFTER

  • AGAINST

  • AGGREGATE

  • ALGORITHM

  • ALL (R)

  • ALTER (R)

  • ALWAYS

  • ANALYSE; removed in 8.0.1

  • ANALYZE (R)

  • AND (R)

  • ANY

  • ARRAY; added in 8.0.17 (reserved); became nonreserved in 8.0.19

  • AS (R)

  • ASC (R)

  • ASCII

  • ASENSITIVE (R)

  • AT

  • ATTRIBUTE; added in 8.0.21 (nonreserved)

  • AUTHENTICATION; added in 8.0.27 (nonreserved)

  • AUTOEXTEND_SIZE

  • AUTO_INCREMENT

  • AVG

  • AVG_ROW_LENGTH

B

  • BACKUP

  • BEFORE (R)

  • BEGIN

  • BETWEEN (R)

  • BIGINT (R)

  • BINARY (R)

  • BINLOG

  • BIT

  • BLOB (R)

  • BLOCK

  • BOOL

  • BOOLEAN

  • BOTH (R)

  • BTREE

  • BUCKETS; added in 8.0.2 (nonreserved)

  • BULK; added in 8.0.32 (nonreserved)

  • BY (R)

  • BYTE

C

  • CACHE

  • CALL (R)

  • CASCADE (R)

  • CASCADED

  • CASE (R)

  • CATALOG_NAME

  • CHAIN

  • CHALLENGE_RESPONSE; added in 8.0.27 (nonreserved)

  • CHANGE (R)

  • CHANGED

  • CHANNEL

  • CHAR (R)

  • CHARACTER (R)

  • CHARSET

  • CHECK (R)

  • CHECKSUM

  • CIPHER

  • CLASS_ORIGIN

  • CLIENT

  • CLONE; added in 8.0.3 (nonreserved)

  • CLOSE

  • COALESCE

  • CODE

  • COLLATE (R)

  • COLLATION

  • COLUMN (R)

  • COLUMNS

  • COLUMN_FORMAT

  • COLUMN_NAME

  • COMMENT

  • COMMIT

  • COMMITTED

  • COMPACT

  • COMPLETION

  • COMPONENT

  • COMPRESSED

  • COMPRESSION

  • CONCURRENT

  • CONDITION (R)

  • CONNECTION

  • CONSISTENT

  • CONSTRAINT (R)

  • CONSTRAINT_CATALOG

  • CONSTRAINT_NAME

  • CONSTRAINT_SCHEMA

  • CONTAINS

  • CONTEXT

  • CONTINUE (R)

  • CONVERT (R)

  • CPU

  • CREATE (R)

  • CROSS (R)

  • CUBE (R); 在 8.0.1 中变为保留

  • CUME_DIST (R); 在 8.0.2 中添加(保留)

  • CURRENT

  • CURRENT_DATE (R)

  • CURRENT_TIME (R)

  • CURRENT_TIMESTAMP (R)

  • CURRENT_USER (R)

  • CURSOR (R)

  • CURSOR_NAME

D

  • DATA

  • DATABASE (R)

  • DATABASES (R)

  • DATAFILE

  • DATE

  • DATETIME

  • DAY

  • DAY_HOUR (R)

  • DAY_MICROSECOND (R)

  • DAY_MINUTE (R)

  • DAY_SECOND (R)

  • DEALLOCATE

  • DEC (R)

  • DECIMAL (R)

  • DECLARE (R)

  • DEFAULT (R)

  • DEFAULT_AUTH

  • DEFINER

  • DEFINITION; 在 8.0.4 中添加(非保留)

  • DELAYED (R)

  • DELAY_KEY_WRITE

  • DELETE (R)

  • DENSE_RANK (R); 在 8.0.2 中添加(保留)

  • DESC (R)

  • DESCRIBE (R)

  • DESCRIPTION; 在 8.0.4 中添加(非保留)

  • DES_KEY_FILE; 在 8.0.3 中移除

  • DETERMINISTIC (R)

  • DIAGNOSTICS

  • DIRECTORY

  • DISABLE

  • DISCARD

  • DISK

  • DISTINCT (R)

  • DISTINCTROW (R)

  • DIV (R)

  • DO

  • DOUBLE (R)

  • DROP (R)

  • DUAL (R)

  • DUMPFILE

  • DUPLICATE

  • DYNAMIC

E

  • EACH (R)

  • ELSE (R)

  • ELSEIF (R)

  • EMPTY (R); 在 8.0.4 中添加(保留)

  • ENABLE

  • ENCLOSED (R)

  • ENCRYPTION

  • END

  • ENDS

  • ENFORCED; 在 8.0.16 中添加(非保留)

  • ENGINE

  • ENGINES

  • ENGINE_ATTRIBUTE; 在 8.0.21 中添加(非保留)

  • ENUM

  • ERROR

  • ERRORS

  • ESCAPE

  • ESCAPED (R)

  • EVENT

  • EVENTS

  • EVERY

  • EXCEPT (R)

  • EXCHANGE

  • EXCLUDE; 在 8.0.2 中添加(非保留)

  • EXECUTE

  • EXISTS (R)

  • EXIT (R)

  • EXPANSION

  • EXPIRE

  • EXPLAIN (R)

  • EXPORT

  • EXTENDED

  • EXTENT_SIZE

F

  • FACTOR; 在 8.0.27 中添加(非保留)

  • FAILED_LOGIN_ATTEMPTS; 在 8.0.19 中添加(非保留)

  • FALSE (R)

  • FAST

  • FAULTS

  • FETCH (R)

  • FIELDS

  • FILE

  • FILE_BLOCK_SIZE

  • FILTER

  • FINISH; 在 8.0.27 中添加(非保留)

  • FIRST

  • FIRST_VALUE (R); 在 8.0.2 中添加(保留)

  • FIXED

  • FLOAT (R)

  • FLOAT4 (R)

  • FLOAT8 (R)

  • FLUSH

  • FOLLOWING; 在 8.0.2 中添加(非保留)

  • FOLLOWS

  • FOR (R)

  • FORCE (R)

  • FOREIGN (R)

  • FORMAT

  • FOUND

  • FROM (R)

  • FULL

  • FULLTEXT (R)

  • FUNCTION (R); 在 8.0.1 中变为保留

G

  • GENERAL

  • GENERATE; 在 8.0.32 中添加(非保留)

  • GENERATED (R)

  • GEOMCOLLECTION; 在 8.0.11 中添加(非保留)

  • GEOMETRY

  • GEOMETRYCOLLECTION

  • GET (R)

  • GET_FORMAT

  • GET_MASTER_PUBLIC_KEY; 在 8.0.4 中添加(保留);在 8.0.11 中变为非保留

  • GET_SOURCE_PUBLIC_KEY; 在 8.0.23 中添加(非保留)

  • GLOBAL

  • GRANT (R)

  • GRANTS

  • GROUP (R)

  • GROUPING (R); 在 8.0.1 中添加(保留)

  • GROUPS (R); 在 8.0.2 中添加(保留)

  • GROUP_REPLICATION

  • GTID_ONLY; 在 8.0.27 中添加(非保留)

H

  • HANDLER

  • HASH

  • HAVING (R)

  • HELP

  • HIGH_PRIORITY (R)

  • HISTOGRAM; 在 8.0.2 中添加(非保留)

  • HISTORY; 在 8.0.3 中添加(非保留)

  • HOST

  • HOSTS

  • HOUR

  • HOUR_MICROSECOND (R)

  • HOUR_MINUTE (R)

  • HOUR_SECOND (R)

I

  • IDENTIFIED

  • IF (R)

  • IGNORE (R)

  • IGNORE_SERVER_IDS

  • IMPORT

  • IN (R)

  • INACTIVE; 在 8.0.14 中添加(非保留)

  • INDEX (R)

  • INDEXES

  • INFILE (R)

  • INITIAL; 在 8.0.27 中添加(非保留)

  • INITIAL_SIZE

  • INITIATE; 在 8.0.27 中添加(非保留)

  • INNER (R)

  • INOUT (R)

  • INSENSITIVE (R)

  • INSERT (R)

  • INSERT_METHOD

  • INSTALL

  • INSTANCE

  • INT (R)

  • INT1 (R)

  • INT2 (R)

  • INT3 (R)

  • INT4 (R)

  • INT8 (R)

  • INTEGER (R)

  • INTERSECT (R); added in 8.0.31 (reserved)

  • INTERVAL (R)

  • INTO (R)

  • INVISIBLE

  • INVOKER

  • IO

  • IO_AFTER_GTIDS (R)

  • IO_BEFORE_GTIDS (R)

  • IO_THREAD

  • IPC

  • IS (R)

  • ISOLATION

  • ISSUER

  • ITERATE (R)

J

  • JOIN (R)

  • JSON

  • JSON_TABLE (R); added in 8.0.4 (reserved)

  • JSON_VALUE; added in 8.0.21 (nonreserved)

K

  • KEY (R)

  • KEYRING; added in 8.0.24 (nonreserved)

  • KEYS (R)

  • KEY_BLOCK_SIZE

  • KILL (R)

L

  • LAG (R); added in 8.0.2 (reserved)

  • LANGUAGE

  • LAST

  • LAST_VALUE (R); added in 8.0.2 (reserved)

  • LATERAL (R); added in 8.0.14 (reserved)

  • LEAD (R); added in 8.0.2 (reserved)

  • LEADING (R)

  • LEAVE (R)

  • LEAVES

  • LEFT (R)

  • LESS

  • LEVEL

  • LIKE (R)

  • LIMIT (R)

  • LINEAR (R)

  • LINES (R)

  • LINESTRING

  • LIST

  • LOAD (R)

  • LOCAL

  • LOCALTIME (R)

  • LOCALTIMESTAMP (R)

  • LOCK (R)

  • LOCKED; added in 8.0.1 (nonreserved)

  • LOCKS

  • LOGFILE

  • LOGS

  • LONG (R)

  • LONGBLOB (R)

  • LONGTEXT (R)

  • LOOP (R)

  • LOW_PRIORITY (R)

M

  • MASTER

  • MASTER_AUTO_POSITION

  • MASTER_BIND (R)

  • MASTER_COMPRESSION_ALGORITHMS; added in 8.0.18 (nonreserved)

  • MASTER_CONNECT_RETRY

  • MASTER_DELAY

  • MASTER_HEARTBEAT_PERIOD

  • MASTER_HOST

  • MASTER_LOG_FILE

  • MASTER_LOG_POS

  • MASTER_PASSWORD

  • MASTER_PORT

  • MASTER_PUBLIC_KEY_PATH; added in 8.0.4 (nonreserved)

  • MASTER_RETRY_COUNT

  • MASTER_SERVER_ID; removed in 8.0.23

  • MASTER_SSL

  • MASTER_SSL_CA

  • MASTER_SSL_CAPATH

  • MASTER_SSL_CERT

  • MASTER_SSL_CIPHER

  • MASTER_SSL_CRL

  • MASTER_SSL_CRLPATH

  • MASTER_SSL_KEY

  • MASTER_SSL_VERIFY_SERVER_CERT (R)

  • MASTER_TLS_CIPHERSUITES; added in 8.0.19 (nonreserved)

  • MASTER_TLS_VERSION

  • MASTER_USER

  • MASTER_ZSTD_COMPRESSION_LEVEL; added in 8.0.18 (nonreserved)

  • MATCH (R)

  • MAXVALUE (R)

  • MAX_CONNECTIONS_PER_HOUR

  • MAX_QUERIES_PER_HOUR

  • MAX_ROWS

  • MAX_SIZE

  • MAX_UPDATES_PER_HOUR

  • MAX_USER_CONNECTIONS

  • MEDIUM

  • MEDIUMBLOB (R)

  • MEDIUMINT (R)

  • MEDIUMTEXT (R)

  • MEMBER; added in 8.0.17 (reserved); became nonreserved in 8.0.19

  • MEMORY

  • MERGE

  • MESSAGE_TEXT

  • MICROSECOND

  • MIDDLEINT (R)

  • MIGRATE

  • MINUTE

  • MINUTE_MICROSECOND (R)

  • MINUTE_SECOND (R)

  • MIN_ROWS

  • MOD (R)

  • MODE

  • MODIFIES (R)

  • MODIFY

  • MONTH

  • MULTILINESTRING

  • MULTIPOINT

  • MULTIPOLYGON

  • MUTEX

  • MYSQL_ERRNO

N

  • NAME

  • NAMES

  • NATIONAL

  • NATURAL (R)

  • NCHAR

  • NDB

  • NDBCLUSTER

  • NESTED; added in 8.0.4 (nonreserved)

  • NETWORK_NAMESPACE; added in 8.0.16 (nonreserved)

  • NEVER

  • NEW

  • NEXT

  • NO

  • NODEGROUP

  • NONE

  • NOT (R)

  • NOWAIT; added in 8.0.1 (nonreserved)

  • NO_WAIT

  • NO_WRITE_TO_BINLOG (R)

  • NTH_VALUE (R); added in 8.0.2 (reserved)

  • NTILE (R); added in 8.0.2 (reserved)

  • NULL (R)

  • NULLS; added in 8.0.2 (nonreserved)

  • NUMBER

  • NUMERIC (R)

  • NVARCHAR

O

  • OF (R); added in 8.0.1 (reserved)

  • OFF; added in 8.0.20 (nonreserved)

  • OFFSET

  • OJ; added in 8.0.16 (nonreserved)

  • OLD; added in 8.0.14 (nonreserved)

  • ON (R)

  • ONE

  • ONLY

  • OPEN

  • OPTIMIZE (R)

  • OPTIMIZER_COSTS (R)

  • OPTION (R)

  • OPTIONAL; added in 8.0.13 (nonreserved)

  • OPTIONALLY (R)

  • OPTIONS

  • OR (R)

  • ORDER (R)

  • ORDINALITY; added in 8.0.4 (nonreserved)

  • ORGANIZATION; 在 8.0.4 版本中添加(非保留)

  • OTHERS; 在 8.0.2 版本中添加(非保留)

  • OUT (R)

  • OUTER (R)

  • OUTFILE (R)

  • OVER (R); 在 8.0.2 版本中添加(保留)

  • OWNER

P

  • PACK_KEYS

  • PAGE

  • PARSER

  • PARTIAL

  • PARTITION (R)

  • PARTITIONING

  • PARTITIONS

  • PASSWORD

  • PASSWORD_LOCK_TIME; 在 8.0.19 版本中添加(非保留)

  • PATH; 在 8.0.4 版本中添加(非保留)

  • PERCENT_RANK (R); 在 8.0.2 版本中添加(保留)

  • PERSIST; 在 8.0.16 版本中变为非保留

  • PERSIST_ONLY; 在 8.0.2 版本中添加(保留);在 8.0.16 版本中变为非保留

  • PHASE

  • PLUGIN

  • PLUGINS

  • PLUGIN_DIR

  • POINT

  • POLYGON

  • PORT

  • PRECEDES

  • PRECEDING; 在 8.0.2 版本中添加(非保留)

  • PRECISION (R)

  • PREPARE

  • PRESERVE

  • PREV

  • PRIMARY (R)

  • PRIVILEGES

  • PRIVILEGE_CHECKS_USER; 在 8.0.18 版本中添加(非保留)

  • PROCEDURE (R)

  • PROCESS; 在 8.0.11 版本中添加(非保留)

  • PROCESSLIST

  • PROFILE

  • PROFILES

  • PROXY

  • PURGE (R)

Q

  • QUARTER

  • QUERY

  • QUICK

R

  • RANDOM; 在 8.0.18 版本中添加(非保留)

  • RANGE (R)

  • RANK (R); 在 8.0.2 版本中添加(保留)

  • READ (R)

  • READS (R)

  • READ_ONLY

  • READ_WRITE (R)

  • REAL (R)

  • REBUILD

  • RECOVER

  • RECURSIVE (R); 在 8.0.1 版本中添加(保留)

  • REDOFILE; 在 8.0.3 版本中移除

  • REDO_BUFFER_SIZE

  • REDUNDANT

  • REFERENCE; 在 8.0.4 版本中添加(非保留)

  • REFERENCES (R)

  • REGEXP (R)

  • REGISTRATION; 在 8.0.27 版本中添加(非保留)

  • RELAY

  • RELAYLOG

  • RELAY_LOG_FILE

  • RELAY_LOG_POS

  • RELAY_THREAD

  • RELEASE (R)

  • RELOAD

  • REMOTE; 在 8.0.3 版本中添加(非保留);在 8.0.14 版本中移除

  • REMOVE

  • RENAME (R)

  • REORGANIZE

  • REPAIR

  • REPEAT (R)

  • REPEATABLE

  • REPLACE (R)

  • REPLICA; 在 8.0.22 版本中添加(非保留)

  • REPLICAS; 在 8.0.22 版本中添加(非保留)

  • REPLICATE_DO_DB

  • REPLICATE_DO_TABLE

  • REPLICATE_IGNORE_DB

  • REPLICATE_IGNORE_TABLE

  • REPLICATE_REWRITE_DB

  • REPLICATE_WILD_DO_TABLE

  • REPLICATE_WILD_IGNORE_TABLE

  • REPLICATION

  • REQUIRE (R)

  • REQUIRE_ROW_FORMAT; 在 8.0.19 版本中添加(非保留)

  • RESET

  • RESIGNAL (R)

  • RESOURCE; 在 8.0.3 版本中添加(非保留)

  • RESPECT; 在 8.0.2 版本中添加(非保留)

  • RESTART; 在 8.0.4 版本中添加(非保留)

  • RESTORE

  • RESTRICT (R)

  • RESUME

  • RETAIN; 在 8.0.14 版本中添加(非保留)

  • RETURN (R)

  • RETURNED_SQLSTATE

  • RETURNING; 在 8.0.21 版本中添加(非保留)

  • RETURNS

  • REUSE; 在 8.0.3 版本中添加(非保留)

  • REVERSE

  • REVOKE (R)

  • RIGHT (R)

  • RLIKE (R)

  • ROLE; 在 8.0.1 版本中变为非保留

  • ROLLBACK

  • ROLLUP

  • ROTATE

  • ROUTINE

  • ROW (R); 在 8.0.2 版本中变为保留

  • ROWS (R); 在 8.0.2 版本中变为保留

  • ROW_COUNT

  • ROW_FORMAT

  • ROW_NUMBER (R); 在 8.0.2 版本中添加(保留)

  • RTREE

S

  • SAVEPOINT

  • SCHEDULE

  • SCHEMA (R)

  • SCHEMAS (R)

  • SCHEMA_NAME

  • SECOND

  • SECONDARY; 在 8.0.16 版本中添加(非保留)

  • SECONDARY_ENGINE; 在 8.0.13 版本中添加(非保留)

  • SECONDARY_ENGINE_ATTRIBUTE; 在 8.0.21 版本中添加(非保留)

  • SECONDARY_LOAD; 在 8.0.13 版本中添加(非保留)

  • SECONDARY_UNLOAD; 在 8.0.13 版本中添加(非保留)

  • SECOND_MICROSECOND (R)

  • SECURITY

  • SELECT (R)

  • SENSITIVE (R)

  • SEPARATOR (R)

  • SERIAL

  • SERIALIZABLE

  • SERVER

  • SESSION

  • SET (R)

  • SHARE

  • SHOW (R)

  • SHUTDOWN

  • SIGNAL (R)

  • SIGNED

  • SIMPLE

  • SKIP; 在 8.0.1 版本中添加(非保留)

  • SLAVE

  • SLOW

  • SMALLINT(R)

  • SNAPSHOT

  • SOCKET

  • SOME

  • SONAME

  • SOUNDS

  • SOURCE

  • SOURCE_AUTO_POSITION; 在 8.0.23 版本中添加(非保留)

  • SOURCE_BIND; 在 8.0.23 版本中添加(��保留)

  • SOURCE_COMPRESSION_ALGORITHMS; 在 8.0.23 版本中添加(非保留)

  • SOURCE_CONNECT_RETRY; 在 8.0.23 版本中添加(非保留)

  • SOURCE_DELAY; 在 8.0.23 版本中添加(非保留)

  • SOURCE_HEARTBEAT_PERIOD; 在 8.0.23 版本中添加(非保留)

  • SOURCE_HOST; 在 8.0.23 版本中添加(非保留)

  • SOURCE_LOG_FILE; 在 8.0.23 版本中添加(非保留)

  • SOURCE_LOG_POS; 在 8.0.23 版本中添加(非保留)

  • SOURCE_PASSWORD; 在 8.0.23 版本中添加(非保留)

  • SOURCE_PORT; 在 8.0.23 版本中添加(非保留)

  • SOURCE_PUBLIC_KEY_PATH; 在 8.0.23 版本中添加(非保留)

  • SOURCE_RETRY_COUNT; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_CA; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_CAPATH; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_CERT; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_CIPHER; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_CRL; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_CRLPATH; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_KEY; 在 8.0.23 版本中添加(非保留)

  • SOURCE_SSL_VERIFY_SERVER_CERT; 在 8.0.23 版本中添加(非保留)

  • SOURCE_TLS_CIPHERSUITES; 在 8.0.23 版本中添加(非保留)

  • SOURCE_TLS_VERSION; 在 8.0.23 版本中添加(非保留)

  • SOURCE_USER; 在 8.0.23 版本中添加(非保留)

  • SOURCE_ZSTD_COMPRESSION_LEVEL; 在 8.0.23 版本中添加(非保留)

  • SPATIAL(R)

  • SPECIFIC(R)

  • SQL(R)

  • SQLEXCEPTION(R)

  • SQLSTATE(R)

  • SQLWARNING(R)

  • SQL_AFTER_GTIDS

  • SQL_AFTER_MTS_GAPS

  • SQL_BEFORE_GTIDS

  • SQL_BIG_RESULT(R)

  • SQL_BUFFER_RESULT

  • SQL_CACHE; 在 8.0.3 版本中移除

  • SQL_CALC_FOUND_ROWS(R)

  • SQL_NO_CACHE

  • SQL_SMALL_RESULT(R)

  • SQL_THREAD

  • SQL_TSI_DAY

  • SQL_TSI_HOUR

  • SQL_TSI_MINUTE

  • SQL_TSI_MONTH

  • SQL_TSI_QUARTER

  • SQL_TSI_SECOND

  • SQL_TSI_WEEK

  • SQL_TSI_YEAR

  • SRID; 在 8.0.3 版本中添加(非保留)

  • SSL(R)

  • STACKED

  • START

  • STARTING(R)

  • STARTS

  • STATS_AUTO_RECALC

  • STATS_PERSISTENT

  • STATS_SAMPLE_PAGES

  • STATUS

  • STOP

  • STORAGE

  • STORED(R)

  • STRAIGHT_JOIN(R)

  • STREAM; 在 8.0.20 版本中添加(非保留)

  • STRING

  • SUBCLASS_ORIGIN

  • SUBJECT

  • SUBPARTITION

  • SUBPARTITIONS

  • SUPER

  • SUSPEND

  • SWAPS

  • SWITCHES

  • SYSTEM(R); 在 8.0.3 版本中添加(保留)

T

  • TABLE(R)

  • TABLES

  • TABLESPACE

  • TABLE_CHECKSUM

  • TABLE_NAME

  • TEMPORARY

  • TEMPTABLE

  • TERMINATED(R)

  • TEXT

  • THAN

  • THEN(R)

  • THREAD_PRIORITY; 在 8.0.3 版本中添加(非保留)

  • TIES; 在 8.0.2 版本中添加(非保留)

  • TIME

  • TIMESTAMP

  • TIMESTAMPADD

  • TIMESTAMPDIFF

  • TINYBLOB(R)

  • TINYINT(R)

  • TINYTEXT(R)

  • TLS; 在 8.0.21 版本中添加(非保留)

  • TO(R)

  • TRAILING(R)

  • TRANSACTION

  • TRIGGER(R)

  • TRIGGERS

  • TRUE(R)

  • TRUNCATE

  • TYPE

  • TYPES

U

  • UNBOUNDED; 在 8.0.2 版本中添加(非保留)

  • UNCOMMITTED

  • UNDEFINED

  • UNDO(R)

  • UNDOFILE

  • UNDO_BUFFER_SIZE

  • UNICODE

  • UNINSTALL

  • UNION(R)

  • UNIQUE(R)

  • UNKNOWN

  • UNLOCK(R)

  • UNREGISTER; 在 8.0.27 版本中添加(非保留)

  • UNSIGNED(R)

  • UNTIL

  • UPDATE(R)

  • UPGRADE

  • URL; added in 8.0.32 (nonreserved)

  • USAGE (R)

  • USE (R)

  • USER

  • USER_RESOURCES

  • USE_FRM

  • USING (R)

  • UTC_DATE (R)

  • UTC_TIME (R)

  • UTC_TIMESTAMP (R)

V

  • VALIDATION

  • VALUE

  • VALUES (R)

  • VARBINARY (R)

  • VARCHAR (R)

  • VARCHARACTER (R)

  • VARIABLES

  • VARYING (R)

  • VCPU; added in 8.0.3 (nonreserved)

  • VIEW

  • VIRTUAL (R)

  • VISIBLE

W

  • WAIT

  • WARNINGS

  • WEEK

  • WEIGHT_STRING

  • WHEN (R)

  • WHERE (R)

  • WHILE (R)

  • WINDOW (R); added in 8.0.2 (reserved)

  • WITH (R)

  • WITHOUT

  • WORK

  • WRAPPER

  • WRITE (R)

X

  • X509

  • XA

  • XID

  • XML

  • XOR (R)

Y

  • YEAR

  • YEAR_MONTH (R)

Z

  • ZEROFILL (R)

  • ZONE; added in 8.0.22 (nonreserved)

MySQL 8.0 新关键字和保留字

以下列表显示了与 MySQL 5.7 相比,在 MySQL 8.0 中新增的关键字和保留字。保留关键字标记为(R)。

A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | R | S | T | U | V | W | Z

A

  • ACTIVE

  • ADMIN

  • ARRAY

  • ATTRIBUTE

  • AUTHENTICATION

B

  • BUCKETS

  • BULK

C

  • CHALLENGE_RESPONSE

  • CLONE

  • COMPONENT

  • CUME_DIST (R)

D

  • DEFINITION

  • DENSE_RANK (R)

  • DESCRIPTION

E

  • EMPTY (R)

  • ENFORCED

  • ENGINE_ATTRIBUTE

  • EXCEPT (R)

  • EXCLUDE

F

  • FACTOR

  • FAILED_LOGIN_ATTEMPTS

  • FINISH

  • FIRST_VALUE (R)

  • FOLLOWING

G

  • GENERATE

  • GEOMCOLLECTION

  • GET_MASTER_PUBLIC_KEY

  • GET_SOURCE_PUBLIC_KEY

  • GROUPING (R)

  • GROUPS (R)

  • GTID_ONLY

H

  • HISTOGRAM

  • HISTORY

I

  • INACTIVE

  • INITIAL

  • INITIATE

  • INTERSECT (R)

  • INVISIBLE

J

  • JSON_TABLE (R)

  • JSON_VALUE

K

  • KEYRING

L

  • LAG (R)

  • LAST_VALUE (R)

  • LATERAL (R)

  • LEAD (R)

  • LOCKED

M

  • MASTER_COMPRESSION_ALGORITHMS

  • MASTER_PUBLIC_KEY_PATH

  • MASTER_TLS_CIPHERSUITES

  • MASTER_ZSTD_COMPRESSION_LEVEL

  • MEMBER

N

  • NESTED

  • NETWORK_NAMESPACE

  • NOWAIT

  • NTH_VALUE (R)

  • NTILE (R)

  • NULLS

O

  • OF (R)

  • OFF

  • OJ

  • OLD

  • OPTIONAL

  • ORDINALITY

  • ORGANIZATION

  • OTHERS

  • OVER (R)

P

  • PASSWORD_LOCK_TIME

  • PATH

  • PERCENT_RANK (R)

  • PERSIST

  • PERSIST_ONLY

  • PRECEDING

  • PRIVILEGE_CHECKS_USER

  • PROCESS

R

  • RANDOM

  • RANK (R)

  • RECURSIVE (R)

  • REFERENCE

  • REGISTRATION

  • REPLICA

  • REPLICAS

  • REQUIRE_ROW_FORMAT

  • RESOURCE

  • RESPECT

  • RESTART

  • RETAIN

  • RETURNING

  • REUSE

  • ROLE

  • ROW_NUMBER (R)

S

  • SECONDARY

  • SECONDARY_ENGINE

  • SECONDARY_ENGINE_ATTRIBUTE

  • SECONDARY_LOAD

  • SECONDARY_UNLOAD

  • SKIP

  • SOURCE_AUTO_POSITION

  • SOURCE_BIND

  • SOURCE_COMPRESSION_ALGORITHMS

  • SOURCE_CONNECT_RETRY

  • SOURCE_DELAY

  • SOURCE_HEARTBEAT_PERIOD

  • SOURCE_HOST

  • SOURCE_LOG_FILE

  • SOURCE_LOG_POS

  • SOURCE_PASSWORD

  • SOURCE_PORT

  • SOURCE_PUBLIC_KEY_PATH

  • SOURCE_RETRY_COUNT

  • SOURCE_SSL

  • SOURCE_SSL_CA

  • SOURCE_SSL_CAPATH

  • SOURCE_SSL_CERT

  • SOURCE_SSL_CIPHER

  • SOURCE_SSL_CRL

  • SOURCE_SSL_CRLPATH

  • SOURCE_SSL_KEY

  • SOURCE_SSL_VERIFY_SERVER_CERT

  • SOURCE_TLS_CIPHERSUITES

  • SOURCE_TLS_VERSION

  • SOURCE_USER

  • SOURCE_ZSTD_COMPRESSION_LEVEL

  • SRID

  • STREAM

  • SYSTEM (R)

T

  • THREAD_PRIORITY

  • TIES

  • TLS

U

  • UNBOUNDED

  • UNREGISTER

  • URL

V

  • VCPU

  • VISIBLE

W

  • WINDOW (R)

Z

  • ZONE

MySQL 8.0 Removed Keywords and Reserved Words

以下列表显示了与 MySQL 5.7 相比,在 MySQL 8.0 中被移除的关键字和保留字。保留关键字标记为(R)。

  • ANALYSE

  • DES_KEY_FILE

  • MASTER_SERVER_ID

  • PARSE_GCOL_EXPR

  • REDOFILE

  • SQL_CACHE

posted @ 2024-06-23 16:24  绝不原创的飞龙  阅读(8)  评论(0编辑  收藏  举报