MySQL优化

mysql易学难精

Mysql的架构介绍

mysql简介

mysql概述

mysql优化

mysql5.5安装:

yum下载的mysql是5.1版本,但我们需要至少5.5版本

1、方法一

mysql开发者相关文档 https://dev.mysql.com/downloads/mysql/

下载5.5教程https://www.cnblogs.com/littlemonsterksn/p/10154662.html

wget是Linux系统中的一个下载文件的命令工具。对于经常要下载一些软件或从远程服务器恢复备份到本地服务器的用户来说是必不可少的工具。

没有wget就下一个:yum -y install wget

得到安装包:mysql-5.5.62-linux-glibc2.12-x86_64.tar.gz,解压后安装

方法二

在资料中找到了对应mysql5.5的rpm包,本地上传到Linux

pscp -r D:\MySQL\MySQL高级\mysql5.5.48 root@192.168.1.15:/opt

上传成功

1
2
3
4
5
6
7
mysqladmin --version有东西说明安装成功
service mysql start
mysql——出现欢迎词,开始按照提示设置密码
/usr/bin/mysqladmin -u root password 123456
mysql——出现报错
mysql -u root -p出现欢迎词
exit

top命令后,实时显示系统中各个进程的资源占用状况,即当前系统正在执行的进程的相关信息,包括进程ID、内存占用率、CPU占用率等。

可以参考:

https://www.cnblogs.com/peida/archive/2012/12/24/2831353.html

设置开机自启动mysql

1
2
3
chkconfig mysql on
chkconfig --list|grep mysql
ntsysv 命令用于设置系统的各种服务,报不是命令就下载-> yum install ntsysv

看到【*】mysql,表示开机后 自动启动mysql(带星号表示,开机自启动,反之相反)

修改配置文件位置:

将 /usr/share/mysql/my-huge.cnf拷贝到/etc下

cp /usr/share/mysql/my-huge.cnf /etc/my.cnf

1
2
3
service mysql stop
service mysql start
mysql -u root -p登录,建立数据库和表发现中文乱码

修改字符集和数据存储路径:

1
2
show variables like 'character%';
发现database(客户端)和server(服务器)都是用的latinl,所以乱码

在 /etc/my.cnf对应位置修改

修改完成后重新启动mysql,添加数据还是中文乱码原因:配置文件没错,否者不能重新启动mysql,乱码的原因是库在修改配置之前建立,因此装完mysql之后应该立即修改配置文件。

MySQL的安装位置——在linux下查看安装目录ps -ef|grep mysql

mysql存储引擎:

查看命令:

两种引擎可否混用?

引擎是针对表,而不是针对库的,在建表的时候声明成不同的引擎即可。 比如: CREATE TABLE test_1 ( name varchar(20) DEFAULT NULL, year int(5) DEFAULT NULL ) ENGINE=MyISAM

两种存储引擎区别

阿里巴巴、淘宝用哪个

索引优化

性能下降SQL慢、执行时间长、等待时间长的原因

1
2
3
4
5
1.查询语句写的烂
2.索引失效
3.关联查询太多join(设计缺陷或不得已的需求)

4.服务器调优及各个参数设置(缓冲、线程等)
索引写操作

索引主要分类:

1.主键索引 2.唯一索引3.普通索引 4.组合索引5.全文索引

索引CREATE创建

1
2
3
4
5
6
7
8
9
CREATE INDEX index_name ON  table_name (col_name,...)

单值索引
创建索引 索引名idx_表名_字段名 on 表名(字段名)
create index idx_user_name on user (name)
复合索引
创建索引 索引名(idx_表名_字段名字段名)on 表名(字段名,字段名)
create index idx_user_nameEmail on user (name,email)

在MySQL中,默认情况下,一个查询只能使用一个索引来访问数据。

不过,MySQL支持索引合并,允许在某些特定条件下使用多个索引。

索引合并

1.并集合并(Union Merge)

当查询的 WHERE 子句中包含多个条件,且这些条件之间使用 OR 连接时,MySQL 会分别扫描每个条件对应的索引,然后取结果的并集。

1
SELECT * FROM table WHERE key1 = 'value1' OR key2 = 'value2';

如果 key1key2 列上都有索引,MySQL 可能会选择使用并集合并策略,分别扫描这两个索引,获取满足每个条件的记录,然后合并结果集,返回满足任一条件的用户记录

2.交集合并(Intersection Merge)

查询的 WHERE 子句中包含多个条件,且这些条件之间使用 AND 连接时,MySQL 会分别扫描每个条件对应的索引,然后取结果的交集。

1
SELECT * FROM users WHERE first_name = 'John' AND last_name = 'Doe' AND age = 30;

如果 first_namelast_nameage 列上都有单独的索引,MySQL 的查询优化器可能会选择使用交集合并策略,分别扫描这三个索引,获取满足每个条件的记录,然后取这些记录的交集作为最终的结果集。

3.排序并集合并(Sort-Union Merge)

适用于 OR 连接的条件,特别是当涉及范围查询时。在这种情况下,MySQL 会先对每个索引查找的结果进行排序,然后再进行合并

1
SELECT * FROM table WHERE col1 < value1 OR col1 > value2;

不适合索引合并的情况:

1.索引合并主要适用于 B-tree 索引,全文索引不支持索引合并。

2.对于存在范围查询(如 BETWEEN<> 等)或 ORDER BY 子句的查询,MySQL 可能不会使用索引合并,而是选择使用单个索引或进行全表扫描。

3.如果某个索引的选择性较差(即该索引列中有大量重复值,区分度较小),查询优化器可能不会选择该索引进行合并。

当两个表 JOIN 时,如果两个表的索引都被使用,这表明每个表都利用了自己的索引进行高效的查找。这是独立索引的使用,不是索引合并。索引合并是MySQL 中一种用于在单个表上使用多个索引的优化策略。

单值索引和复合索引如何选择

单值索引

1
2
3
4
5
6
7
优点:
简单易用:单值索引结构简单,易于创建和维护。
适用范围广:适用于查询条件中涉及单个列的情况。
灵活组合:多个单值索引可以在某些情况下被 MySQL 的查询优化器合并使用(索引合并)。

缺点:
效率问题:对于涉及多个列的查询条件,单值索引可能无法充分发挥作用,查询效率可能不如复合索引。

复合索引

1
2
3
4
5
6
7
高性能:对于涉及多个列的查询条件,复合索引可以显著提高查询效率,尤其是当查询条件中的列顺序与复合索引的列顺序匹配时。
覆盖查询:如果查询中的所有列都在复合索引中,MySQL 可以直接通过索引获取数据,无需回表查询(覆盖索引)。
缺点:
维护成本高:复合索引的维护成本较高,插入、更新和删除操作可能会影响索引的性能。
顺序敏感:复合索引的列顺序非常重要,只有查询条件中的列顺序与复合索引的列顺序匹配时,才能有效利用索引。

在 MySQL 中,复合索引本身不会被MySQL查询优化器合并使用
  • 选择复合索引的场景
    • 查询条件中经常同时使用多个列。
    • 查询条件中的列具有明显的过滤顺序(某些列的过滤效果更好)。
    • 查询可以被复合索引覆盖(覆盖索引)。
  • 选择单值索引的场景
    • 表中的查询条件通常只涉及单个列。
    • 表需要支持多种不同的查询条件组合,难以通过复合索引来覆盖所有情况。
    • 表的插入、更新和删除操作非常频繁,且对性能要求较高。

ALTER命令添加数据表索引:

1.添加一个主键,意味着索引值必须是唯一的,且不能为NULL。

1
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list)

2.这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。

1
ALTER TABLE tbl_name ADD UNIQUE index_name(column_list)

3.添加普通索引,索引值可出现多次。

1
ALTER TABLE tbl_name ADD INDEX index_name(column_list)

4.该语句指定了索引为FULLTEXT,用于全文索引

1
ALTER TABLE tbl_name ADD FULLTEXT index_name(column_list)

全文索引可以高效地支持复杂的文本搜索操作,适用于**TEXTVARCHARCHAR**类型字段

删除索引

1
2
3
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name
ALTER TABLE table_name DROP PRIMARY KEY

mysql300万记录以内没问题,超过就需要优化。

常见通用的Join查询

SQL执行顺序

手写

机读

join关联图:内连接、外连接( 左(外)连接、右(外)连接、全(外)连接)、笛卡尔积

join 等同于 inner join,默认是内连接.

left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录。

right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录。

inner join(等值连接) 只返回两个表中联结字段相等的行。

1
2
3
select * from A inner join B on A.id = B.id;
select * from A join B on A.id = B.id;
select * from A,B where A.id = B.id;

MySQL不支持全外连接,但可以模拟,详细看《笛卡尔积-内连接-外连接》。

join语句优化:

主键自带主键索引(不需要建立)

尽可能减少join语句中NestedLoop的循环总次数(不要join过多或嵌套);

永远用小表驱动大表(根据数据量大小区分大表和小表,嵌套循环访问,详细看后面扩展);

优先优化NestedLoop的外层循环(即驱动表),其次是内层循环(被驱动表);

保证join语句中被驱动表(大表)上join字段已经被索引;

当无法保证被驱动表的join条件字段被索引,在内存资源充足的前提下,不要吝啬JoinBuffer的设置;

驱动表的索引优化是为了减少外层循环的次数,而内层循环的优化是为了减少每次内层循环的执行时间,两者相辅相成。

查询优化——永远小表驱动大表类似全套循环NestedLoop,这里的“小表”和“大表”是相对的,指的是表的数据量大小。

扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NestedLoop的工作原理————所谓嵌套
两个表 A(驱动表,outer table)和 B(被驱动表,inner table),Nested Loop 的执行过程如下:
1.从驱动表 A 中读取一行。
2.对于这一行,遍历被驱动表 B 中的所有行,查找满足连接条件的行。
3.重复上述步骤,直到驱动表 A 中的所有行都被处理完毕。
Nested Loop 的循环次数主要由驱动表的行数决定。因此,减少循环次数的关键在于尽可能降低驱动表的行数.以下是一些优化策略:
1.选择较小的表作为驱动表:将较小的表作为驱动表,可以减少外层循环的次数,从而减少总的循环次数。
2.使用索引:在连接条件和过滤条件上使用索引,可以减少被驱动表的扫描范围,从而减少内层循环的次数。
3.添加过滤条件:在驱动表上添加过滤条件,可以减少驱动表的行数。
4.优化查询顺序:通过调整查询顺序,使驱动表的行数尽可能少。

Nested Loop 和子查询的区别:
Nested Loop 是一种连接算法,用于执行表之间的连接操作。而子查询(Subquery)是一种查询嵌套方式,用于在查询中嵌套另一个查询。Nested Loop 主要用于多表连接,而子查询可以用于各种复杂的查询场景,包括单表查询和多表查询。

每join一个表就嵌套一层循环,但总体上是只查询了一次。而子查询,每多一个子查询,则会多一次查询。

怎么知道哪个表是驱动表哪个表是被驱动表:
在 SQL 查询中,特别是涉及多个表的 JOIN 操作时,驱动表(outer table)和被驱动表(inner table)的身份并不是固定的,而是由查询优化器根据多种因素动态确定的。通常较小的表更适合作为驱动表,因为扫描小表的速度更快,能更快地生成驱动行来访问被驱动表,当然主要还得看查询优化器的选择。
当没有索引时,优化器主要依据表的大小和数据量、查询条件的过滤效果等因素来决定连接顺序。较小的表或能通过条件过滤掉大量数据的表更可能被选为驱动表。
如果有索引,优化器会评估使用索引的成本,包括索引的查找成本和通过索引访问数据的成本。如果索引能够显著降低连接操作的总成本,优化器会调整连接顺序,将能高效利用索引的表作为驱动表。索引的存在使得优化器有更多选择来优化查询计划。
最直接和可靠的方法是使用 EXPLAIN 查看查询执行计划,从而知晓哪个表是驱动表或被驱动表。
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 100 NULL
1 SIMPLE t2 ref t1_id t1_id 4 t1.id 10 Using index
1 SIMPLE t3 ref t2_id t2_id 4 t2.id 5 Using index
1
2
3
4
explain可以查看查询执行计划,从而确定哪个表是驱动表,哪个表是被驱动表。

通过观察table列的顺序,可以确定查询中表的访问顺序。通常,第一个表(id最小的表)是驱动表,后续的表是被驱动表。在EXPLAIN输出中,id值相同的表属于同一个SELECT查询,id值较小的通常是外层查询,而较大的id值表示子查询或衍生表。id值越大,表示该查询越先被执行。
如上表,table1相对于table2是驱动表,table2相对于table3是驱动表,整体上看table1是驱动表,其他都是被驱动表。
性能分析

MySql Query optimizer(mysql查询优化器)

  1. MySql中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划(他认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间)

  2. MySQL Query Optimizer工作流程
    1)当客户端向MySQL请求一条Query,命令解析器模块完成请求分类,区别出是SELECT并转发给MySQL Query Optimizer。
    2)MySQL Query Optimizer首先会对整条Query进行优化,将一些常量表达式换算成常量值,并对查询条件进行简化和转换(如去掉一些无用或显而易见的条件、结构调整等)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    比如:
    SELECT * FROM table WHERE 1=1 AND column = 'value';
    简化处理后
    SELECT * FROM table WHERE column = 'value';

    SELECT * FROM table WHERE column = 'value1' OR column = 'value2' OR column = 'value3';
    转为:
    SELECT * FROM table WHERE column IN ('value1', 'value2', 'value3');

    即常量表达式计算、去掉无用的条件、去掉冗余的条件、转换OR为IN、 转换子查询为连接查询、转换连接查询(join)为子查询、转换OR条件为UNION、转换DISTINCT为GROUP BY等

    3)然后分析Query中的Hint信息(如果有),看显示的Hint信息是否可以完全确定该Query的执行计划。如果没有Hint或Hint信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据Query进行写相应的计算分析,然后再得出最后的执行计划。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    hint信息:hint(提示)是用户给数据库查询优化器的指令,用于指定查询的执行计划或影响优化器的选择。
    MySQL提供了多种类型的hint,比如USE INDEX:
    SELECT /*+ USE INDEX (idx_column1) */ column1, column2
    FROM table
    WHERE column1 = 'value1';
    或者IGNORE INDEX:
    SELECT /*+ IGNORE INDEX (idx_column1) */ column1, column2
    FROM table
    WHERE column1 = 'value1';
    还有:FORCE INDEX、JOIN ORDER、MAX_EXECUTION_TIME。

    hint信息是用户(如开发者或数据库管理员)根据对查询和数据库的理解,手动添加到SQL语句中的特殊指令。在大多数情况下,数据库的查询优化器能够自动为查询生成高效的执行计划,因此hint不是必需的。只有在查询优化器无法生成理想执行计划,且用户认为自己可以指定更好的计划时,才会考虑使用hint。hint不仅可以由数据库管理员(DBA)使用,也可以由应用开发者在编写SQL语句时添加。

mysql常见瓶颈(略)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.查询性能瓶颈
查询语句不合理、索引缺失或使用不当,会导致查询执行时间长。
2.硬件资源瓶颈
CPU瓶颈:数据库操作过程中,CPU资源消耗过大,可能导致响应延迟。高 CPU 使用率会使得数据库处理请求变慢。
内存瓶颈:内存不足会导致数据库缓存不足,增加磁盘 I/O 操作,降低查询效率。频繁的磁盘交换会严重影响性能。
磁盘I/O瓶颈:磁盘读写速度慢,会导致数据访问延迟,影响数据库性能。全表扫描、大量数据插入等操作会加重磁盘 I/O 负载。
3.锁竞争和死锁
过多的锁竞争或死锁现象会导致并发操作等待时间增加,降低系统性能。
4.大量慢查询
存在大量耗时较长的慢查询语句会导致系统性能下降。
5.数据库设计问题
不合理的表设计、冗余字段、过多的关联查询等会影响性能。
6.数据库连接池配置不当
连接池设置不合理,导致连接数不足或过多,影响系统的并发处理能力。
7.数据量和索引过大
当数据量庞大或索引过多时,查询性能会下降。可以考虑分区、分表等策略来减轻压力,提高查询效率。
8.错误配置或参数设置不当
MySQL的配置文件中的参数设置对性能有重要影响。若配置不当,可能导致性能瓶颈。可以根据实际需求进行适当的配置和参数调优。
Explain关键字:

https://www.cnblogs.com/dlp-527/p/11824467.html

用法:Explain+SQL语句,可以获取 SQL 查询的执行计划(执行计划是数据库查询优化器决定如何执行查询的详细步骤)。

id:查询中每个子查询或表的执行顺序标识符,id 相同,表示这些查询可以并行执行; id 不同,数字越大,优先级越高,越先被执行。

select_type:查询类型,描述了查询中每个 SELECT 子句的性质。常见的类型包括 SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)、UNION(联合查询的一部分)、derived(表示该查询是一个派生表)等。

table:显示与查询相关的表的名称。通过观察table列的顺序,可以确定查询中表的访问顺序。通常,第一个表(id最小的表)是驱动表,后续的表是被驱动表。

partitions:指示查询访问的表的分区信息(如果表被分区),不常用。

type: 查询类型,指示查询优化器如何访问表中的数据,这个字段的值对查询性能有很大影响。

possible_keys:表示查询优化器可能使用的索引,用于提高查询速度

**key:**表示实际被使用的索引。

key_len:表示在查询中实际使用的索引字段数据的长度,以字节为单位。

1
2
3
4
5
6
7
8
对于单值索引
key_len 表示索引字段的实际长度。例如,如果有一个 INT 类型的字段(通常占用 4 字节)作为索引,key_len 的值就是 4。如果是 VARCHAR(10) 字段,并且使用了完整的字段长度,key_len 可能是 30(假设字符集为 utf8,每个字符占用 3 字节,10 个字符即 30 字节)。

对于复合索引
key_len 表示使用的索引字段的总长度。例如,如果联合索引包含两个字段:INT 类型(4 字节)和 CHAR(10) 类型(假设占用 10 字节),那么 key_len 的值可能是 14。

对于字符串类型字段
key_len 会显示实际使用的字节长度。例如,如果有一个 VARCHAR(255) 字段,但实际查询中只使用了前 10 个字符,key_len 可能是 30(假设字符集为 utf8)。

**ref:**指示与索引列进行比较的列或常量。

rows:表示查询优化器预计需要扫描的行数。这个值越小,查询性能越好。

filtered:表示查询优化器预计经过过滤后保留的行的百分比,值越高,说明筛选条件越严格。

Extra:额外的比较重要的信息,如 Using where(使用了 WHERE 条件过滤结果)、Using index(表示使用索引覆盖查询,无需回表查询)、Using filesort(需要额外的排序操作)、Using temporary(使用了临时表)、Using join buffer(表示使用连接缓冲区)等。

拓展:

select_type中的DERIVED:表示这个查询后的结果是一个派生表,即查询的结果被当作一个临时表使用,通常这个临时表是由子查询的结果构成的,也称为子查询或内联接子查询。派生表可以包含在主查询的 FROM 子句中,就像它是一个普通表一样,允许你对子查询的结果进行操作,比如进一步的筛选、排序或联接其他表。

select_typeDERIVED 时,这通常意味着查询优化器会递归执行子查询,并将结果放入一个临时表中,然后主查询可以使用这个临时表。这种派生表的使用可能会影响查询性能,特别是当子查询复杂或返回大量数据时。在优化查询时,考虑将派生表转换为连接(JOIN)或其他形式,可能会提高效率。

**type:**又称“访问类型”,表示表连接的类型,反映了MySQL决定如何查找表中的行。是较为重要的一个指标。结果从最好到最坏依次是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

system: 这是 const 连接类型的一个特例,表中只有一行数据或者是空表。

const:常量表(例如,查询主键为常量的表)。通常发生在使用 PRIMARY KEY 或 UNIQUE 索引作为条件进行查找时,返回记录一定是1行记录的等值where条件。其他数据库也叫做唯一索引扫描,当查询的表仅有一行时,使用 system。

eq_ref:唯一索引扫描,类似于 const,但用于多个值匹配的情况(例如,两表一对多的关系)。每个索引键最多只匹配一行,如 PRIMARY KEY 或 UNIQUE 索引的全部索引部分被匹配。

ref:非唯一索引扫描表,返回数据不唯一的等值查找就可能出现。
range:索引范围扫描,使用索引来查找一定范围内的值。
Index:全索引扫描,查询优化器决定扫描整个索引。
all:最差,指全表扫描数据文件,然后在server层进行过滤返回符合要求的记录。最坏的情况,从头到尾全表扫描。百万记录以下不用管,百万以上需要优化。
----------------------
fulltext:使用全文索引查找文本列中的关键词。
ref_or_null:类似于 ref,但索引列的值可以是 NULL。
index_merge:使用索引合并优化查询,即查询使用了两个以上的索引。
unique_subquery:子查询返回唯一值。
index_subquery:子查询返回多个值。

type为all的地方,通常需要进行优化,一般需要达到 ref、eq_ref 级别,范围查找需要达到 range。

例子:

id=2,最先执行,select_type为DERIVED表示这个查询的结果是一个派生表。也就是在 FROM 子句中的子查询,其结果被当作一个临时表来使用。在这个例子中,子查询 select * from t1 where id = 1 就是一个派生表,别名为 d1。它是where等值查询主键,type为const,常量表查找。

id=1,为外面一层查询,select_type为PRIMARY,是主键查询,table 为 <derived2><derived2>表示这是第二个查询(主查询中的派生表)的结果集,实际上对应的是 d1 这个派生表,type为system,派生表d1是一个只有一行的表,所以连接类型为 system

extra:额外的比较重要的信息,包含了查询优化器在执行查询时使用的额外步骤或者特性。有Using filesort、Using temporary 的一定需要优化,根据rows可以直观看出优化结果。

1
2
3
4
5
6
7
8
9
using index:查询时不需要回表查询,直接通过索引就可以获取查询的数据。利用覆盖索引,无需回表即可取得结果数据,这种结果是好的。

using where:在查找使用索引的情况下,需要回表去查询所需的数据,需要在server层进行过滤。

using filesort:
不是利用磁盘的文件排序,而是通过内存排序。排序时无法使用到索引时,就会出现这个。
常见于order by和group by语句中。没有办法利用现有索引进行排序,需要额外排序,建议:根据排序需要,创建相应合适的索引。

using temporary:表示使用了临时表存储中间结果。临时表可以是内存临时表和磁盘临时表,执行计划中看不出来,需要查看全局状态变量Created_tmp_tables和Created_tmp_disk_tables,它们分别记录了创建的内存临时表和磁盘临时表的数量,检查执行语句前后,这两个的变化情况,就能确定是内存还是磁盘。

覆盖索引(Covering Index),一说为索引覆盖。

1
2
3
4
理解方式一:select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说**查询列要被所建的索引覆盖**。

理解方式二:索引是高效找到行的一个方法,一般来说,数据库能通过索引找到一个列的数据,而不必读取整个行,因为索引叶子节点存储了它们索引的数据;
当能通过读取索引就可以得到想要的数据,也就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。

注意:如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *,因为若将所有字段一起做索引会导致索引文件过大,查询性能下降。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
执行顺序1:
id为4,select_type为union,说明第四个select是union里的第二个select,最先执行【select name,id,from t2】

执行顺序2:
id为3,是整个查询中第三个select的一部分。因查询包含在from中,所以为derived,即结果作为派生表。【select id,name from t1 where other_column = ''】

执行顺序3:
id为2,select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】

执行顺序4:
id为1,表示是union里的第一个select,select_type列的primary表示该查询为外层查询,table列被记为<derived3>,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name....】

执行顺序5:
id为null,代表从union的临时表中读取行的阶段,table列的<union1,4>表示用第一个和第四个select的结果进行union操作。【两个结果union操作】

<union1,4> 表示该行是 UNION 操作的结果集,它合并了查询中第一个和第四个 SELECT 语句的结果。UNION 操作会将两个或多个 SELECT 语句的结果集合并成一个结果集。
通过explain优化

索引失效,口诀:

简化:带头大哥不能少,中间兄弟不能断,索引列上少计算,范围之后全失效,百分like加右边,字符串里有引号。

解释:带头大哥不能少(第一字段,只要用了,不是非得它开头),中间兄弟不能断(断了索引只能用到前面的,后面用不上影响精确度,key_len和ref少了),索引列上少计算(动了容易索引失效,性能降低),范围之后全失效(索引用到范围字段为止,后面用不上索引),百分like加右边(若like '%字符%'必须用,则用覆盖索引解决),字符串里有引号(比如varchar类型,查询必须加单引号,如name=‘XXX’,否者索引失效)

单表分析:

range类型查询字段后面的索引无效,出现using filesort->删除索引,重建索引(将>的字段去掉`)===中间兄弟没有断,索引字段可以跨where和order by使用。???

category_id = 1 可以利用索引快速定位,同时 ORDER BY views DESC 也可以利用索引的顺序,避免了文件排序(Using filesort 消失),提高了查询效率。

双表分析:

其实将class和book对调一下位置查询也是可以的,不用再删除

三表分析:

LEFT JOIN 中,驱动表通常是左边的表,尽管优化器可以调整连接顺序以提高性能,但在 LEFT JOIN 中,左表通常会被选为驱动表,因为必须保证左表的所有行都被保留。

建立索引:

  • 连接条件列 :通常,应该在两个表的连接条件列上设置索引。对于左表,如果连接列上有过滤条件,索引会更有帮助。对于右表,索引可以加速匹配过程。
  • 查询过滤条件列 :如果查询中有针对某个表的过滤条件,那么在该表的相应列上设置索引可以提高过滤效率。
  • 覆盖查询索引 :理想情况下,索引应该覆盖查询中所有使用的列(即索引包含查询中涉及的所有列),以避免回表操作,提高查询性能。
1
后2行的type都是ref且总rows优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。
查询优化(where)

注意:

1.尽量的扩展索引,不要新建索引

1
例如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

2.最左前缀匹配原则

1
2
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。
比如a = 1 and b = 2 and c > 3 and d = 4, 如果建立(a,b,c,d)顺序的索引,abc用到,d是用不到索引的,如果建立(a,b,d,c)的索引,则索引中的字段都可以用到,where后a,b,d的顺序可以任意调整。

3.=和in可以乱序

1
2
3
4
5
6
7
8
9
10
11
12
13
比如建立(a,b,c)索引,where后a = 1 and b = 2 and c = 3 可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。
复合索引来说(a,b,c)其相当于3个索引(a),(a,b),(a,b,c)3个索引。
in可以乱序. 但会导致回表查询——using where

where和order by的字段会联和使用索引吗?
答:会。
如果表中建立2个单值索引c和v,where 用c,order by用v。实际上索引只用到了c,v并没有用到。由于查询优化器通常不会同时使用两个索引,order by排序操作无法用到索引v,需要回表查询,需要额外的处理,如文件排序Using filesort。
而如果建立复合索引cv,则where条件有c,order by中有v排序,则形成了一个覆盖索引,使得查询和排序都可以直接通过索引完成,无需回表查询数据,避开Using filesort。
若where用索引order by使用非索引字段,会导致filesort ,若where+in字段+order by非索引会导致索引失效。

为什么where和order by使用单值索引,无法形成索引合并?
答:因为查询优化器通常不会同时使用两个索引来同时满足 WHERE 和 ORDER BY 子句。因为索引合并主要用于在一个查询中使用多个索引进行条件过滤(如 OR 条件),而不是同时用于过滤和排序。如果查询需要排序,而排序列没有包含在用于过滤的索引中,MySQL 可能需要进行额外的排序操作(如文件排序 Using filesort),或者重新扫描数据以应用排序。
即一条查询sql语句,查询优化器只会使用一个索引,而不会使用2个索引,除非复合索引合并的策略,比如or或and 或or中参和的范围查询。

4.索引列排序

1
2
MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。
因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

5.尽量选择区分度高的列作为索引

1
区分度的公式是count(distinct col)/count(*),表示字段中数据不重复的比例,比例越大扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0

一般需要join的字段都要求是0.1以上,即平均1条扫描10条记录。

6.OR和AND

1
2
OR条件的联合(union),AND条件的相交(intersection),两种情况的联合及相交,做联合操作时(通常是 or),通常耗费CPU、内存资源。
如果是一个是单列索引,另一个非索引,那么会引发回表查询, explain 的 extra 显示 using where,此时先根据单列索引查到数据后,再回表进一步筛选非索引列。

7.使用短索引

1
对字符串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和提高了索引的缓存效率,从而间接减少了磁盘 I/O 操作的次数。

短索引创建:

1
2
CREATE INDEX idx_city ON table_name (city(3));
这里city(3)表示索引只包含city字段的前3个字符,在应用短索引后,应该通过测试来评估其对查询性能的影响,确保它确实提高了效率。

参考:https://blog.csdn.net/qq_22238021/article/details/80922166

实操

1
2
3
4
5
6
7
8
type: ref 表明使用了非唯一索引进行查找。
使用了索引idx_test03_c1234,实际使用的字节数为62,
ref: const,const 表示查询中使用了常量值来匹配索引字段。
rows: 1 表示预计需要访问的行数。
Using where 表示需要额外的条件过滤。
没有 Using index 表示没有覆盖索引,需要回表查询。

where匹配了c1c2前俩个字段用于过滤,order by利用c3的索引顺序进行排序,避免了using filesort文件排序,由于where跳过c3,所以c4需要回表查询获取。即最终是用到c1c2,c3用于排序但不统计。

1
2
分析第一条sql:where过滤用到了c1,order by 利用索引排序c2、c3直接获得,避免了using filesort文件排序。where中的c5需要回表查询。即最终用到了c1。
分析第二条sql:where过滤用到了c1,order by 用的是c3、c2不符合索引顺序,所以需要回表查询,并且使用using filesort文件排序。where中的c5需要回表查询。即最终用到了c1。

1
2
3
分析第一条sql:where过滤用到了c1、c2,order by 用的是c3、c2不符合索引顺序,但由于c2已经被过滤使用,所以order by只需要排序c3,符合索引顺序,无需回表查询排序,避开了using filesort文件排序。where中的c5需要回表查询。即最终用到了c1、c2。
分析第二条sql:
where过滤用到了c1,order by 用的是c3、c2不符合索引顺序,所以需要回表查询,并且使用using filesort文件排序。where中的c5需要回表查询。即最终用到了c1。

where a=3 and b like ‘kk%’ and c=4

like 后面是常量+%,虽是范围,但可落地,后面索引不失效

一般性建议

查询优化(order by)

若where未使用索引,order by使用了索引,有可能出现索引合并吗?

答:这种情况下不会出现索引合并,因为 WHERE 子句没有利用索引,说明它没有从索引中获取到数据行的过滤信息。而 ORDER BY 子句只是使用索引来对结果进行排序,并没有参与到行的过滤过程中。索引合并主要是为了优化行的过滤过程,提高 WHERE 子句的查询效率。在这种情况下,行的过滤是通过全表扫描等方式来进行的,没有出现多个不同索引用于过滤行的情况,所以不会发生索引合并。

where没有利用到索引,而order by利用到索引。这种情况也是有意义的,虽然需要回表查询数据,但索引排序可以减少排序的时间,避开了文件排序(filesort)。

order by多个字段,有索引会使用索引排序,但其中任何一个字段没有索引,就会利用filesort进行辅助排序,会有额外的IO开销和内存开销。filesort目前有两种排序算法,双路排序和单路排序。

双路排序与单路排序

1
2
双路排序查询数据时,只是将排序字段和记录ID加载到内存中,排好序后再根据id回表加载数据。 
单路排序会一次性将数据查出到内存中,占用内存大,但效率高,因为排好序后就已经是可以返回的结果了。

**ORDER BY 多字段排序时,索引失效的主因是:

  1. 字段顺序不匹配ORDER BY 后的字段顺序与复合索引的字段顺序不一致。比如索引abc,而order by b,c,a,顺序不一致,导致索引失效。
  2. 排序方向不匹配ORDER BY 后的字段的排序方向与复合索引定义的排序方向不一致。比如索引a asc,b asc,而order by a desc,b asc。

在建立复合索引时,可以建立一个一升序一降序的复合索引,通常不建议(会导致存储和维护更加复杂),而是使用默认排序(通常是升序)。

1
CREATE INDEX idx_example ON table_name (column1 ASC, column2 DESC);

1
2
3
4
5
6
7
8
9
分析:
type为index表明使用了全索引扫描
key为idx_A_ageBirth表明实际使用的索引是idx_A_ageBirth。
extra中
Using index 表示查询使用了索引,这意味着索引被用于快速检索数据行。
Using filesort 表示 MySQL 需要进行额外的文件排序操作,而不是直接利用索引的顺序。

key不是null,type不是all,extra中有”Using index;Using filesort“,说明不是所有失效,而是索引部分失效,原因是一升一降,与索引的默认字段排序不一致。
在 MySQL 中,即使索引的默认顺序是升序(ASC),而查询使用的是降序(DESC)排序,索引仍然可以被使用。这是因为MySQL 的优化器会 可以通过反向扫描索引来满足降序排序的需求。

提高Order By的速度

小总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MySQL两种排序方式:文件排序、扫描有序索引排序
排序和查询能使用相同的索引

KEYa_b_c(a_b_c)
order by能使用索引最左前缀
-order by a
-order by a,b
-order by a,b,c
-order by a desc,b desc,c desc

如果where使用索引的最左前缀,order by能使用索引
-where a=const order by b,c
-where a=const and b=const order by c
-where a=const and b> const order by b,c
where会用到a和b字段过滤,order by中由于b字段已经被过滤,只需要考虑c,符合索引字段顺序,order by利用索引排序而无需额外的文件排序操作。
-where a in(...) order by b,c
where子句索引只用到了a,order by 的字段和索引字段顺序一致,索引order by利用索引排序而无需额外的文件排序操作。

不能使用索引进行排序
order by a asc,b desc,c desc //字段排序不一致
where g= const order by b,c //丢失a索引
where a= const order by c //丢失b索引
where a= const order by a,d //d不是索引的一部分
查询优化(group by)

无法使用索引时,group by 使用两种策略来完成:临时表或者文件排序。

可以使用索引时,where和group在索引上的联系,类似于where和order by在索引上的联系,文件排序的预知相对类似(只要符合索引字段顺序),但在临时表上,相对难以预知,情况更复杂。

1
2
3
HAVING子句通常用于对分组后的结果再处理(在分组后的结果基础上过滤),where高于having,能写在where限定的条件就不要去having限定。

当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置。

临时表产生的原因:

1
2
3
4
5
6
1.索引覆盖和顺序
索引是否覆盖了分组字段,以及是否覆盖了 SELECT 列表中所有需要的字段。如果都覆盖了,那么有较大可能不使用临时表。如果查询需要的字段不在索引中,即使分组字段有索引支持,也可能需要临时表来存储中间结果。
2.聚合函数的影响
当查询中有聚合函数(如 COUNT(*)、SUM() 等)时,即使索引覆盖了分组字段,也可能需要使用临时表来辅助计算聚合结果。因为需要先对分组后的数据进行聚合计算,再返回最终结果。
3.多表连接和子查询
如果查询涉及到多个表的连接、子查询等复杂操作,情况就更加复杂。优化器需要综合考虑多个因素来决定是否使用临时表或文件排序。

group by实质是先排序后进行分组(在MySQL的早期版本是这样,但从MySQL 8.0开始,GROUP BY 语句不会在分组前先进行排序,默认也不再包含隐式排序。),遵照索引的最佳左前缀。这里的“排序”指 GROUP BY 操作本身的内部排序。

查询截取分析

1
2
3
4
5
1.观察,至少跑1天,看看生产的慢SQL情况。
2.开启慢查询日志,设置阙值,比如超过5秒钟就是慢SQL,并将它抓取出来。
3.explain + 慢SQL分析
4.show profile 检查执行细节和生命周期
5.运维经理 或 DBA,进行SQL数据库服务器的参数调优

总结

1.慢查询的开启并捕获

2.explain + 慢SQL分析

3.show profile查询SQL在MySQL服务器里面的执行细节和生命周期情况

4.SQL数据库服务器的参数调优。

Show profile :

是mysql 提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于sql 调优的测量。默认情况下,参数处于关闭状态,并保存最近15次的运行结果

慢查询日志

用select sleep(4)命令,模拟超过超过3s的慢sql

mysqld配置:

1
2
3
4
5
6
7
slow_query_log=1

slow_query_log_file=/var/lib/mysql/atguigu-slow.log

long_query_time=3

log_output=FILE

日志分析工具mysqldumpslow

1
>mysqldumpslow --help

mysqldumpslow的帮助信息

1
2
3
4
5
6
7
8
9
10
s:是表示按照何种凡=方式排序
c:访问次数
I:锁定时间
r:返回记录
t:查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询时间
t:即为返回前面多少条的数据
g:后边搭配一个正则匹配模式,大小写不敏感的

批量数据脚本

插入数据1000万条,为防止压坏,每次插入50w条数据

建表插入数据后会报错,解决办法

1、随机产生字符串

substring(字符串,开始位置,长度)截取字符串

concat(str,str)字符串连接,如果有任何一个参数为null,则返回值为null

concat(str1, seperator,str2,seperator,…)返回结果为连接参数产生的字符串并且有分隔符,如果有任何一个参数为null,则返回值为null

2、随机产生部门编号

3、创建存储过程

emp表中插入数据insert_emp(开始编号,插入条数)

删除

1
delimiter;

drop procedure insert_em;

dept表中插入数据

4、调用存储过程

​ 插入10个部门

Show Profile

1、查看是否支持

1
>Show  variables like 'profiling';

2、开启功能

1
>set profiling=on;

3、运行sql

4、查看结果

1
>show profile

5、诊断sql

1
>show profile cpu,block io for query 

全局查询日志

只能在测试环境用,不可用于生产

配置启用

编码启用

子查询和JOIN如何选择

每join一个表就嵌套一层循环,但总体上是只查询了一次。而子查询,每多一个子查询,则会多一次查询。

MySQL中的子查询和JOIN查询在性能上有所不同,具体取决于数据量和查询的类型。以下是关于这两种查询方式的详细讨论:

JOIN查询:

  • 优点:JOIN查询可以将多个表的数据通过关联条件联合起来,一次性处理多个表的记录,提高了查询效率。
  • 缺点:如果在JOIN查询中使用的连接条件不当,可能会导致查询结果出错。另外,由于需要连接多个表,因此对于连接的表数量的上限有所限制,超过这个数量后,查询性能可能会降低。

子查询:

  • 优点:子查询能够在一个查询中嵌套另一个查询,实现更为复杂的查询逻辑。
  • 缺点:子查询会导致查询过程变得更复杂,因为它们通常在主查询执行之前就已经完成计算。如果子查询的结果集非常大,其执行速度可能相对较慢。

在某些情况下,如数据量较小(例如1000条数据),使用JOIN查询的时间会更短,而在数据量较大的情况下(例如1000000条数据),使用IN子查询的性能会更好。

例如:

1
2
3
4
5
SELECT name, department_id
FROM employees
WHERE department_id IN (SELECT department_id
FROM departments
WHERE department_name = 'Sales');

然而,如果子查询的结果集过大,可能导致SQL语句过长,甚至无法执行。此外,如果存在分页需求,IN子查询也无法满足。

综上所述,JOIN查询在大多数情况下是更好的选择,尤其是在处理大量数据时。但是,如果子查询的结果集很小且不需要分页,那么子查询也是一个有效的解决方案。在实际应用中,应根据具体的业务需求和数据量来选择最合适的方法。

可参考:

https://blog.51cto.com/u_16099232/6712681

https://www.shence123.com/s/23262.html

子查询可以分页吗?

子查询(Subquery)本身可以用于实现分页功能,但是它们并不总是分页查询的最佳选择。

1
2
3
4
SELECT * FROM (
SELECT * FROM your_table ORDER BY some_column LIMIT 1000
) AS subquery
LIMIT 10 OFFSET 90;

外层查询的 LIMIT 10 表示要获取10条记录,OFFSET 90 表示从内部查询结果的第91条记录开始获取。

分析:

子查询可能遇到以下问题:

  1. 性能问题:如果内部查询返回了大量的数据,这可能会导致性能问题,因为数据库需要先处理内部查询的所有数据,然后再由外部查询进行分页。
  2. 资源消耗:子查询可能会消耗更多的服务器资源,尤其是当处理大量数据时。
  3. 复杂性:在某些情况下,子查询可能会使 SQL 语句变得更加复杂,难以理解和维护。
  4. 限制:在某些数据库系统中,子查询的使用可能受到限制,或者在性能上不如直接使用 LIMITOFFSET

通常建议:

  • 直接使用 LIMITOFFSET:这是最简单直接的方法,适用于大多数情况。
  • 使用行号:在一些数据库系统中,可以使用行号(如 MySQL 的 ROW_NUMBER() 函数)来实现分页,这通常比使用 OFFSET 更高效。
  • 索引:确保用于排序和分页的列上有适当的索引,以提高查询性能。

如何将Mysql2个性质相同的表的数据做排序分页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1. 使用 UNION ALL 合并两个表的数据
UNION ALL 用于合并两个或多个 SELECT 语句的结果集。UNION ALL 会保留重复记录,而 UNION 会自动去除重复记录。如果不需要去重,使用 UNION ALL 可以减少内存消耗和处理时间。
2. 使用 ORDER BY 进行排序
在合并后的结果集上使用 ORDER BY 进行排序。
3. 使用 LIMIT 和 OFFSET 进行分页
在排序后的结果集上使用 LIMIT 和 OFFSET 进行分页。

SELECT id, name, value
FROM (
SELECT id, name, value FROM table1
UNION ALL
SELECT id, name, value FROM table2
) AS combined
ORDER BY value DESC
LIMIT 2 OFFSET 2;

注意:
1.字段一致性 :确保两个表的字段数量和类型一致,否则 UNION ALL 可能会报错。
2.性能 :如果两个表的数据量较大,UNION ALL 和 ORDER BY 可能会影响性能。可以考虑为排序字段添加索引以提高效率。
3.去重 :如果需要去除重复记录,可以使用 UNION 代替 UNION ALL,但 UNION 会自动对结果进行排序,这可能会增加额外的性能开销。

如果数据量过大,内存不足怎么办?
1.两个表数据合并到一个表中。
2.调整 MySQL 配置:增加可用内存(如 tmp_table_size 和 max_heap_table_size),以支持更大的内存表操作。为合并操作中的排序字段添加索引,可以提高排序效率,减少内存消耗。
3.分批处理数据:使用 LIMIT 和 OFFSET 来分批获取数据
SELECT *
FROM (
SELECT * FROM table1
UNION ALL
SELECT * FROM table2
) AS combined
ORDER BY id
LIMIT 1000 OFFSET 0;
4.使用临时表
会话结束 :当创建临时表的客户端会话结束时,临时表会自动删除。这是为了释放资源并避免临时表占用过多存储空间。
显式删除 :可以使用 DROP TEMPORARY TABLE 语句显式删除临时表。

CREATE TEMPORARY TABLE temp_table AS
SELECT * FROM table1
LIMIT 1000 OFFSET 0;

INSERT INTO temp_table
SELECT * FROM table2
LIMIT 1000 OFFSET 0;

SELECT * FROM temp_table
ORDER BY id;