一条sql在MySql中的执行,要经历分析器、优化器、执行器。
这条sql走不走索引、走哪个索引,是在优化器中进行的,sql优化器会列出这条sql的所有可能的执行计划,会根据一定的规则,来对每一个执行计划来进行分析,最后决定选择哪个执行计划,是通过最后的计算成本来得到的。
SQL优化器将分析所有可能的执行计划,并选择成本最低的执行。这个优化器被称为CBO(基于成本的优化器)。
在 MySQL中,一条 SQL 的计算成本计算,很好理解,就是访问数据库(数据库页、磁盘)+处理数据。
CPU成本,表示计算成本,例如索引键值的比较、记录值的比较和结果集的排序。这些操作都在服务器层完成
IO成本,表示引擎级IO的成本,MySQL 8.0可以通过区分表的数据是否在内存中来分别计算读取内存IO和磁盘IO的成本。
Cost = Server Cost + Engine Cost = CPU Cost + IO Cost
MySQL优化器认为:
如果一段SQL需要创建一个基于磁盘的临时表,此时成本最大,是基于内存的临时表的20倍。比较索引键值和记录的成本很低,但如果要比较的记录很多,成本就会非常大。
从磁盘读取的开销是内存开销的 4 倍(成本不是一成不变的会根据硬件变化)。
查看sql的运行成本:
EXPLAIN FORMAT=json select ...
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1.41"
},
"table": {
"table_name": "t",
"access_type": "range",
"possible_keys": [
"idx_a_b"
],
"key": "idx_a_b",
"used_key_parts": [ // 使用了索引的键
"a"
],
"key_length": "5",
"rows_examined_per_scan": 2,
"rows_produced_per_join": 1,
"filtered": "50.00",
"index_condition": "((`testdb`.`t`.`b` = 2) and (`testdb`.`t`.`a` <> 2))",
"cost_info": {
"read_cost": "1.31", // 表示从InnoDB存储引擎读取的成本
"eval_cost": "0.10", // 表示服务器层的CPU成本
"prefix_cost": "1.41", // 表示SQL的总成本
"data_read_per_join": "16" // 表示读取记录中的字节总数
},
"used_columns": [
"a",
"b",
"c"
]
}
}
}
ead_cost
表示从InnoDB存储引擎读取的成本;
eval_cost
表示服务器层的CPU成本;
prefix_cost
表示SQL的总成本;
data_read_per_join
表示读取记录中的字节总数。
MySql的索引,是通过采样统计的方法来得到的
InnoDB 默认会选择 N 个数据页,统计这些页面上的不同值,得到一个平均值,然后乘以这个索引的页面数,就得到了这个索引的基数。
而数据表是会持续更新的,索引统计信息也不会固定不变。所以,当变更的数据行数超过 1/M 的时候,会自动触发重新做一次索引统计。
在 MySQL 中,有两种存储索引统计的方式,可以通过设置参数 innodb_stats_persistent 的值来选择:
由于是采样统计,所以不管 N 是 20 还是 8,这个基数都是很容易不准的,但是大体上差不多。
参考:
https://m.php.cn/faq/497571.html
https://aiguangyuan.blog.csdn.net/article/details/109664294