explain
使用explain
用于分析sql语句的性能。案例分析:
mysql> show create table employees;
CREATE TABLE `employees` (
`id` int(11) NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) DEFAULT NULL,
`hire_date` date NOT NULL,
`card_no` char(18) NOT NULL,
PRIMARY KEY (`id`),
KEY `first_name` (`first_name`),
KEY `card_no` (`card_no`),
KEY `first_name2` (`first_name`,`last_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
正常格式
mysql> EXPLAIN SELECT * FROM employees WHERE id = 1;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
JSON格式
mysql> EXPLAIN FORMAT=JSON SELECT * FROM employees WHERE id = 1;
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1.00"
},
"table": {
"table_name": "employees",
"access_type": "const",
"possible_keys": [
"PRIMARY"
],
"key": "PRIMARY",
"used_key_parts": [
"id"
],
"key_length": "4",
"ref": [
"const"
],
"rows_examined_per_scan": 1,
"rows_produced_per_join": 1,
"filtered": "100.00",
"cost_info": {
"read_cost": "0.00",
"eval_cost": "0.20",
"prefix_cost": "0.00",
"data_read_per_join": "160"
},
"used_columns": [
"id",
"first_name",
"last_name",
"hire_date",
"card_no"
]
}
}
}
输出对照表
以下数据来自官网:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
Column | JSON Name | Meaning |
---|---|---|
id | select_id | The SELECT identifier |
select_type | None | The SELECT type |
table | table_name | The table for the output row |
partitions | partitions | The matching partitions |
type | access_type | The join type |
possible_keys | possible_keys | The possible indexes to choose |
key | key | The index actually chosen |
key_len | key_length | The length of the chosen key |
ref | ref | The columns compared to the index |
rows | rows | Estimate of rows to be examined |
filtered | filtered | Percentage of rows filtered by table condition |
Extra | None | Additional information |
JSON properties which are NULL are not displayed in JSON-formatted EXPLAIN output.
type(JSON Name: access_type)
当前查询的连接类型。性能从最佳到最差
- system:当前表中只有一条,const中的一种特例
- const:主键或唯一索引
- eq_ref:两表通过主键或唯一键关联
-- 官网案例
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;
-- 注意细节:结果项需要有索引才是eq_ref,否则是ALL
SELECT ref_table.key_column FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;
- ref:使用非唯一索引扫描
- fulltext:全文索引
- ref_or_null:使用非唯一索引或null扫描
- index_merge:合并索引
- unique_subquery:
- index_subquery
- range
- index
- ALL
MySql自带的全文索引只能用于数据库引擎为MYISAM的数据表,如果是其他数据引擎,则全文索引不会生效。
possible_keys(JSON Name: possible_keys)
可能使用的索引。
出现possible_keys有值,key显示NULL的情况,这种情况是因为表中数据不多,mysql认为索引对此查询帮助不大,选择了全表查询。要查看表具有哪些索引,请使用。 SHOW INDEX FROM tbl_name
key(JSON Name:key)
当前查询使用的索引。
key_len(JSON Name: key_length)
索引长度。
字段允许为NULL加1字节,变长数据类型加2字节。
- 字符串
长度和字符集有关,gbk(一个字符=2个字节),utf8(一个字符=3个字节),utf8mb4(一个字符=4个字节)
char(n):(2n/3n/4n)[+1] 字节
varchar(n):(2n/3n/n4)+2[+1] 字节
- 数值类型
tinyint:1字节
smallint:2字节
int:4字节
bigint:8字节
- 时间类型
date:3字节
timestamp:4字节
datetime:8字节
索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半 部分的字符提取出来做索引。
ref(JSON Name: ref)
在key列记录的索引中,表查找值所用到的列或常量,常见的有:const,字段名
rows(JSON Name: rows)
MySQL认为执行查询必须检查的行数。
filtered(JSON Name: filtered)
该filtered列指示将被表条件过滤的表行的估计百分比。
最大值为100,这表示未过滤行。值从100减小表示过滤量增加。 rows显示检查的估计行数,rows × filtered显示将与下表连接的行数。例如,如果 rows为1000且 filtered为50.00(50%),则与下表连接的行数为1000×50%= 500。
Extra (JSON Name:无)
这一列展示的是额外信息。常见的重要值如下:
- Using index:使用覆盖索引
- Using where:使用where语句来处理结果,查询的列未被索引覆盖
- Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围;
- Using temporary:mysql需要创建一张临时表来处理查询。
出现这种情况一般是要进行优化的,首先是想到用索引来优化。
- Using filesort:将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘 完成排序。
这种情况下一般也是要考虑使用索引来优化的。
- Select tables optimized away:使用某些聚合函数(比如max、min)来访问存在索引的某个字段
索引
关于索引介绍的内容参考博客: https://www.cnblogs.com/yuanrw/p/10225659.html
数据结构
B+Tree
下图为B+Tree存储的数据结构图:
- 非叶子节点不存放数据,只有叶子节点存放数据
- 每个叶子节点有一个指针指向下一个节点,把所有的叶子节点串在了一起
BTree
B+树和B树的区别是:
- B树的节点中没有重复元素,B+树有。
- B树的中间节点会存储数据指针信息,而B+树只有叶子节点才存储。
- B+树的每个叶子节点有一个指针指向下一个节点,把所有的叶子节点串在了一起。
哈希
哈希索引,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。如果多个列的哈希值相同,索引会以链表的方式存放多个指针记录到同一个哈希条目中。
因为索引自身只存储对应的哈希值,所以索引的结构十分紧凑,哈希索引查找的速度非常快。但是哈希索引也有它的限制:
- 哈希索引不是按照索引顺序存储的,无法用于排序。
- 不支持部分索引列匹配查找。
- 不支持范围查找。
索引类型
以下内容来自CSDN-杰哥一号号-mysql中innodb和myisam对比及索引原理区别
InnoDB索引实现
CREATE TABLE `employees` (
`Col1` int(11) NOT NULL,
`Col2` int(11) NOT NULL,
`Col3` varchar(16) NOT NULL,
PRIMARY KEY (`Col1`),
KEY `Col3` (`Col3`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
主键索引(聚簇索引)
二级索引(辅助索引)
索引选择技巧
- 它应该是UNIQUE和NOT NULL
- 选择最小的可能键,因为所有二级索引都会存储主键。如果主键很大,整个索引也会占用更多的空间
- 选择一个单调递增的值。物理行是根据主键进行排序的。如果选择一个随机键,需要做多次的行重排,这会导致新跟那个下降。
- 最好选择一个主键。如果找不到任务主键,请添加一个AUTO_INCREMENT列。如果你不选择任何内容,InnoDB会在内部生成一个带有6字节行ID的隐藏主键索引。
MyISAM索引实现(选读)
CREATE TABLE `employees` (
`Col1` int(11) NOT NULL,
`Col2` int(11) NOT NULL,
`Col3` varchar(16) NOT NULL,
PRIMARY KEY (`Col1`),
KEY `Col2` (`Col2`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8
主键索引(聚簇索引)
二级索引(辅助索引)
数据类型
表占用的存储空间越小,有以下三个优点:
- 向磁盘写入或读取的数据就越少,查询起来就越快
- 在处理查询时,磁盘上的内容会被加载到主内存中。所以,表越小,占用的主存空间就越小
- 被索引占用的空间就越小
建议操作
- 如果要存储员工编号,而其可能的最大值为500000,则最佳数据类型为mediumint(3个字节),如果将它存储为4个字节的int类型,则每一行都浪费了一个字节。
- 如果要存储员工姓名,由于其长度不等,可能的最大长度为20个字符,最好将其声明为varchar(20)类型。如果讲员工姓名定义为char(20)类型,但是只有几个人的姓名长为20个字符,其余的长度不到10个字符,就会浪费10个字符的空间。
- 在声明varchar类型时,要考虑其长度。尽快varchar类型在磁盘上进行了优化,但这个类型的数据被加载到内存时会占用全部的长度空间。
例如:将col存储在类型为varchar(255)的字段中,并且实际长度为10,则它在磁盘上占用10+1(用于存储长度)个字节,但是在内存中会占用全部的255个字节。
- 类型为varchar的字段,需要用2个字节来存储内容的实际长度。
在磁盘中如果实际长度小于255则使用1个字节来存放长度,超过255则使用2个字节。
- 如果不允许为空,则设置为NOT NULL。这样做可以避免了测试每个值是否为空的开销,并且还节省了存储空间-每列能节省一个字节。
- 如果字符串的长度是固定的,请存储为char而非varchar类型(因为varchar需要一个或两个字节来存储字符串的长度)
- 如果这些值是固定的,则使用ENUM而非varchar类型。只需要使用1或2个字节
- 优先选择整数类型而非字符串类型
- 优先利用前缀索引
10.尝试利用InnoDB压缩
警告排查
使用 SHOW WARNINGS;