[转帖]【建议收藏】15755 字,讲透 MySQL 性能优化(包含 MySQL 架构、存储引擎、调优工具、SQL、索引、建议等等)

建议,收藏,mysql,性能,优化,包含,架构,存储,引擎,工具,sql,索引,等等 · 浏览次数 : 0

小编点评

| SellerID | Name | Nickname | Password | Status | Address | CreateTime | |:---|---|---|---|---|---| | itcast | 传智播客教育科技有限公司 | 传智播客 | e10adc3949ba59abbe56e057f20f883e | 1 | 北京市 | 2088-01-01 12:00:00 | | theima | 黑马程序员 | 黑马程序员 | e10adc3949ba59abbe56e057f20f883e | 0 | 北京市 | 2088-01-01 12:00:00 | | luoji | 罗技科技有限公司 | 罗技小店 | e10adc3949ba59abbe56e057f20f883e | 1 | 北京市 | 2088-01-01 12:00:00 | | oppo | OPPO科技有限公司 | OPPO官方旗舰店 | e10adc3949ba59abbe56e057f20f883e | 0 | 北京市 | 2088-01-01 12:00:00 | | ourpalm | 掌趣科技股份有限公司 |掌趣小店 | e10adc3949ba59abbe56e057f20f883e | 1 | 北京市 | 2088-01-01 12:00:00 | | qiandu | 千度科技 | 千度小店 | e10adc3949ba59abbe56e057f20f883e | 2 | 北京市 | 2088-01-01 12:00:00 | | sina | 新浪科技有限公司 | 微博官方旗舰店 | e10adc3949ba59abbe56e057f20f883e | 1 | 北京市 | 2088-01-01 12:00:00 | | xixiaomi |小米科技 |小米官方旗舰店 | e10adc3949ba59abbe56e057f20f883e | 0 | 北京市 | 2088-01-01 12:00:00 | |科技 | 科技 | 科技 | e10adc3949ba59abbe56e057f20f883e | 0 | 北京市 | 2088-01-01 12:00:00 |

正文

https://my.oschina.net/jiagoushi/blog/5593246

 

 

0. 目录

1)MySQL 总体架构介绍

2)MySQL 存储引擎调优

3)常用慢查询分析工具

4)如何定位不合理的 SQL

5)SQL 优化的一些建议

1 MySQL 总体架构介绍

1.1 MySQL 总体架构介绍

引言
MySQL是一个关系型数据库
应用十分广泛
在学习任何一门知识之前
对其架构有一个概括性的了解是非常重要的
比如索引、sql是在哪个地方执行的
流程是什么样的
今天我们就先来学习一下MySQL的总体架构

总的来说:MySQL 架构是一个客户端 - 服务器系统。

MySQL 主要包括以下几部分:

Server 层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。

存储引擎: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自己的日志模块 redolog 模块。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了

连接器: 身份认证和权限相关 (登录 MySQL 的时候)。

查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)mysql 的 server 层增加一层缓存模块,类似一个内存的 kv 层,k 是 sql,value 是结果

分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。

优化器: 按照 MySQL 认为最优的方案去执行。

执行器: 执行语句,然后从存储引擎返回数据。

1.2 MySQL 存储引擎介绍

引言
和大多数的数据库不同, MySQL中有一个存储引擎的概念

针对不同的存储需求可以选择最优的存储引擎。

存储引擎就是存储数据,建立索引,更新查询数据等等技术的实现方式 。

存储引擎是基于表的,而不是基于库的
所以存储引擎也可被称为表类型。

MySQL 提供了插件式的存储引擎架构。所以 MySQL 存在多种存储引擎,可以根据需要使用相应引擎,或者编写存储引擎。

MySQL5.0支持的存储引擎包含 : InnoDB 、MyISAM 、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等

可以通过指定 show engines , 来查询当前数据库支持的存储引擎 :

SHOW ENGINES;

 

表含义:
  - support : 指服务器是否支持该存储引擎
  - transactions : 指存储引擎是否支持事务
  - XA : 指存储引擎是否支持分布式事务处理
  - Savepoints : 指存储引擎是否支持保存点(实现回滚到指定保存点)
  • 查看 MySQL 数据库存储引擎配置

    SHOW VARIABLES LIKE '%storage_engine%';
    

 

1.2.1 如何更改数据库表引擎

  • 建表语句后面加入引擎赋值即可,命令举例如下,
CREATE TABLE t1(
	id INT ,
    name VARCHAR(20)
) ENGINE = MyISAM;

  • 修改已有的表引擎,命令举例如下,
ALTER TABLE t1 ENGINE = InnoDB;

1.2.2 常用引擎及其特性对比

  • 常见的存储引擎 :

    MyISAM 存储引擎 : 访问快,不支持事务和外键。表结构保存在.frm 文件中,表数据保存在.MYD 文件中,索引保存在.MYI 文件中。

    [root@linux-141 itcast]# ll
    -rw-r-----. 1 mysql mysql       8630 910 16:01 t_account_myisam.frm
    -rw-r-----. 1 mysql mysql         52 910 16:06 t_account_myisam.MYD
    -rw-r-----. 1 mysql mysql       2048 910 17:56 t_account_myisam.MYI
    [root@linux-141 itcast]#
    
    

**innoDB 存储引擎 (**5.5 版本开始默认) : 支持事务,占用磁盘空间大,支持并发控制。表结构保存在.frm 文件中,如果是共享表空间,数据和索引保存在 innodb_data_home_dir 和 innodb_data_file_path 定义的表空间中,可以是多个文件。如果是多表空间存储,每个表的数据和索引单独保存在 .ibd 中。

[root@linux-141 itcast]# ll
-rw-r-----. 1 mysql mysql       8630 910 16:02 t_account_innodb.frm
-rw-r-----. 1 mysql mysql      98304 914 15:50 t_account_innodb.ibd
[root@linux-141 itcast]#

MEMORY 存储引擎 : 内存存储,速度快,不安全,适合小量快速访问的数据。表结构保存在.frm 中。

 

特性对比 :

特点InnoDBMyISAMMEMORYMERGENDB
存储限制 64TB 没有
事务安全 == 支持 ==        
锁机制 == 行锁 (适合高并发)== == 表锁 == 表锁 表锁 行锁
B 树索引 支持 支持 支持 支持 支持
哈希索引     支持    
全文索引 支持 (5.6 版本之后) 支持      
集群索引 支持        
数据索引 支持   支持   支持
索引缓存 支持 支持 支持 支持 支持
数据可压缩   支持      
空间使用 N/A
内存使用 中等
批量插入速度
支持外键 == 支持 ==        

1.2.3 如何选择不同类型的引擎

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

以下是几种常用的存储引擎的使用环境。

  • InnoDB : 是 Mysql 的默认存储引擎,用于事务处理应用程序,支持外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询以外,还包含更新、删除操作,那么 InnoDB 存储引擎是比较合适的选择。InnoDB 存储引擎除了有效的降低由于删除和更新导致的锁定, 还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB 是最合适的选择。
  • MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
  • MEMORY:将所有数据保存在 RAM 中,在需要快速定位记录和其他类似数据环境下,可以提供极快的访问。MEMORY 的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY 表通常用于更新不太频繁的小表,用以快速得到访问结果。

1.3 SQL 的执行流程是什么样的

  • 客户端发送一条查询给服务器。
  • 服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果。否则进入下一阶段。
  • 服务器端进行 SQL 解析、预处理,再由优化器生成对应的执行计划。
  • MySQL 根据优化器生成的执行计划,再调用存储引擎的 API 来执行查询。
  • 将结果返回给客户端。

 

2 MySQL 存储引擎调优

2.1 MySQL 服务器硬件优化

tips

硬件(cpu、内存等)相关

了解即可

关于提升硬件设备性能:

例如选择尽量高频率的 ** 内存(** 频率不能高于主板的支持)、提升网络带宽、使用 SSD 高速磁盘、提升 CPU 性能等。

CPU 的选择:

  • 对于数据库并发比较高的场景,CPU 的数量比频率重要。
  • 对于 CPU 密集型场景和频繁执行复杂 SQL 的场景,CPU 的频率越高越好

磁盘的选择

影响数据库最大的性能问题就是磁盘 I/O 为提高数据库的 IOPS 性能,可使用 SSD 或 PCIE-SSD 高速磁盘设备

磁盘 IO 的优化

可以用 RAID 来进行优化

常用 RAID(磁盘阵列)级别:

RAID0:也称为条带,就是把多个磁盘链接成一个硬盘使用,这个级别 IO 最好 RAID1:也称为镜像,要求至少有两个磁盘,每组磁盘存储的数据相同 RAID5:也是把多个(最少 3 个)硬盘合并成一个逻辑盘使用,数据读写时会建立奇偶校验信息,并且奇偶校验信息和相对应的数据分别存储在不同的磁盘上。当 RAID5 的一个磁盘数据发生损坏后,利用剩下的数据和响应的奇偶校验信息去恢复被损坏的数据

RAID1+0(建议使用):就是 RAID0 和 RAID1 的组合。同时具备两个级别的优缺点,一般建议数据库使用这个级别。

2.2 MySQL 数据库配置优化

tips:

以下为生产环境中最常用的 DB 参数配置

  • 表示缓冲池字节大小,大的缓冲池可以减少磁盘 IO 次数。 innodb_buffer_pool_size = 推荐值为物理内存的 50%~80%。

  • 用来控制 redo log buffer 刷新到磁盘的策略。 innodb_flush_log_at_trx_commit=1

    select @@innodb_flush_log_at_trx_commit;
    
    0 : 提交事务的时候,不立即把 redo log buffer 里的数据刷入磁盘文件中,而是依靠 InnoDB 的主线程每秒执行一次刷新到磁盘。此时可能你提交事务了,结果 mysql 宕机了,然后此时内存里的数据全部丢失。
    1 : 提交事务的时候,立即把 redo log buffer 里的数据刷入磁盘文件中,只要事务提交成功,那么数据就必然在磁盘里了。
    2 : 提交事务的时候,把 redo log buffer日志写入磁盘文件对应的系统缓存,而不是直接进入磁盘文件,这时可能1秒后才会把系统缓存里的数据写入到磁盘文件。
    
  • 每提交 1 次事务就同步写到磁盘中,可以设置为 1。 sync_binlog=1

    0:默认值。事务提交后,将二进制日志从缓冲写入操作系统缓冲,但是不进行刷新操作(fsync()),此时只是写入了操作系统缓冲而没有刷新到磁盘,若操作系统宕机则会丢失部分二进制日志。
    1:事务提交后,将二进制文件写入磁盘并立即执行刷新操作,相当于是同步写入磁盘,不经过操作系统的缓存。
    N:每写N次操作系统缓冲就执行一次刷新操作。
    
  • 脏页占 innodb_buffer_pool_size 的比例,触发刷脏页到磁盘。 推荐值为 25%~50%。 innodb_max_dirty_pages_pct=30

    脏页:内存数据页和磁盘数据页上的内容不一致
    
  • 后台进程最大 IO 性能指标。 默认 200,如果 SSD,调整为 5000~20000

    PCIE-SSD 可调整为 5w 左右

    默认:innodb_io_capacity=200

  • 指定 innodb 共享表空间文件的大小。 innodb_data_file_path = ibdata:1G:autoextend:默认 10M,

    一般设置为 1GB

  • 慢查询日志的阈值设置,单位秒。 long_query_time=0.3

    合理设置区间 0.1s~0.5s,

  • mysql 复制的形式,row 为 MySQL8.0 的默认形式。 binlog_format=row

    建议 binlog 的记录格式为 row 模式

    STATEMENT模式:每一条会修改数据的sql语句都会记录到binlog中。
    ROW模式:不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了,修改成什么样了。
    MIXED模式:以上两种模式的混合使用。
    
  • 降低 interactive_timeout、wait_timeout 的值。

    交互等待时间和非交互等待时间,值一致,建议 300~500s,默认 8 小时

    在用mysql客户端对数据库进行操作时,打开终端窗口,如果一段时间(8小时)没有操作,再次操作时,会报错:当前的连接已经断开,需要重新建立连接
    
  • 数据库最大连接数 max_connections=200

  • 过大,实例恢复时间长;过小,造成日志切换频繁。 innodb_log_file_size = 默认

    redo log 空间大小

  • 全量日志建议关闭。 默认关闭 general_log=0

    开启 general log 将所有到达MySQL Server的SQL语句记录下来,general_Log文件就会产生很大的文件,建议关闭
    

2.3 Mysql 中查询缓存优化

tips:

在 MySQL 8.0 之后废弃这个功能

原理:复杂、实用性不高

作为了解即可

1) 查询缓存概述

开启 Mysql 的查询缓存,当执行完全相同的 SQL 语句的时候,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存。

2) 操作流程

回顾

 

  1. 客户端发送一条查询给服务器;
  2. 服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果。否则进入下一阶段;
  3. 服务器端进行 SQL 解析、预处理,再由优化器生成对应的执行计划;
  4. MySQL 根据优化器生成的执行计划,调用存储引擎的 API 来执行查询;
  5. 将结果返回给客户端。

3) 查询缓存配置

  1. 查看当前的 MySQL 数据库是否支持查询缓存:

    SHOW VARIABLES LIKE 'have_query_cache';	
    

2. 查看当前 MySQL 是否开启了查询缓存 :

SHOW VARIABLES LIKE 'query_cache_type';

3. 查看查询缓存的占用大小 :

SHOW VARIABLES LIKE 'query_cache_size';

4. 查看查询缓存的状态变量:

SHOW STATUS LIKE 'Qcache%';

各个变量的含义如下:

参数含义
Qcache_free_blocks 查询缓存中的可用内存块数
Qcache_free_memory 查询缓存的可用内存量
Qcache_hits 查询缓存命中数
Qcache_inserts 添加到查询缓存的查询数
Qcache_lowmen_prunes 由于内存不足而从查询缓存中删除的查询数
Qcache_not_cached 非缓存查询的数量(由于 query_cache_type 设置而无法缓存或未缓存)
Qcache_queries_in_cache 查询缓存中注册的查询数
Qcache_total_blocks 查询缓存中的块总数

4) 开启查询缓存

MySQL 的查询缓存默认是关闭的,需要手动配置参数 query_cache_type , 来开启查询缓存。query_cache_type 该参数的可取值有三个 :

含义
OFF 或 0 查询缓存功能关闭
ON 或 1 查询缓存功能打开,SELECT 的结果符合缓存条件即会缓存,否则,不予缓存,显式指定 SQL_NO_CACHE,不予缓存
DEMAND 或 2 查询缓存功能按需进行,显式指定 SQL_CACHE 的 SELECT 语句才会缓存;其它均不予缓存

在 my.cnf 配置中,增加以下配置 :

#开启查询缓存
query_cache_type=1

配置完毕之后,重启服务既可生效 ;

然后就可以在命令行执行 SQL 语句进行验证 ,执行一条比较耗时的 SQL 语句,然后再多执行几次,查看后面几次的执行时间;获取通过查看查询缓存的缓存命中数,来判定是否走查询缓存。

-- 执行SQL语句进行验证 查询缓存
SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';
-- 将SELECT修改为小写,发现缓存失效
SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

5) 查询缓存 SELECT 选项

可以在 SELECT 语句中指定两个与查询缓存相关的选项 :

SQL_CACHE : 如果查询结果是可缓存的,并且 query_cache_type 系统变量的值为 ON 或 DEMAND ,则缓存查询结果 。

SQL_NO_CACHE : 服务器不使用查询缓存。它既不检查查询缓存,也不检查结果是否已缓存,也不缓存查询结果。

例子:

SELECT SQL_CACHE id, name FROM xxx;
SELECT SQL_NO_CACHE id, name FROM xxx;

6) 查询缓存失效的情况

tips

需要注意的问题

1) SQL 语句不一致的情况, 要想命中查询缓存,查询的 SQL 语句必须一致。

SQL1 : select count(*) from xxx;
SQL2 : Select count(*) from xxx;

2) 当查询语句中有一些不确定的值,则不会缓存。如 : now () , current_date () , curdate () , curtime () , rand () , uuid () , user () , database () 。

SQL1 : select * from xxx where updatetime < now() limit 1;
SQL2 : select user();
SQL3 : select database();

3) 不使用任何表查询语句。

select 'A';

4) 查询 mysql, information_schema 或 performance_schema 数据库中的表时,不会走查询缓存。

select * from information_schema.engines;

5) 在存储的函数,触发器或事件的主体内执行的查询。

6) 如果表更改,则使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。这包括使用 MERGE 映射到已更改表的表的查询。一个表可以被许多类型的语句,如被改变 INSERT, UPDATE, DELETE, TRUNCATE TABLE, ALTER TABLE, DROP TABLE,或 DROP DATABASE 。

将查询缓存关闭,因为后面还需要进行索引的验证,所以不希望走查询缓存

[root@linux-141 itcast]# vi /etc/my.cnf
[root@linux-141 itcast]# service mysql restart
Shutting down MySQL.... SUCCESS!
Starting MySQL. SUCCESS!

2.4. Mysql 内存管理及优化

1)内存优化原则

1) 将尽量多的内存分配给 MySQL 做缓存,但要给操作系统和其他程序预留足够内存。

2) MyISAM 存储引擎的数据文件读取依赖于操作系统自身的 IO 缓存,因此,如果有 MyISAM 表,就要预留更多的内存给操作系统做 IO 缓存。

3) 排序区、连接区等缓存是分配给每个数据库会话(session)专用的,其默认值的设置要根据最大连接数合理分配,如果设置太大,不但浪费资源,而且在并发连接较高时会导致物理内存耗尽。

2) MyISAM 内存优化

MyISAM 存储引擎使用 key_buffer 缓存索引块,加速 myisam 索引的读写速度。对于 myisam 表的数据块,mysql 没有特别的缓存机制,完全依赖于操作系统的 IO 缓存。

key_buffer_size

key_buffer_size 决定 MyISAM 索引块缓存区的大小,直接影响到 MyISAM 表的存取效率。可以在 MySQL 参数文件中设置 key_buffer_size 的值,对于一般 MyISAM 数据库,建议至少将 1/4 可用内存分配给 key_buffer_size。

在 my.cnf 中做如下配置:

key_buffer_size=512M
read_buffer_size

如果需要经常顺序扫描 MyISAM 表,可以通过增大 read_buffer_size 的值来改善性能。但需要注意的是 read_buffer_size 是每个 session 独占的,如果默认值设置太大,就会造成内存浪费。

read_rnd_buffer_size

对于需要做排序的 MyISAM 表的查询,如带有 order by 子句的 sql,适当增加 read_rnd_buffer_size 的值,可以改善此类的 sql 性能。

但需要注意的是 read_rnd_buffer_size 是每个 session 独占的,如果默认值设置太大,就会造成内存浪费。

3) InnoDB 内存优化

innodb 用一块内存区做 IO 缓存池,该缓存池不仅用来缓存 innodb 的索引块,而且也用来缓存 innodb 的数据块。

innodb_buffer_pool_size

该变量决定了 innodb 存储引擎表数据和索引数据的最大缓存区大小。在保证操作系统及其他程序有足够内存可用的情况下,innodb_buffer_pool_size 的值越大,缓存命中率越高,访问 InnoDB 表需要的磁盘 I/O 就越少,性能也就越高。

innodb_buffer_pool_size=512M
innodb_log_buffer_size

决定了 innodb 重做日志缓存的大小,对于可能产生大量更新记录的大事务,增加 innodb_log_buffer_size 的大小,可以避免 innodb 在事务提交前就执行不必要的日志写入磁盘操作。

innodb_log_buffer_size=10M

2.5. Mysql 并发参数调整

从实现上来说,MySQL Server 是多线程结构,包括后台线程和客户服务线程。多线程可以有效利用服务器资源,提高数据库的并发性能。在 Mysql 中,控制并发连接和线程的主要参数包括 max_connections、back_log、thread_cache_size、table_open_cahce。

1) max_connections

最大可支持的连接数

采用 max_connections 控制允许连接到 MySQL 数据库的最大数量,默认值是 151。如果状态变量 connection_errors_max_connections 不为零,并且一直增长,则说明不断有连接请求因数据库连接数已达到允许最大值而失败,这时可以考虑增大 max_connections 的值。

Mysql 最大可支持的连接数,取决于很多因素,包括给定操作系统平台的线程库的质量、内存大小、每个连接的负荷、CPU 的处理速度,期望的响应时间等。在 Linux 平台下,性能好的服务器,支持 500-1000 个连接不是难事,需要根据服务器性能进行评估设定。

2) back_log

积压请求栈大小

back_log 参数控制 MySQL 监听 TCP 端口时设置的积压请求栈大小。如果 MySql 的连接数达到 max_connections 时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即 back_log,如果等待连接的数量超过 back_log,将不被授予连接资源,将会报错。5.6.6 版本之前默认值为 50 , 之后的版本默认为 50 + (max_connections / 5), 但最大不超过 900。

如果需要数据库在较短的时间内处理大量连接请求, 可以考虑适当增大 back_log 的值。

3) table_open_cache

执行线程可打开表缓存个数

该参数用来控制所有 SQL 语句执行线程可打开表缓存的数量, 而在执行 SQL 语句时,每一个 SQL 执行线程至少要打开 1 个表缓存。该参数的值应该根据设置的最大连接数 max_connections 以及每个连接执行关联查询中涉及的表的最大数量来设定 :

max_connections x N ;

4) thread_cache_size

缓存客户服务线程的数量

为了加快连接数据库的速度,MySQL 会缓存一定数量的客户服务线程以备重用,通过参数 thread_cache_size 可控制 MySQL 缓存客户服务线程的数量。

5)lock_wait_timeout

innodb_lock_wait_timeout

事务等待行锁的时间

该参数是用来设置 InnoDB 事务等待行锁的时间,默认值是 50ms , 可以根据需要进行动态设置。对于需要快速反馈的业务系统来说,可以将行锁的等待时间调小,以避免事务长时间挂起; 对于后台运行的批量处理程序来说, 可以将行锁的等待时间调大, 以避免发生大的回滚操作。

3 常用慢查询分析工具

引言
在日常的业务开发中
MySQL 出现慢查询是很常见的
大部分情况下会分为两种情况
1、业务增长太快
2、要么就是SQL 写的太xx了
所以
对慢查询 SQL 进行分析和优化很重要
其中 mysqldumpslow MySQL 服务自带的一款很好的分析调优工具

3.1 调优工具 mysqldumpslow

3.1.1 调优工具常用设置

1、什么是 MySQL 慢查询日志

MySQL 提供的一种慢查询日志记录,用来记录在 MySQL 查询中响应时间超过阀值的记录 具体指运行时间超过 long_query_time 值的 SQL,则会被记录到慢查询日志中

2、如何查看慢查询设置情况

慢查询的时间阈值设置

show variables like '%slow_query_log%';

解释

  • slow_query_log // 是否开启,默认关闭,建议调优时才开启
  • slow_query_log_file // 慢查询日志存放路径

3、如何开启慢查询日志记录

1) 命令开启

set global slow_query_log =1; //只对当前会话生效,重启失效

执行成功

再次执行

show variables like '%slow_query_log%';

先关闭客户端连接,再进行重新连接,即可看到设置生效

发现开启了 mysqldumpslow 调优工具

mysql> show variables like '%slow_query_log%';
+---------------------+-------------------------------------------+
| Variable_name       | Value                                     |
+---------------------+-------------------------------------------+
| slow_query_log      | ON                                        |
| slow_query_log_file | /opt/mysql-5.7.28/data/linux-141-slow.log |
+---------------------+-------------------------------------------+
2 rows in set (0.02 sec)

mysql> 

2)配置文件开启

vim my.cnf
在[mysqld]下添加:
slow_query_log = 1
slow_query_log_file = /opt/mysql-5.7.28/data/linux-141-slow.log
重启MySQL服务
 

修改并且重启后

发现开启了 mysqldumpslow 调优工具

mysql> show variables like '%slow_query_log%';
+---------------------+-------------------------------------------+
| Variable_name       | Value                                     |
+---------------------+-------------------------------------------+
| slow_query_log      | ON                                        |
| slow_query_log_file | /opt/mysql-5.7.28/data/linux-141-slow.log |
+---------------------+-------------------------------------------+
2 rows in set (0.02 sec)

mysql> 

3)哪些 SQL 会记录到慢查询日志

-- 查看阀值(大于),默认10s
show variables like 'long_query_time%';

 

默认值是 10 秒

4)如何设置查询阀值

  • 命令设置
-- 设置慢查询阀值
set global long_query_time = 1;

备注:另外开一个 session 或重新连接 ,才会看到变化

执行成功发发现慢 sql 的时间变成了 1 秒

配置文件设置

vim my.cnf
[mysqld]
long_query_time = 1
log_output = FILE
重启MySQL服务

执行成功发发现慢 sql 的时间变成了 1 秒

5)如何把未使用索引的 SQL 记录写入慢查询日志

-- 查看设置,默认关闭
show variables like 'log_queries_not_using_indexes';

我们发现,未使用索引的 sql 默认是不记录到慢查询日志的

开启配置

set global log_queries_not_using_indexes = on;

执行如下

6)模拟数据

-- 睡眠2s再执行
select sleep(2);
-- 查看慢查询条数
show global status like '%Slow_queries%';

我们发现,每执行一次 select sleep (2),之后,再通过 show global status ... 命令,他的值就会 + 1

 

3.1.2 调优工具常用命令

语法格式

mysqldumpslow [ OPTS... ] [ LOGS... ] //命令行格式

常用到的格式组合

-s 表示按照何种方式排序
    c 访问次数
    l 锁定时间
    r 返回记录
    t 查询时间
    al 平均锁定时间
    ar 平均返回记录数
    at  平均查询时间
-t 返回前面多少条数据
-g 后边搭配一个正则匹配模式,大小写不敏感

1、拿到慢日志路径

show variables like '%slow_query_log%';

日志路径为:/opt/mysql-5.7.28/data/linux-141-slow.log

查看日志

[root@linux-141 mysql-5.7.28]# cat /opt/mysql-5.7.28/data/linux-141-slow.log
/opt/mysql-5.7.28/bin/mysqld, Version: 5.7.28-log (MySQL Community Server (GPL)). started with:
Tcp port: 3306  Unix socket: /tmp/mysql.sock
Time                 Id Command    Argument
# Time: 2021-09-15T01:40:31.342430Z
# User@Host: root[root] @  [192.168.36.1]  Id:     2
# Query_time: 2.000863  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
use itcast;
SET timestamp=1631670031;
-- 睡眠2s再执行
select sleep(2);
[root@linux-141 mysql-5.7.28]#

2、得到访问次数最多的 10 条 SQL

[root@linux-141 mysql-5.7.28]# ./bin/mysqldumpslow -s r -t  10 /opt/mysql-5.7.28/data/linux-141-slow.log
-bash: ./bin/mysqldumpslow: /usr/bin/perl: 坏的解释器: 没有那个文件或目录
[root@linux-141 mysql-5.7.28]# yum -y install perl perl-devel
[root@linux-141 mysql-5.7.28]# ./bin/mysqldumpslow -s r -t  10 /opt/mysql-5.7.28/data/linux-141-slow.log

3、按照时间排序的前 10 条里面含有左连接的 SQL

[root@linux-141 mysql-5.7.28]# ./bin/mysqldumpslow -s t -t 10 -g "left join"  /opt/mysql-5.7.28/data/linux-141-slow.log

Reading mysql slow query log from /opt/mysql-5.7.28/data/linux-141-slow.log
Died at ./bin/mysqldumpslow line 167, <> chunk 28.
[root@linux-141 mysql-5.7.28]#

3.1.3 慢日志文件分析

1、查看慢查询日志

[root@linux-141 mysql-5.7.28]# cat /opt/mysql-5.7.28/data/linux-141-slow.log
/opt/mysql-5.7.28/bin/mysqld, Version: 5.7.28-log (MySQL Community Server (GPL)). started with:
Tcp port: 3306  Unix socket: /tmp/mysql.sock
Time                 Id Command    Argument
# Time: 2021-09-15T01:40:31.342430Z
# User@Host: root[root] @  [192.168.36.1]  Id:     2
# Query_time: 2.000863  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
use itcast;
SET timestamp=1631670031;
-- 睡眠2s再执行
select sleep(2);
# Time: 2021-09-15T01:50:32.130305Z
# User@Host: root[root] @  [192.168.36.1]  Id:     2
# Query_time: 3.001904  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
SET timestamp=1631670632;
select sleep(3);
# Time: 2021-09-15T01:50:55.064372Z
# User@Host: root[root] @  [192.168.36.1]  Id:     2
# Query_time: 4.008082  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
SET timestamp=1631670655;
select sleep(4);
# Time: 2021-09-15T01:51:01.343463Z
# User@Host: root[root] @  [192.168.36.1]  Id:     2
# Query_time: 5.007035  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
SET timestamp=1631670661;
select sleep(5);
# Time: 2021-09-15T01:51:07.737834Z                                     	###### 执行SQL时间
# User@Host: root[root] @  [192.168.36.1]  Id:     2						###### 执行SQL的主机信息
# Query_time: 6.009129  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0	###### SQL的执行信息
SET timestamp=1631670667;													###### SQL执行时间
select sleep(6);															###### SQL内容
[root@linux-141 mysql-5.7.28]#

属性解释

# Time: 2021-09-15T01:51:07.737834Z                                     	###### 执行SQL时间
# User@Host: root[root] @  [192.168.36.1]  Id:     2						###### 执行SQL的主机信息
# Query_time: 6.009129  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0	###### SQL的执行信息
SET timestamp=1631670667;													###### SQL执行时间
select sleep(6);															###### SQL内容

3.2 调优工具 show profile

tips:

show profile,它也是调优工具

也是 MySQL 服务自带的分析调优工具

不过这款更高级

比较接近底层硬件参数的调优。

简介:

show profile 是 MySQL 服务自带更高级的分析调优工具

比较接近底层硬件参数的调优

1、查看 show profile 设置

-- 默认关闭,保存近15次的运行结果
show variables like 'profiling%';

通过上面我们发现,show profile 工具默认是关闭状态,15 表示保存了近 15 次的运行结果。

2、开启调优工具

执行下面的命令开启

SET profiling = ON;

再次查看状态

show variables like 'profiling%';

 

3、查看最近 15 次的运行结果

-- 查看最近15次的运行结果
show profiles;

-- 可以显示警告和报错的信息
show warnings;

-- 慢查询语句
SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

显示最近 15 次的运行结果

4、诊断运行的 SQL

接下来,我们一起诊断一下 query id 为 23 的慢查询

-- 语法
SHOW PROFILE cpu,block io FOR QUERY query id;
-- 示例
SHOW PROFILE cpu,block io FOR QUERY 129;

开始执行

 

解释:
通过Status一列,可以看到整条SQL的运行过程
1. starting //开始
2. checking permissions //检查权限
3. Opening tables //打开数据表
4. init //初始化
5. System lock //锁机制
6. optimizing //优化器
7. statistics //分析语法树
8. prepareing //预准备
9. executing //引擎执行开始
10. end //引擎执行结束
11. query end //查询结束
12. closing tables //释放数据表
13. freeing items //释放内存
14. cleaning up //彻底清理
查看类型选项
SHOW PROFILE...后面的列,即:SHOW PROFILE ALL, BLOCK IO, ... FOR QUERY 209;
ALL //显示索引的开销信息
BLOCK IO //显示块IO相关开销
CONTEXT SWITCHES  //上下文切换相关开销
CPU //显示CPU相关开销信息
IPC //显示发送和接收相关开销信息
MEMORY //显示内存相关开销信息
PAGE FAULTS //显示页面错误相关开销信息
SOURCE //显示和source_function,source_file,source_line相关的开销信息
SWAPS //显示交换次数相关开销的信息

重要提示

如出现以下一种或者几种情况,说明SQL执行性能极其低下,亟需优化
* converting HEAP to MyISAM  //查询结果太大,内存都不够用了往磁盘上搬了
* Creating tmp table //创建临时表:拷贝数据到临时表,用完再删
* Copying to tmp table on disk //把内存中临时表复制到磁盘,危险
* locked //出现死锁

4 如何定位不合理的 SQL

引言
在应用的开发过程中,由于初期数据量小,开发人员写 SQL 语句时更重视功能上的实现,但是当应用系统正式上线后,随着生产数据量的急剧增长,很多SQL语句开始逐渐显露出性能问题,对生产的影响也越来越大,此时这些有问题的SQL语句就成为整个系统性能的瓶颈,因此我们必须要对它们进行优化,本章将详细介绍在MySQL中优化SQL语句的方法。

当面对一个有SQL性能问题的数据库时,我们应该从何处入手来进行系统的分析,使得能够尽快定位问题SQL并尽快解决问题。

4.1 如何查看 SQL 执行频率

MySQL 客户端连接成功后,通过

-- 服务器状态信息
show [session|global] status;

命令可以提供服务器状态信息。show [session|global] status 可以根据需要加上参数 “session” 或者 “global” 来显示 session 级(当前连接)的统计结果和 global 级(自数据库上次启动至今)的统计结果。

如果不写,默认使用参数是 “session”。

下面的命令显示了当前 session 中所有统计参数的值:

show status like 'Com_______';

 

show status like 'Innodb_rows_%';

Com_xxx 表示每个 xxx 语句执行的次数,我们通常比较关心的是以下几个统计参数。

参数含义
Com_select 执行 select 操作的次数,一次查询只累加 1。
Com_insert 执行 INSERT 操作的次数,对于批量插入的 INSERT 操作,只累加一次。
Com_update 执行 UPDATE 操作的次数。
Com_delete 执行 DELETE 操作的次数。
Innodb_rows_read select 查询返回的行数。
Innodb_rows_inserted 执行 INSERT 操作插入的行数。
Innodb_rows_updated 执行 UPDATE 操作更新的行数。
Innodb_rows_deleted 执行 DELETE 操作删除的行数。
Connections 试图连接 MySQL 服务器的次数。
Uptime 服务器工作时间。
Slow_queries 慢查询的次数。

Com_*** : 这些参数对于所有存储引擎的表操作都会进行累计。

Innodb_*** : 这几个参数只是针对 InnoDB 存储引擎的,累加的算法也略有不同。

4.2 如何定位低效率 SQL

以下两种方式:

  • 慢查询日志(重要) : 通过慢查询日志定位那些执行效率较低的 SQL 语句,用 --log-slow-queries [=file_name] 选项启动时,mysqld 写一个包含所有执行时间超过 long_query_time 秒的 SQL 语句的日志文件。

    tips:

    关于慢查询 SQL 如何获取

    参看上个章节

  • show processlist (重要) :

    慢查询日志在查询结束以后才记录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题。

    可以使用 show processlist 命令查看当前 MySQL 在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。

 

属性字段解释
1id列,用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id()查看
2user列,显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句
3host列,显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户
4db列,显示这个进程目前连接的是哪个数据库
5command列,显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等
6time列,显示这个状态持续的时间,单位是秒
7state列,显示使用当前连接的sql语句的状态,很重要的列。
	state描述的是语句执行中的某一个状态。一个sql语句,以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成
8info列,显示这个sql语句,是判断问题语句的一个重要依据

4.3 使用 explain 分析执行计划

-- explain 分析执行计划
explain SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

 

字段含义
id select 查询的序列号,是一组数字,表示的是查询中执行 select 子句或者是操作表的顺序。
select_type 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个 SELECT)等
table 输出结果集的表
partitions 匹配的分区
type 表示表的连接类型,性能由好到差的连接类型为 (system ---> const -----> eq_ref ------> ref -------> ref_or_null----> index_merge ---> index_subquery -----> range -----> index ------> all )
possible_keys 表示查询时,可能使用的索引
key 表示实际使用的索引
key_len 索引字段的长度
rows 扫描行的数量
filtered 按表条件过滤的行百分比
extra 执行情况的说明和描述

4.3.1 环境准备

 

CREATE TABLE `t_role` (
  `id` varchar(32) NOT NULL,
  `role_name` varchar(255) DEFAULT NULL,
  `role_code` varchar(255) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_role_name` (`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `t_user` (
  `id` varchar(32) NOT NULL,
  `username` varchar(45) NOT NULL,
  `password` varchar(96) NOT NULL,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `user_role` (
  `id` int(11) NOT NULL auto_increment ,
  `user_id` varchar(32) DEFAULT NULL,
  `role_id` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_role_user` (`role_id`,`user_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into `t_user` (`id`, `username`, `password`, `name`) values('1','super','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','超级管理员');
insert into `t_user` (`id`, `username`, `password`, `name`) values('2','admin','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','系统管理员');
insert into `t_user` (`id`, `username`, `password`, `name`) values('3','itcast','$2a$10$8qmaHgUFUAmPR5pOuWhYWOr291WJYjHelUlYn07k5ELF8ZCrW0Cui','test02');
insert into `t_user` (`id`, `username`, `password`, `name`) values('4','stu1','$2a$10$pLtt2KDAFpwTWLjNsmTEi.oU1yOZyIn9XkziK/y/spH5rftCpUMZa','学生1');
insert into `t_user` (`id`, `username`, `password`, `name`) values('5','stu2','$2a$10$nxPKkYSez7uz2YQYUnwhR.z57km3yqKn3Hr/p1FR6ZKgc18u.Tvqm','学生2');
insert into `t_user` (`id`, `username`, `password`, `name`) values('6','t1','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','老师1');

INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('5','学生','student','学生');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('7','老师','teacher','老师');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('8','教学管理员','teachmanager','教学管理员');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('9','管理员','admin','管理员');
INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('10','超级管理员','super','超级管理员');

INSERT INTO user_role(id,user_id,role_id) VALUES(NULL, '1', '5'),(NULL, '1', '7'),(NULL, '2', '8'),(NULL, '3', '9'),(NULL, '4', '8'),(NULL, '5', '10') ;

4.3.2 explain 之 id

id 字段是 select 查询的序列号,是一组数字,表示的是查询中执行 select 子句或者是操作表的顺序。

id 情况有三种 :

1) id 相同表示加载表的顺序是从上到下。

explain select * from t_role r, t_user u, user_role ur where r.id = ur.role_id and u.id = ur.user_id ;

 

2) id 不同 id 值越大,优先级越高,越先被执行。

EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id = (SELECT id FROM t_user WHERE username = 'stu1'))

 

3) id 有相同,也有不同,同时存在。id 相同的可以认为是一组,从上往下顺序执行;在所有的组中,id 的值越大,优先级越高,越先执行。

EXPLAIN SELECT * FROM t_role r , (SELECT * FROM user_role ur WHERE ur.`user_id` = '2') a WHERE r.id = (select role.id from t_user, user_role role where role.id = 10) ;

 

4.3.3 explain 之 select_type

表示 SELECT 的类型,常见的取值,如下表所示:

EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id = (SELECT id FROM t_user WHERE username = 'stu1'));

 

select_type含义
SIMPLE 简单的 select 查询,查询中不包含子查询或者 UNION
PRIMARY 查询中若包含任何复杂的子查询,最外层查询标记为该标识
SUBQUERY 在 SELECT 或 WHERE 列表中包含了子查询
DERIVED 在 FROM 列表中包含的子查询,被标记为 DERIVED(衍生) MYSQL 会递归执行这些子查询,把结果放在临时表中
UNION 若第二个 SELECT 出现在 UNION 之后,则标记为 UNION ; 若 UNION 包含在 FROM 子句的子查询中,外层 SELECT 将被标记为 : DERIVED
UNION RESULT 从 UNION 表获取结果的 SELECT

4.3.4 explain 之 table

展示这一行的数据是关于哪一张表的

EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id = (SELECT id FROM t_user WHERE username = 'stu1'));

 

4.3.5 explain 之 type

EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id = (SELECT id FROM t_user WHERE username = 'stu1'));

 

type 显示的是访问类型,是较为重要的一个指标,可取值为:

type含义
NULL MySQL 不访问任何表,索引,直接返回结果
system 表只有一行记录 (等于系统表),这是 const 类型的特例,一般不会出现
const 表示通过索引一次就找到了,const 用于比较 primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于 where 列表中,MySQL 就能将该查询转换为一个常量。const 会将 "主键" 或 "唯一" 索引的所有部分与常量值进行比较
eq_ref 类似 ref,区别在于使用的是唯一索引,使用主键的关联查询,关联查询出的记录只有一条。常见于主键或唯一索引扫描
ref 非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回所有匹配某个单独值的所有行(多个)
range 只检索给定返回的行,使用一个索引来选择行。 where 之后出现 between , <,> , in 等操作。
index index 与 ALL 的区别为 index 类型只是遍历了索引树, 通常比 ALL 快, ALL 是遍历数据文件。
all 将遍历全表以找到匹配的行

结果值从最好到最坏依次是:

NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL


system > const > eq_ref > ref > range > index > ALL

一般来说, 我们需要保证查询至少达到 range 级别, 最好达到 ref 。

4.3.6 explain 之 key

possible_keys : 显示可能应用在这张表的索引, 一个或多个。 
key : 实际使用的索引, 如果为NULL, 则没有使用索引。
key_len : 表示索引中使用的字节数。len=3*n+2(n为索引字段的长度)

EXPLAIN select * from t_role where role_name = '超级管理员'; 
select 255 * 3 + 2; -- role_name VARCHAR(255)

 

4.3.7 explain 之 rows

扫描行的数量。

4.3.8 explain 之 extra

其他的额外的执行计划信息,在该列展示 。

EXPLAIN select u.username from t_user u order by u.username desc;

 

extra含义
using filesort 说明 mysql 会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取, 称为 “文件排序”, 效率低。
using temporary 使用了临时表保存中间结果,MySQL 在对查询结果排序时使用临时表。常见于 order by 和 group by; 效率低
using index 表示相应的 select 操作使用了覆盖索引, 避免访问表的数据行, 效率不错。

5 如何合理使用索引加速

tips:

500 万条建表 sql 参照网盘 sql 脚本

[root@linux-141 bin]# ./mysql -u root -p itcast < product_list-5072825.sql

索引是数据库优化最常用也是最重要的手段之一,通过索引通常可以帮助用户解决大多数的 MySQL 的性能优化问题。

5.1 验证索引提升查询效率

在我们准备的表结构 product_list 中, 一共存储了 500 多万记录;

mysql> select count(1) from product_list;
+----------+
| count(1) |
+----------+
|  5072825 |
+----------+
1 row in set (1.71 sec)

mysql> 

1) 根据 ID 查询

SELECT * FROM product_list WHERE id = 121926;

 

查询速度很快, 接近 0s , 主要的原因是因为 id 为主键, 有索引;

2). 根据 store_name 进行精确查询

执行用时 4 分钟

SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

 

查看 SQL 语句的执行计划 :

explain SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

 

处理方案 , 针对 store_name 字段, 创建索引 :

create index product_list_stname on product_list(store_name);

 

索引创建完成之后,再次进行查询 :

SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

 

通过 explain , 查看执行计划,执行 SQL 时使用了刚才创建的索引

-- 查看SQL语句的执行计划
explain SELECT * FROM product_list WHERE store_name = '联想北达兴科专卖店';

 

5.2 索引的使用

5.2.1 准备环境

create table `tb_seller` (
	`sellerid` varchar (100),
	`name`  varchar (100) not null,
	`nickname` varchar (50),
	`password` varchar (60),
	`status`  varchar (1) not null,
	`address`  varchar (100) not null,
	`createtime` datetime,
    primary key(`sellerid`)
)engine=innodb default charset=utf8; 

insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('alibaba','阿里巴巴','阿里小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('baidu','百度科技有限公司','百度小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('huawei','华为科技有限公司','华为小店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itcast','传智播客教育科技有限公司','传智播客','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itheima','黑马程序员','黑马程序员','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('luoji','罗技科技有限公司','罗技小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('oppo','OPPO科技有限公司','OPPO官方旗舰店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('ourpalm','掌趣科技股份有限公司','掌趣小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('qiandu','千度科技','千度小店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('sina','新浪科技有限公司','新浪官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('xiaomi','小米科技','小米官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('yijia','宜家家居','宜家家居旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');


create index idx_seller_name_sta_addr on tb_seller(name,status,address);

5.2.2 避免索引失效

组合索引 (name,status,address)

1) 全值匹配

对索引中所有列都指定具体值。

-- 全值匹配
explain select * from tb_seller where name='小米科技' and status='1' and address='北京市';
ken_len = 3 * N + 2;
-- name varchar(100)  	==302
-- status varchar(1)  	==5
-- address varchar(100) ==302

 

2) 最左前缀法则

如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。

匹配最左前缀法则,走索引:

explain select * from tb_seller  where name='小米科技';  

 

违反最左前缀法则 , 索引失效:

explain select * from tb_seller  where status='1';
explain select * from tb_seller  where status='1'  and  address='北京市';

 

如果符合最左法则,但是出现跳跃某一列,只有最左列索引生效:

explain select * from tb_seller  where name='小米科技'  and  address='北京市';

 

3) 范围查询右边的列

-- 使用范围查询的情况,右边的列失效 
explain select * from tb_seller  where name='小米科技' and status='1'  and  address='北京市';
explain select * from tb_seller  where name='小米科技' and status>'1'  and  address='北京市';

 

根据前面的两个字段 name , status 查询是走索引的, 但是最后一个条件 address 没有用到索引。

4) 禁止列运算

-- 不要在索引列上进行运算操作, 索引将失效。
explain select * from tb_seller  where substring(name,3,2) ='科技';

 

5) 字符串不加单引号

造成索引失效。

-- 字符串不加单引号,造成索引失效。
explain select * from tb_seller  where name='科技' and status='0';
explain select * from tb_seller  where name='科技' and status=0;

 

由于,在查询时,没有对字符串加单引号,MySQL 的查询优化器,会自动的进行类型转换,造成索引失效。

本文由传智教育博学谷教研团队发布。

如果本文对您有帮助,欢迎关注点赞;如果您有任何建议也可留言评论私信,您的支持是我坚持创作的动力。

转载请注明出处!

与[转帖]【建议收藏】15755 字,讲透 MySQL 性能优化(包含 MySQL 架构、存储引擎、调优工具、SQL、索引、建议等等)相似的内容:

[转帖]【建议收藏】15755 字,讲透 MySQL 性能优化(包含 MySQL 架构、存储引擎、调优工具、SQL、索引、建议等等)

https://my.oschina.net/jiagoushi/blog/5593246 0. 目录 1)MySQL 总体架构介绍 2)MySQL 存储引擎调优 3)常用慢查询分析工具 4)如何定位不合理的 SQL 5)SQL 优化的一些建议 1 MySQL 总体架构介绍 1.1 MySQL 总体

[转帖]7600字带你学会 Redis 性能优化点, 建议收藏!

https://www.cnblogs.com/shoshana-kong/p/14040195.html 在一些网络服务的系统中,Redis 的性能,可能是比 MySQL 等硬盘数据库的性能更重要的课题。比如微博,把热点微博[1],最新的用户关系,都存储在 Redis 中,大量的查询击中 Redi

[转帖]太厉害了,终于有人能把Ansible讲的明明白白了,建议收藏

https://zhuanlan.zhihu.com/p/530678807 一: ansible 的概述 1. ansible简介 Ansible是一款为类Unix系统开发的自由开源的配置和自动化工具。 它用Python写成,类似于saltstack和Puppet,但是有一个不同和优点是我们不需要

[转帖]TCP半连接队列和全连接队列

TCP半连接队列和全连接队列 文章很长,建议收藏起来慢慢读! 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备免费赠送 经典图书:《Java高并发核心编程(卷1)》 面试必备 + 大

[转帖]使用电信号传输TCP/IP:如何收发数据包(MTU,MSS,包的序号SYN,确认号ACK,动态调整等待ACK时长,滑动窗口)

https://www.jianshu.com/p/a819a777d33c 连接建立起来后,也就是TCP建链后也就进入数据传输阶段。数据收发操作是从应用程序调用write将要发送的数据交给协议栈开始的,协议栈收到数据后执行发送操作。 首先,协议栈并不关心应用程序传来的数据是什么内容。应用程序在调用

[转帖]计算机网络【TCP的序号 确认号详解 TCP三次握手 和 四次挥手】

文章目录 初始TCP三次握手--建立连接再聊TCP的序号和确认号TCP建立连接--三次握手为什么需要三次握手,二次握手为什么不行?假如第三次握手失败,是如何处理的?TCP释放连接--四次挥手为什么断开连接需要4次挥手TCP释放连接--状态解读 初始TCP三次握手–建立连接 在发送方和接收方方收发TC

[转帖]Linux性能优化(四)——BCC性能监控工具

原文 https://blog.51cto.com/9291927/2593705 1、BCC简介 一、BCC简介 BCC是一个Python库,简化了eBPF应用的开发过程,并收集了大量性能分析相关的eBPF应用。BCC为BPF开发提供了不一样的前端支持,包括Python和Lua,实现了map建立、

[转帖]3.3.7. 自动诊断和建议报告SYS_KDDM

https://help.kingbase.com.cn/v8/perfor/performance-optimization/performance-optimization-6.html#sys-ksh KDDM 是 KingbaseES 性能自动诊断和建议的报告。它基于 KWR 快照采集的性能

[转帖]华为openGauss 单机部署

不建议用于生产. 单机部署形态是一种非常特殊的部署形态,这种形态对于可靠性、可用性均无任何保证。由于只有一个数据副本,一旦发生数据损坏、丢失,只能通过物理备份恢复数据。这种部署形态,一般用于数据库体验用户,以及测试环境做语法功能调测等场景。不建议用于商业现网运行。 图 1 单机部署形态图

[转帖]Redis 4.0 自动内存碎片整理(Active Defrag)源码分析

阅读本文前建议先阅读此篇博客: Redis源码从哪里读起 Redis 4.0 版本增加了许多不错的新功能,其中自动内存碎片整理功能 activedefrag 肯定是非常诱人的一个,这让 Redis 集群回收内存碎片相比 Redis 3.0 更加优雅,便利。我们升级 Redis 4.0 后直接开启了a