Bootstrap

MySQ索引优化

MySQ索引优化

## MySQL整体架构

​ mysql是一个优秀的数据库,其最大的优点就是基于热插拔方式的存储引擎,可以根据需要切换,同时也可以对其进行修改,从总体结构来看,也可以把其分为连接层,服务层,引擎层层和存储层,我们平时编写sql就是再服务层。

image-20201025220719906

各个层的功能如下

image-20201025220750752

mysql引擎很多,最主要的是innodb和myisam,其主要区别如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5iTBXGem-1640514810838)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201025221033854.png)]

MySQL索引

为什么需要索引

​ mysql的数据是存在磁盘上的的,其io性能很差,另外对于关系型数据库来说,在大数量的情况下,最好的情况是依次就能查到目标值,最坏需要全表扫描,平均来说查找时间还是很长,因此为了能够快速查找数据,因此需要建立索引,索引是一个以二叉树为基础的多叉有序树(B和B+树),由于采用二分查找算法,依次每次都能排除一般的数据,查询高效。

MySQL索引分类

mysql常见索引如下以及相关特性:

​ 唯一索引:该索引字段值唯一。

​ 单值索引:该索引这设置在一个字段上。

​ 组合索引(复合索引):由多个字段组成的索引。

​ 覆盖索引:查出的字段刚好和索引字段对应。

​ 聚集索引:叶子节点存储数据(B+树)。

​ 非聚集索引:叶子节点不存储数据(B树)

索引建立原则

  • 主键和外键需要建立索引。
  • 有一定数据量的情况下才需要建立索引(例如300达到300万)
  • 业务中经常用来查询的字段需要建立索引。
  • 经常更新的字段上不宜建立索引,因为需要重新构建索引树。
  • 数据重复太多的字段没必要建立索引,例如字典项。

SQL的解析顺序

正常来说我们编写的sql如下

image-20201025224425141

、mysql解析器是按照如下顺序解析的

image-20201025224550118

image-20201025224453148

Explain详解

​ 在工作中,我们常常编写的是sql性能低下,比如运行时间过长等等,这时候当我们利用相关工具检测出具体时哪些sql时,这时候需要找到具体存在的问题进行优化,而explain就可以做这个事情,它可以帮助我们发现是否使用了索引查询等指标,从而帮助我们过更好的优化。例如对于存在的dept表:

image-20201025231150769

使用expla分析语句SELECT * from dept WHERE dname = ‘开发部’ and dage >8

image-20201025231251394

expain出来的信息有10列,分别是id、select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtered、Extra。

各字段概要信息如下

  1. id:选择标识符
  2. select_type:表示查询的类型。
  3. table:输出结果集的表
  4. partitions:匹配的分区
  5. type:表示表的连接类型
  6. possible_keys:表示查询时,可能使用的索引
  7. key:表示实际使用的索引
  8. key_len:索引字段的长度
  9. ref:列与索引的比较
  10. rows:扫描出的行数(估算的行数)
  11. filtered:按表条件过滤的行百分比
  12. Extra:执行情况的描述和说明

各字段详细信息如下

1.id

SELECT识别符。这是SELECT的查询序列号,其执行顺序如下:

  • id相同时,执行顺序由上至下
  • 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  • id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

2.select_type

表示查询中每个select子句的类型

  • SIMPLE(简单SELECT,不使用UNION或子查询等)
  • PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)
  • UNION(UNION中的第二个或后面的SELECT语句)
  • DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
  • UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)
  • SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)
  • DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)
  • DERIVED(派生表的SELECT, FROM子句的子查询)
  • UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)

3.table

显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名字,可能是简称

4.type

对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。

常用的类型有: **ALL、index、range、 ref、eq_ref、const、system、**NULL(从左到右,性能从差到好

ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行。

index: Full Index Scan,index与ALL区别为index类型只遍历索引树。

range:只检索给定范围的行,使用一个索引来选择行。

ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件。

const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system。

NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

5.possible_keys

指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。

6.key

显示MySQL实际决定使用的键(索引),必然包含在possible_keys中。

7.key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)。不损失精确性的情况下,长度越短越好 。

8.ref

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

9.rows

估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。

10.Extra

该列包含MySQL解决查询的详细信息,有以下几种情况:

**Using where:**不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤

**Using temporary:**表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by

**Using filesort:**当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”g join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。

Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

No tables used:Query语句中使用from dual 或不含任何from子句

黑色加粗是常用的。

SQL优化

sql查询慢的主要原因是没有合理的使用索引,而索引的主要功能是查找和排序,所以也是针对这两部分优化,例如对于where条件的优化和order by的优化,假如一个索引是由a、b、c三个字段顺序组成,对于where条件的优化,其优化的主要原则如下:

1.最左原则,即where后面的条件要从a开始,否则将会导致索引失效而进行全盘扫描。

2.范围查询导致后面索引会失效,比如说b是一个范围查询,那么就会导致c失效。

3.遵循小表驱动大表原则(即先查询小表),合理使用in和exist,因为in是先加载嵌套部分,再加载外面的,而exist正好相反,根据各自特点合理使用。

4.使用非‘b%’的模糊查询会导致索引失效。

5.进行函数计算时会导致索引实现,比如说一个字符串索引,在查询时未加单引号(数值),此时会导致索引失效,因为会调用底层转换函数。

6.使用不等于(!=或者<>)会导致索引失效。

7.使用is null或者is not null 会导致索引失效。

8.尽量使用覆盖索引和全值匹配。

order优化

image-20201026000901984

image-20201026000954037

image-20201026001116744

image-20201026001203098

image-20201026001241623

image-20201026001300583

image-20201026001550080

异常SQL诊断

​ 当遇到查询慢的情况,如何找出是那些sql导致的呢,常用的有两种方式,慢查询日志和show profile,慢查询日志能帮助我们找出哪些sql的运行时间超过设置的指定时间,而show profile则能分析出整个sql执行生命周期在各个阶段的耗时。

慢查询日志

  • 查看是否开启慢查询功能:
mysql> show variables like 'slow_query%';
+---------------------+------------------------------------+
| Variable_name       | Value                              |
+---------------------+------------------------------------+
| slow_query_log      | OFF                                |
| slow_query_log_file | /var/lib/mysql/instance-1-slow.log |
+---------------------+------------------------------------+
2 rows in set (0.01 sec)
mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.00 sec)

说明:

slow_query_log 慢查询开启状态
slow_query_log_file 慢查询日志存放的位置(这个目录需要MySQL的运行帐号的可写权限,一般设置为MySQL的数据存放目录)
long_query_time 查询超过多少秒才记录

配置

临时配置

默认没有开启慢查询日志记录,通过命令临时开启:

mysql> set global slow_query_log='ON';
Query OK, 0 rows affected (0.00 sec)
 
mysql> set global slow_query_log_file='/var/lib/mysql/instance-1-slow.log';
Query OK, 0 rows affected (0.00 sec)
 
mysql> set global long_query_time=2;
Query OK, 0 rows affected (0.00 sec)
永久配置

修改配置文件达到永久配置状态:

/etc/mysql/conf.d/mysql.cnf
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/lib/mysql/instance-1-slow.log
long_query_time = 2

配置好后,重新启动 MySQL 即可。

测试
通过运行下面的命令,达到问题 SQL 语句的执行:

mysql> select sleep(2);
+----------+
| sleep(2) |
+----------+
|        0 |
+----------+
1 row in set (2.00 sec)

然后查看慢查询日志内容:

$ cat /var/lib/mysql/instance-1-slow.log
/usr/sbin/mysqld, Version: 8.0.13 (MySQL Community Server - GPL). started with:
Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
Time                 Id Command    Argument
/usr/sbin/mysqld, Version: 8.0.13 (MySQL Community Server - GPL). started with:
Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
Time                 Id Command    Argument
# Time: 2018-12-18T05:55:15.941477Z
# User@Host: root[root] @ localhost []  Id:    53
# Query_time: 2.000479  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
SET timestamp=1545112515;
select sleep(2);

通过慢查询日志找出哪些sql存在问题后,再结合explain进行优化。

show profile

步骤分析

image-20201026003619643 image-20201026003643917

image-20201026003819098

image-20201026003848354

MySQL锁

常见分类

image-20201026003951596

**读锁(共享锁)**是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

image-20201026005133021

image-20201026005235633

**写锁(排他锁)**如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

表锁:对整个表加锁。

行锁:对行数据加锁。

表锁、行锁和读写锁只是从两个维度划分的,可以共存的,例如只是针对某个表或者某个记录加读写锁。

优化策略

image-20201026005832000

image-20201026005910536

image-20201026005932109

image-20201026010001106

参考:

https://www.cnblogs.com/tufujie/p/9413852.html

https://www.cnblogs.com/magic-chenyang/p/10557002.html

;