方便後续解释概念,我们在创建一个跟原先single_table一样的表,叫single_table2(s2)。
mysql> create table single_table2( -> id int not null auto_increment,
-> key1 varchar(10),
-> key2 int,
-> key3 varchar(100),
-> key_part1 varchar(100),
-> key_part2 varchar(100),
-> key_part3 varchar(100),
-> common_field varchar(100),
-> primary key (id),
-> key idx_key1 (key1),
-> unique key uk_key2 (key2),
-> key idx_key3 (key3),
-> key idx_key_part(key_part1, key_part2, key_part3)
-> ) engine=InnoDB charset=utf8;
Query OK, 0 rows affected, 1 warning (0.44 sec)
首先要知道什麽是条件过滤?
我们已经知道MySQL中连接查询是巢状结构回圈,驱动表会被存取一次,被驱动表会被存取多次。
因此成本就是
我们把查询驱动表後的纪录笔数称为驱动表的扇出(fanout)。
有时候扇出值很好计算
select * from s1 inner join s2;
很明显这是全表资料扫描,驱动表有多少纪录,扇出值就是多少,以我们前文的数字就是10146
有时候没那麽容易知道
select * from s1 inner join s2
where s1.commom_field > 'xyz';
多了一个条件s1.commom_field > 'xyz',但由於最佳化器并不会真的去执行查询,所以我们只能猜测有多少笔纪录符合这条件,而这个猜测的过程也就是条件过滤。
连接查询总成本=单次存取驱动的成本+驱动表扇出值x单次存取被驱动表的成本。
对於左外连接和右外连接,驱动表与被驱动表是固定的,所以会单纯直接计算成本最低的方式即可。
但对於内连接来说,驱动表与被驱动表的位置是可以互换的,因此需要考虑当不同的表做驱动表时,成本最低的连接顺序是那一个。
再来实际运行的时候,比重较大的是驱动表扇出值x单次存取被驱动表的成本,所以我们要尽可能的降低这两个值,
像是在被驱动表中建立索引,如果可以,被驱动表的连接列最好是该表的主键或是唯一二级索引,这样就更快了。
对於n表连接会有nx(n-1)x(n-2)x...x1种的顺序,也就是n阶的连接顺序。
而这麽多种变化一一计算成本想来就是个费时的工程,因此有一些优化的方式。
前文介绍了两个成本常数:
mysql> show tables from mysql like '%cost%';
+--------------------------+
| Tables_in_mysql (%cost%) |
+--------------------------+
| engine_cost |
| server_cost |
+--------------------------+
2 rows in set (0.46 sec)
前文提到再执行一笔叙述时,分为两个部分server层跟储存引擎层,server层负责连接管理、查询快取、语法解析、查询最佳化等,而储存引擎层才是具体执行操作的地方,server层的成本常数都存在server_cost,而储存引擎层则存在engine_cost。
接下来我们分别来看看这两个表有那些成本常数
servercost
mysql> select * from mysql.server_cost;
+------------------------------+------------+---------------------+---------+---------------+
| cost_name | cost_value | last_update | comment | default_value |
+------------------------------+------------+---------------------+---------+---------------+
| disk_temptable_create_cost | NULL | 2018-07-17 11:08:22 | NULL | 20 |
| disk_temptable_row_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.5 |
| key_compare_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.05 |
| memory_temptable_create_cost | NULL | 2018-07-17 11:08:22 | NULL | 1 |
| memory_temptable_row_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.1 |
| row_evaluate_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.1 |
+------------------------------+------------+---------------------+---------+---------------+
6 rows in set (0.26 sec)
disk_temptable_create_cost:基於磁碟的临时表成本
disk_temptable_row_cost:向基於磁碟的临时表存取一笔纪录的成本
key_compare_cost:两笔纪录比较操作的成本,多用在排序操作上
memory_temptable_create_cost:基於记忆体的临时表成本
memory_temptable_row_cost:向基於记忆体的临时表存取一笔纪录的成本
row_evaluate_cost:读取并检测一笔纪录是否符合搜索条件的成本
在执行语法像是distinct子句、group by子句、union子句,mysql都有可能在内部创建一个临时表来辅助完成查询。资料量大的时候可能创建基於磁碟的临时表(使用myisam,innodb等引擎),而资料不多时可能创建基於记忆体的临时表(使用memory引擎)。
这些常数预设值都是null,想要更新数值的话如下:
mysql> update mysql.server_cost set cost_value=0.4 where cost_name='row_evaluate_cost';
Query OK, 1 row affected (0.54 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> flush optimizer_costs;
Query OK, 0 rows affected (0.57 sec)
mysql> select * from mysql.server_cost;
+------------------------------+------------+---------------------+---------+---------------+
| cost_name | cost_value | last_update | comment | default_value |
+------------------------------+------------+---------------------+---------+---------------+
| disk_temptable_create_cost | NULL | 2018-07-17 11:08:22 | NULL | 20 |
| disk_temptable_row_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.5 |
| key_compare_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.05 |
| memory_temptable_create_cost | NULL | 2018-07-17 11:08:22 | NULL | 1 |
| memory_temptable_row_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.1 |
| row_evaluate_cost | 0.4 | 2021-09-27 08:42:34 | NULL | 0.1 |
+------------------------------+------------+---------------------+---------+---------------+
6 rows in set (0.00 sec)
enginecost
mysql> select * from mysql.engine_cost;
+-------------+-------------+------------------------+------------+---------------------+---------+---------------+
| engine_name | device_type | cost_name | cost_value | last_update | comment | default_value |
+-------------+-------------+------------------------+------------+---------------------+---------+---------------+
| default | 0 | io_block_read_cost | NULL | 2018-07-17 11:08:22 | NULL | 1 |
| default | 0 | memory_block_read_cost | NULL | 2018-07-17 11:08:22 | NULL | 0.25 |
+-------------+-------------+------------------------+------------+---------------------+---------+---------------+
2 rows in set (0.03 sec)
这边特别说明一个列engine_name,表示成本常数适用的储存引擎名称,default表示都通用。
io_block_read_cost:从磁碟上读取一个区块对应的成本(对innodb来说,一页就是一个区块,不过对myisam来说预设为4096位元组作为一个区块)
memory_block_read_cost:跟前面类似,只是变成从记忆体读取。
一样可以透过跟前述一样的方式更新成本常数,也可以透过新增的方式来插入只符合特定引擎的成本常数。
耶~take a break
<<: Day 21 - SwiftUI开发学习5(文字填入)
当了解完网格容器的属性後,接下来要进入网格项目的部分,如果你还不了解什麽是网格容器及网格环境,可以回...
这是 Roblox 从零开始系列,游戏环境章节的第二个单元,你将学会如何利用大气效果让游戏变得更加真...
Bootstrap已是目前全球被大量网页开发者使用的一个网页UI框架了,其特色在於使用简单,开发快速...
前言 该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系...
最後一天复习一下 Django是MTV架构 MTV架构分别是model、Template、views...