MYSQL8-性能调优

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树的区别是:

  1. B树的节点中没有重复元素,B+树有。
  2. B树的中间节点会存储数据指针信息,而B+树只有叶子节点才存储。
  3. 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

主键索引(聚簇索引)

在这里插入图片描述

二级索引(辅助索引)

在这里插入图片描述

索引选择技巧

  1. 它应该是UNIQUE和NOT NULL
  2. 选择最小的可能键,因为所有二级索引都会存储主键。如果主键很大,整个索引也会占用更多的空间
  3. 选择一个单调递增的值。物理行是根据主键进行排序的。如果选择一个随机键,需要做多次的行重排,这会导致新跟那个下降。
  4. 最好选择一个主键。如果找不到任务主键,请添加一个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

主键索引(聚簇索引)

在这里插入图片描述

二级索引(辅助索引)

在这里插入图片描述

数据类型

表占用的存储空间越小,有以下三个优点:

  1. 向磁盘写入或读取的数据就越少,查询起来就越快
  2. 在处理查询时,磁盘上的内容会被加载到主内存中。所以,表越小,占用的主存空间就越小
  3. 被索引占用的空间就越小

建议操作

  1. 如果要存储员工编号,而其可能的最大值为500000,则最佳数据类型为mediumint(3个字节),如果将它存储为4个字节的int类型,则每一行都浪费了一个字节。
  2. 如果要存储员工姓名,由于其长度不等,可能的最大长度为20个字符,最好将其声明为varchar(20)类型。如果讲员工姓名定义为char(20)类型,但是只有几个人的姓名长为20个字符,其余的长度不到10个字符,就会浪费10个字符的空间。
  3. 在声明varchar类型时,要考虑其长度。尽快varchar类型在磁盘上进行了优化,但这个类型的数据被加载到内存时会占用全部的长度空间。

例如:将col存储在类型为varchar(255)的字段中,并且实际长度为10,则它在磁盘上占用10+1(用于存储长度)个字节,但是在内存中会占用全部的255个字节。

  1. 类型为varchar的字段,需要用2个字节来存储内容的实际长度。

在磁盘中如果实际长度小于255则使用1个字节来存放长度,超过255则使用2个字节。

  1. 如果不允许为空,则设置为NOT NULL。这样做可以避免了测试每个值是否为空的开销,并且还节省了存储空间-每列能节省一个字节。
  2. 如果字符串的长度是固定的,请存储为char而非varchar类型(因为varchar需要一个或两个字节来存储字符串的长度)
  3. 如果这些值是固定的,则使用ENUM而非varchar类型。只需要使用1或2个字节
  4. 优先选择整数类型而非字符串类型
  5. 优先利用前缀索引
    10.尝试利用InnoDB压缩

警告排查

使用 SHOW WARNINGS;

关注我

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章