MySQL Cost-Based Optimizer

本文详细介绍了MySQL的成本估算模型,包括I/O操作消耗(io_cost)、CPU操作消耗(cpu_cost)等不同类型的成本,并深入探讨了io_cost的具体计算方法,如表扫描、索引扫描和读取成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MySQL Cost-Based Optimizer 模型

MySQL Cost_estimate 定义了多种消耗类型

1. io_cost cost of I/O operations IO操作消耗
2. cpu_cost cost of CPU operations CPU操作消耗
3. import_cost cost of remote operations 远程操作消耗
4. mem_cost memory used (bytes) 内存消耗


KEY_COMPARE_COST= 0.1
MEMORY_TEMPTABLE_CREATE_COST= 2.0
MEMORY_TEMPTABLE_ROW_COST= 0.2
DISK_TEMPTABLE_CREATE_COST= 40.0
DISK_TEMPTABLE_ROW_COST= 1.0
ROW_EVALUATE_COST= 0.2
MEMORY_BLOCK_READ_COST= 1.0
IO_BLOCK_READ_COST= 1.0


io_block_read_cost 
memory_block_read_cost

disk_temptable_create_cost
disk_temptable_row_cost
key_compare_cost
memory_temptable_create_cost
memory_temptable_row_cost
row_evaluate_cost

MySQL目前版本只使用了 io_cost,cpu_cost

io_cost 定义了以下三种方法: 表扫、索引扫、读取消耗

Cost_estimate handler::table_scan_cost()
{
/*
This function returns a Cost_estimate object. The function should be
implemented in a way that allows the compiler to use "return value
optimization" to avoid creating the temporary object for the return value
and use of the copy constructor.
*/

const double io_cost= scan_time() * table->cost_model()->page_read_cost(1.0);
Cost_estimate cost;
cost.add_io(io_cost);
return cost;
}

Cost_estimate handler::index_scan_cost(uint index, double ranges, double rows)
{
/*
This function returns a Cost_estimate object. The function should be
implemented in a way that allows the compiler to use "return value
optimization" to avoid creating the temporary object for the return value
and use of the copy constructor.
*/

DBUG_ASSERT(ranges >= 0.0);
DBUG_ASSERT(rows >= 0.0);

const double io_cost= index_only_read_time(index, rows) *
table->cost_model()->page_read_cost_index(index, 1.0);
Cost_estimate cost;
cost.add_io(io_cost);
return cost;
}

Cost_estimate handler::read_cost(uint index, double ranges, double rows)
{
/*
This function returns a Cost_estimate object. The function should be
implemented in a way that allows the compiler to use "return value
optimization" to avoid creating the temporary object for the return value
and use of the copy constructor.
*/

DBUG_ASSERT(ranges >= 0.0);
DBUG_ASSERT(rows >= 0.0);

const double io_cost= read_time(index, static_cast<uint>(ranges),
static_cast<ha_rows>(rows)) *
table->cost_model()->page_read_cost(1.0);
Cost_estimate cost;
cost.add_io(io_cost);
return cost;
}

cpu_cost

MySQL Cost-Based Optimizer 模型

MySQL Cost_estimate 定义了多种消耗类型

1. io_cost cost of I/O operations IO操作消耗
2. cpu_cost cost of CPU operations CPU操作消耗
3. import_cost cost of remote operations 远程操作消耗
4. mem_cost memory used (bytes) 内存消耗


KEY_COMPARE_COST= 0.1
MEMORY_TEMPTABLE_CREATE_COST= 2.0
MEMORY_TEMPTABLE_ROW_COST= 0.2
DISK_TEMPTABLE_CREATE_COST= 40.0
DISK_TEMPTABLE_ROW_COST= 1.0
ROW_EVALUATE_COST= 0.2
MEMORY_BLOCK_READ_COST= 1.0
IO_BLOCK_READ_COST= 1.0


io_block_read_cost 
memory_block_read_cost

disk_temptable_create_cost
disk_temptable_row_cost
key_compare_cost
memory_temptable_create_cost
memory_temptable_row_cost
row_evaluate_cost

MySQL目前版本只使用了 io_cost,cpu_cost

io_cost 定义了以下三种方法: 表扫、索引扫、读取消耗

Cost_estimate handler::table_scan_cost()
{
/*
This function returns a Cost_estimate object. The function should be
implemented in a way that allows the compiler to use "return value
optimization" to avoid creating the temporary object for the return value
and use of the copy constructor.
*/

const double io_cost= scan_time() * table->cost_model()->page_read_cost(1.0);
Cost_estimate cost;
cost.add_io(io_cost);
return cost;
}

Cost_estimate handler::index_scan_cost(uint index, double ranges, double rows)
{
/*
This function returns a Cost_estimate object. The function should be
implemented in a way that allows the compiler to use "return value
optimization" to avoid creating the temporary object for the return value
and use of the copy constructor.
*/

DBUG_ASSERT(ranges >= 0.0);
DBUG_ASSERT(rows >= 0.0);

const double io_cost= index_only_read_time(index, rows) *
table->cost_model()->page_read_cost_index(index, 1.0);
Cost_estimate cost;
cost.add_io(io_cost);
return cost;
}

Cost_estimate handler::read_cost(uint index, double ranges, double rows)
{
/*
This function returns a Cost_estimate object. The function should be
implemented in a way that allows the compiler to use "return value
optimization" to avoid creating the temporary object for the return value
and use of the copy constructor.
*/

DBUG_ASSERT(ranges >= 0.0);
DBUG_ASSERT(rows >= 0.0);

const double io_cost= read_time(index, static_cast<uint>(ranges),
static_cast<ha_rows>(rows)) *
table->cost_model()->page_read_cost(1.0);
Cost_estimate cost;
cost.add_io(io_cost);
return cost;
}

转载于:https://www.cnblogs.com/hyming011/p/8251784.html

<think>我们正在解决的是MySQL启动时出现的"Failed to open optimizer cost constant tables"错误。这个错误通常与系统表(特别是mysql系统数据库中的表)的损坏或缺失有关。根据引用[1]和[2]中的错误信息,我们注意到之前有关于mysql.plugin和mysql.user表不存在的错误,这些错误与当前问题属于同一类问题,即系统表的问题。 ### 问题分析 "Failed to open optimizer cost constant tables"错误表明MySQL在启动时无法打开优化器成本常量表。这些表存储在mysql系统数据库中,包括`server_cost`和`engine_cost`表(在MySQL 8.0中引入)。可能的原因包括: 1. **系统表损坏**:mysql数据库中的表(如`server_cost`、`engine_cost`)损坏。 2. **权限问题**:MySQL服务账户没有权限访问这些表。 3. **数据目录损坏**:由于异常关机或磁盘问题导致数据目录损坏。 4. **升级问题**:从旧版本升级到MySQL 8.0时,系统表未正确升级。 ### 解决方案 我们将按照以下步骤进行修复。注意:在执行任何操作之前,请务必备份MySQL数据目录(通常为/var/lib/mysql)。 #### 步骤1: 停止MySQL服务 ```bash sudo systemctl stop mysql ``` #### 步骤2: 备份数据目录 ```bash sudo cp -r /var/lib/mysql /var/lib/mysql_backup_$(date +%F) ``` #### 步骤3: 尝试修复系统表 由于错误涉及优化器成本常量表,我们可以尝试重建mysql系统数据库。有两种方法: **方法A: 自动重建系统表(推荐)** 1. 删除现有的mysql系统数据库(注意:这不会影响用户数据库,但会重置权限等系统设置): ```bash sudo rm -rf /var/lib/mysql/mysql ``` 2. 重新初始化系统表: ```bash sudo mysqld --initialize-insecure --user=mysql ``` 注意:`--initialize-insecure`会创建一个空密码的root账户,启动后需要立即设置密码。 3. 修复权限: ```bash sudo chown -R mysql:mysql /var/lib/mysql ``` **方法B: 手动重建特定表(如果不想重建整个mysql数据库)** 如果错误仅限于优化器成本常量表(`server_cost`和`engine_cost`),我们可以尝试只重建这两张表。 1. 启动MySQL跳过权限检查: ```bash sudo mysqld_safe --skip-grant-tables --skip-networking & ``` 2. 连接到MySQL服务器: ```bash mysql -u root ``` 3. 在MySQL命令行中,切换到mysql数据库,并尝试重建表: ```sql USE mysql; -- 删除损坏的表(如果存在) DROP TABLE IF EXISTS server_cost; DROP TABLE IF EXISTS engine_cost; -- 重新创建表 CREATE TABLE server_cost ( cost_name varchar(64) NOT NULL, cost_value float DEFAULT NULL, last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, comment varchar(1024) DEFAULT NULL, PRIMARY KEY (cost_name) ) ENGINE=InnoDB; CREATE TABLE engine_cost ( engine_name varchar(64) NOT NULL, device_type int NOT NULL, cost_name varchar(64) NOT NULL, cost_value float DEFAULT NULL, last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, comment varchar(1024) DEFAULT NULL, PRIMARY KEY (cost_name, engine_name, device_type) ) ENGINE=InnoDB; -- 插入默认数据 INSERT INTO server_cost (cost_name, cost_value, comment) VALUES ('disk_temptable_create_cost', NULL, 'Cost of creating a temporary table in a disk-based storage (InnoDB or MyISAM)'), ('disk_temptable_row_cost', NULL, 'Cost of accessing a row in a disk-based temporary table'), ('key_compare_cost', NULL, 'Cost of comparing record keys'), ('memory_temptable_create_cost', NULL, 'Cost of creating a temporary table in a MEMORY storage engine'), ('memory_temptable_row_cost', NULL, 'Cost of accessing a row in a MEMORY temporary table'), ('row_evaluate_cost', NULL, 'Cost of evaluating record conditions'); INSERT INTO engine_cost (engine_name, device_type, cost_name, cost_value, comment) VALUES ('default', 0, 'io_block_read_cost', NULL, 'Cost of reading an index or data block from disk for InnoDB'), ('default', 0, 'memory_block_read_cost', NULL, 'Cost of reading an index or data block from memory for InnoDB'); FLUSH PRIVILEGES; EXIT; ``` 4. 停止临时实例: ```bash sudo mysqladmin -u root shutdown ``` #### 步骤4: 启动MySQL并验证 ```bash sudo systemctl start mysql sudo systemctl status mysql tail -f /var/log/mysql/error.log # 查看错误日志,确认问题是否解决 ``` #### 步骤5: 重置root密码(如果使用了--initialize-insecure) ```bash mysql -u root --skip-password ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewPassword'; ``` #### 步骤6: 运行mysql_upgrade(可选,但推荐) 在MySQL 8.0中,`mysql_upgrade`命令已被废弃,取而代之的是在启动时自动执行升级。但如果你使用的是旧版本升级到8.0,或者仍然遇到问题,可以尝试: ```bash sudo mysql_upgrade -u root -p ``` ### 注意事项 1. **权限问题**:确保MySQL数据目录的所有权和权限正确: ```bash sudo chown -R mysql:mysql /var/lib/mysql sudo find /var/lib/mysql -type d -exec chmod 750 {} \; ``` 2. **SELinux**:如果系统启用了SELinux,可能需要恢复上下文: ```bash sudo restorecon -Rv /var/lib/mysql # CentOS/RHEL ``` 3. **数据恢复**:如果修复后用户数据库不可见,可以从备份中复制用户数据库目录到/var/lib/mysql下,并确保权限正确。 4. **InnoDB恢复**:如果问题是由于InnoDB损坏,可以尝试在my.cnf中添加: ```ini [mysqld] innodb_force_recovery = 1 # 从1到6,逐步尝试,成功启动后立即备份数据 ``` 注意:此模式是只读的,用于紧急恢复。 ### 根本原因 优化器成本常量表(`server_cost`和`engine_cost`)是MySQL 8.0中用于优化查询执行计划的系统表。这些表的损坏或缺失会导致MySQL无法启动,因为优化器需要这些信息来生成执行计划。 ### 相关问题 §§相关问题§§ 1. MySQL 8.0启动时出现"Table 'mysql.component' doesn't exist"错误如何解决? 2. 如何备份和恢复MySQL系统表? 3. MySQL 8.0中如何重置root密码? 4. 使用`mysqld --initialize`初始化数据目录时有哪些注意事项?</think>### MySQL启动失败:无法打开优化器成本常量表 (Failed to open optimizer cost constant tables) 解决方案 #### 问题分析 此错误通常发生在以下场景: 1. **系统表损坏**:`mysql`系统数据库中的优化器成本表(`server_cost`、`engine_cost`)损坏或丢失[^1] 2. **升级问题**:从MySQL 5.7升级到8.0时,系统表未正确迁移 3. **权限异常**:MySQL服务账户无权访问系统表文件 4. **数据目录损坏**:异常关机或磁盘错误导致文件损坏 5. **初始化失败**:首次安装时系统表未正确创建 错误日志典型表现: ``` [ERROR] [MY-010952] Failed to open optimizer cost constant tables [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it[^1] ``` --- ### 解决方案(分步操作) #### 步骤1:停止MySQL服务 ```bash sudo systemctl stop mysql ``` #### 步骤2:备份数据目录(关键!) ```bash sudo cp -r /var/lib/mysql /var/lib/mysql_backup_$(date +%F) ``` #### 步骤3:修复优化器成本表(两种方法) **方法A:自动重建系统表(推荐)** ```bash # 1. 删除损坏的系统表 sudo rm -f /var/lib/mysql/mysql/server_cost.* sudo rm -f /var/lib/mysql/mysql/engine_cost.* # 2. 重建系统表 sudo mysqld --initialize-insecure --user=mysql # 3. 修复权限 sudo chown -R mysql:mysql /var/lib/mysql ``` **方法B:手动重建成本表(当自动方法失效时)** ```bash # 1. 启动MySQL跳过权限检查 sudo mysqld_safe --skip-grant-tables --skip-networking & # 2. 连接MySQL mysql -u root # 3. 在MySQL命令行重建成本表 > USE mysql; > CREATE TABLE IF NOT EXISTS server_cost ( cost_name VARCHAR(64) NOT NULL PRIMARY KEY, cost_value FLOAT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, comment VARCHAR(1024) ) ENGINE=InnoDB; > CREATE TABLE IF NOT EXISTS engine_cost ( engine_name VARCHAR(64) NOT NULL, device_type INTEGER NOT NULL, cost_name VARCHAR(64) NOT NULL, cost_value FLOAT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, comment VARCHAR(1024), PRIMARY KEY (cost_name, engine_name, device_type) ) ENGINE=InnoDB; > INSERT INTO server_cost (cost_name) VALUES ('disk_temptable_create_cost'), ('disk_temptable_row_cost'), ('key_compare_cost'), ('memory_temptable_create_cost'), ('memory_temptable_row_cost'), ('row_evaluate_cost'); > INSERT INTO engine_cost (engine_name, device_type, cost_name) VALUES ('default', 0, 'io_block_read_cost'), ('default', 0, 'memory_block_read_cost'); > FLUSH PRIVILEGES; > EXIT; # 4. 停止临时实例 sudo mysqladmin -u root shutdown ``` #### 步骤4:启动并验证服务 ```bash sudo systemctl start mysql sudo systemctl status mysql # 检查错误日志 tail -f /var/log/mysql/error.log ``` #### 步骤5:重置root密码(如果使用--initialize-insecure) ```bash mysql -u root --skip-password > ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewPassword'; ``` --- ### 关键注意事项 1. **文件权限修复**: ```bash sudo chown -R mysql:mysql /var/lib/mysql sudo find /var/lib/mysql -type d -exec chmod 750 {} \; ``` 2. **SELinux问题处理**: ```bash sudo restorecon -Rv /var/lib/mysql # CentOS/RHEL系统 ``` 3. **数据恢复**: - 从备份复制用户数据库: ```bash sudo cp -r /var/lib/mysql_backup_*/your_database /var/lib/mysql/ ``` - 运行表修复工具: ```bash sudo mysql_upgrade -u root -p ``` 4. **InnoDB恢复模式**(仅限紧急情况): 在`my.cnf`中添加: ```ini [mysqld] innodb_force_recovery = 1 # 值从1-6逐步尝试[^4] ``` 成功启动后立即备份数据并移除该配置 --- ### 根本原因解析 优化器成本常量表存储查询优化的关键参数: 1. MySQL 8.0引入`server_cost`和`engine_cost`表管理执行计划成本 2. 这些表位于`mysql`系统数据库中 3. 启动时优化器需要加载这些表计算查询成本 4. 表损坏或缺失会导致启动失败 > **数学表达**:优化器成本计算公式为: > $$ \text{Total Cost} = \sum (\text{operation cost} \times \text{row estimate}) $$ > 其中操作成本从成本常量表获取[^1] --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值