### 8.9.5 优化器成本模型

[](<>)[](<>)

为了生成执行计划，优化器使用基于查询执行期间发生的各种操作的成本估计的成本模型。优化器有一组内置的默认“成本常量”可供它用来做出有关执行计划的决策。

优化器还有一个成本估算数据库，可在执行计划构建期间使用。这些估计值存储在`server_cost`和`引擎成本`中的表`mysql`系统数据库，可随时配置。这些表的目的是使优化器在尝试达到查询执行计划时使用的成本估计值可以轻松调整。

-   [成本模型一般操作](cost-model.html#cost-model-operation)

-   [成本模型数据库](cost-model.html#cost-model-database)

-   [更改成本模型数据库](cost-model.html#cost-model-database-modifications)

#### 成本模型一般操作

可配置的优化器成本模型是这样工作的：

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

-   在运行时，服务器可能会重新读取成本表。这发生在动态加载存储引擎或[`FLUSH OPTIMIZER_COSTS`](flush.html#flush-optimizer-costs)语句被执行。

-   成本表使服务器管理员能够通过更改表中的条目轻松调整成本估算。通过将条目的成本设置为`空值`.优化器使用内存中的成本值，因此对表的更改应遵循[`FLUSH OPTIMIZER_COSTS`](flush.html#flush-optimizer-costs)生效。

-   客户端会话开始时的当前内存中成本估计适用于整个会话，直到它结束。特别是，如果服务器重新读取成本表，则任何更改的估计仅适用于随后启动的会话。现有会话不受影响。

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

#### 成本模型数据库

优化器成本模型数据库由两个表组成`mysql`包含查询执行期间发生的操作的成本估算信息的系统数据库：

-   [](<>) [](<>) `server_cost`：一般服务器操作的优化器成本估算

-   [](<>) [](<>) `引擎成本`：针对特定存储引擎的特定操作的优化器成本估算

    这`server_cost`表包含以下列：

-   `成本名称`

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

-   `成本价值`

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

    覆盖默认成本估算（对于指定`空值`)，将成本设置为非`空值`价值。要恢复为默认值，请将值设置为`空值`.然后执行[`FLUSH OPTIMIZER_COSTS`](flush.html#flush-optimizer-costs)告诉服务器重新读取成本表。

-   `最后更新`

    最后一行更新的时间。

-   `评论`

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

-   `默认值`

    成本估算的默认（编译）值。此列是只读生成的列，即使关联的成本估算发生更改，它也会保留其值。对于在运行时添加到表中的行，该列的值为`空值`.

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

    服务器识别这些`成本名称`的值`server_cost`桌子：

-   `disk_temptable_create_cost`,`disk_temptable_row_cost`

    存储在基于磁盘的存储引擎中的内部创建的临时表的成本估算（无论是`InnoDB`要么`MyISAM`）。增加这些值会增加使用内部临时表的成本估算，并使优化器更喜欢使用较少的查询计划。有关此类表的信息，请参阅[第 8.4.4 节，“MySQL 中的内部临时表使用”](internal-temporary-tables.html).

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

-   `key_compare_cost`

    [](<>)[](<>)

    比较记录键的成本。增加此值会导致比较许多键的查询计划变得更加昂贵。例如，执行一个查询计划`文件排序`与避免使用索引进行排序的查询计划相比，它变得相对昂贵。

-   `memory_temptable_create_cost`,`memory_temptable_row_cost`

    内部创建的临时表的成本估算存储在`记忆`存储引擎。增加这些值会增加使用内部临时表的成本估算，并使优化器更喜欢使用较少的查询计划。有关此类表的信息，请参阅[第 8.4.4 节，“MySQL 中的内部临时表使用”](internal-temporary-tables.html).

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

-   `row_evaluate_cost`

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

    这`引擎成本`表包含以下列：

-   `引擎名称`

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

-   `设备类型`

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

-   `成本名称`

    与中相同`server_cost`桌子。

-   `成本价值`

    与中相同`server_cost`桌子。

-   `最后更新`

    与中相同`server_cost`桌子。

-   `评论`

    与中相同`server_cost`桌子。

-   `默认值`

    成本估算的默认（编译）值。此列是只读生成的列，即使关联的成本估算发生更改，它也会保留其值。对于在运行时添加到表中的行，该列的值为`空值`，除了如果该行具有相同的`成本名称`值作为原始行之一，`默认值`列与该行具有相同的值。

    的主键`引擎成本`table 是一个包含 (`成本名称`,`引擎名称`,`设备类型`) 列，因此无法为这些列中的任何值组合创建多个条目。

    服务器识别这些`成本名称`的值`引擎成本`桌子：

-   `io_block_read_cost`

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

-   `memory_block_read_cost`

    相似`io_block_read_cost`，但表示从内存数据库缓冲区中读取索引或数据块的成本。

    如果`io_block_read_cost`和`memory_block_read_cost`值不同时，执行计划可能会在同一查询的两次运行之间发生变化。假设内存访问的成本小于磁盘访问的成本。在这种情况下，在将数据读入缓冲池之前的服务器启动时，您可能会获得与运行查询后不同的计划，因为那时数据在内存中。

#### 更改成本模型数据库

对于希望从默认值更改成本模型参数的 DBA，请尝试将值加倍或减半并衡量效果。

的变化`io_block_read_cost`和`memory_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;
```

这个例子展示了如何改变`io_block_read_cost`只为`InnoDB`存储引擎：

```
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;
```
