欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MySQL高级(一)mysql架构及索引

程序员文章站 2022-03-09 08:52:54
...

一、mysql的sql_mode配置

概述:sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入。

1.1 有关group by子句的问题

mysql中,select的一个子句group by在查询时很容易犯一个难以发现的错误。那就是group by不允许select(查)它后面没有跟的字段(但是select查询分段函数可以)。
例如:

CREATE TABLE mytbl2 (id INT,NAME VARCHAR(200),age INT,dept INT);
INSERT INTO mytbl2 VALUES(1,'zhang3',33,101);
INSERT INTO mytbl2 VALUES(2,'li4',34,101);
INSERT INTO mytbl2 VALUES(3,'wang5',34,102);
INSERT INTO mytbl2 VALUES(4,'zhao6',34,102);
INSERT INTO mytbl2 VALUES(5,'tian7',36,102);
#每个部门年龄最大的人
SELECT NAME,dept,MAX(age) FROM mytbl2 GROUP BY dept;

MySQL高级(一)mysql架构及索引
此时发现最大值的name都是错的。101组年龄最大的是li4;102组最大的是tian7。
正因为sql_mode默认为null,所以会造成这个问题。
1.查询sql_mode

show  variables like 'sql_mode';

2.修改sql_mode常用值:

set sql_mode='ONLY_FULL_GROUP_BY';

3.了解其他sql_mode常用值:
ONLY_FULL_GROUP_BY:
对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,因为列不在GROUP BY从句中
NO_AUTO_VALUE_ON_ZERO:
该值影响自增长列的插入。默认设置下,插入0或NULL代表生成下一个自增长值。如果用户 希望插入的值为0,而该列又是自增长的,那么这个选项就有用了。
STRICT_TRANS_TABLES:
在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做限制
NO_ZERO_IN_DATE:
在严格模式下,不允许日期和月份为零

二、Mysql逻辑架构

2.1 概述

和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,==插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。==这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

2.2 逻辑架构

MySQL高级(一)mysql架构及索引

2.2.1 步骤

驱动Connectors–>CONNECTION Pool 连接池 -->缓存区Caches --> SQLinterface接入SQL接口查–> PARSER 解析器解析 -->Optimizer 优化器优化 -->引擎查询 -->返回结果给Connector,并缓存一份.
查询流程详细说明
1.mysql客户端通过协议与mysql服务器建连接,发送查询语句,先检查查询缓存,如果命中,直接返回结果,否则进行语句解析,也就是说,在解析查询之前,服务器会先访问查询缓存(query cache)——它存储SELECT语句以及相应的查询结果集。如果某个查询结果已经位于缓存中,服务器就不会再对查询进行解析、优化、以及执行。它仅仅将缓存中的结果返回给用户即可,这将大大提高系统的性能。
2.语法解析器和预处理:首先mysql通过关键字将SQL语句进行解析,并生成一颗对应的“解析树”。mysql解析器将使用mysql语法规则验证和解析查询;预处理器则根据一些mysql规则进一步检查解析数是否合法。
3.查询优化器当解析树被认为是合法的了,并且由优化器将其转化成执行计划。一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。
4.然后,mysql默认使用的BTREE索引,并且一个大致方向是:无论怎么折腾sql,至少在目前来说,mysql最多只用到表中的一个索引。

2.2.2架构说明

1)连接层
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
2)服务层
2.1 Management Serveices & Utilities: 系统管理和控制工具
2.2 SQL Interface: SQL接口
接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface
2.3 Parser: 解析器
SQL命令传递到解析器的时候会被解析器验证和解析。
2.4 Optimizer: 查询优化器。
SQL语句在查询之前会使用查询优化器对查询进行优化。
用一个例子就可以理解: select uid,name from user where gender= 1;
优化器来决定先投影还是先过滤。
2.5 Cache和Buffer: 查询缓存。
如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。
这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等。
3)引擎层
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。后面介绍InnoDB
4)存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。

2.3利用show profile 查看sql的执行周期

先开启 show variables like '%profiling%';

set profiling=1;
 
select * from xxx ;
 
show profiles;     

#显示最近的几次查询
show profile cpu,block io for query Query_ID #查看程序的执行步骤

三、Mysql存储引擎

3.1如何查看存储引擎

#看你的mysql现在已提供什么存储引擎:

mysql> show engines;

#看你的mysql当前默认的存储引擎:

  mysql> show variables like '%storage_engine%';

3.2 各个引擎简介

1.InnoDB存储引擎
InnoDB是MySQL的默认事务型引擎,它被设计用来处理大量的短期(short-lived)事务。除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。
2.MyISAM存储引擎
MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持事务和行级锁,有一个毫无疑问的缺陷就是崩溃后无法安全恢复。
3.Archive引擎
Archive档案存储引擎只支持INSERT和SELECT操作,在MySQL5.1之前不支持索引。
Archive表适合日志和数据采集类应用。
根据英文的测试结论来看,Archive表比MyISAM表要小大约75%,比支持事务处理的InnoDB表小大约83%。
4.Blackhole引擎
Blackhole引擎没有实现任何存储机制,它会丢弃所有插入的数据,不做任何保存。但服务器会记录Blackhole表的日志,所以可以用于复制数据到备库,或者简单地记录到日志。但这种应用方式会碰到很多问题,因此并不推荐。
5.CSV引擎
CSV引擎可以将普通的CSV文件作为MySQL的表来处理,但不支持索引。
CSV引擎可以作为一种数据交换的机制,非常有用。
CSV存储的数据直接可以在操作系统里,用文本编辑器,或者excel读取。6.Memory引擎
如果需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没有关系,那么使用Memory表是非常有用。Memory表至少比MyISAM表要快一个数量级。

3.3 MyISAM和InnoDB

MySQL高级(一)mysql架构及索引
关注点也不同:InnoDB节省资源、消耗少、简单业务;而MyISAM适合并发写、事务、更大资源。

四、索引简介及分类

4.1 索引产生的原因

(1)性能下降SQL慢 ;(2)执行时间长;(3) 等待时间长

4.2 索引是什么

(1)官方定义:索引(Index)是帮助MySQL高效获取数据的数据结构。
(2)索引的本质:索引是数据结构。
(3)可以简单理解为“排好序的快速查找数据结构”。
详解:
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
一种可能的索引方式示例:
MySQL高级(一)mysql架构及索引
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址。
为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。

4.3 索引的优劣势

优势:1.提高数据检索的效率,降低数据库的IO成本;2.通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗;
劣势:虽然索引大大提高了查询速度,同时却会降低更新表的速度,因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段。
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的

4.4 mysql索引结构

4.4.1 BTree索引

MySQL高级(一)mysql架构及索引

4.4.2 B+Tree索引

MySQL高级(一)mysql架构及索引
B+Tree与B-Tree 的区别:
1)B-树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的索引,记录只放在叶子节点中。
2)在B-树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。

4.5mysql索引分类

1.单值索引
即一个索引只包含单个列,一个表可以有多个单列索引。

单值索引创建语法:

(1)随表一起建索引:

CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name)
);
  

(2)单独建单值索引:

CREATE  INDEX idx_customer_name ON customer(customer_name); 

(3)删除索引:

DROP INDEX idx_customer_name  on customer;

2.唯一索引
索引列的值必须唯一,但允许有空值。

唯一索引创建语法

(1)随表一起建索引:

CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),
  UNIQUE (customer_no)
);
  

(2)单独建唯一索引:

CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no); 

(3)删除索引:

DROP INDEX idx_customer_no on customer ;

3.主键索引
设定为主键后数据库会自动建立索引,innodb为聚簇索引(聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。术语‘聚簇’表示数据行和相邻的键值聚簇的存储在一起。)

主键索引创建语法

(1)随表一起建索引:

CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id) 
);
   
CREATE TABLE customer2 (id INT(10) UNSIGNED   ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id) 
);2

)单独建主键索引:

ALTER TABLE customer 
 add PRIMARY KEY customer(customer_no);  

(3)删除建主键索引:

ALTER TABLE customer 
 drop PRIMARY KEY ;  

(4)修改建主键索引:
必须先删除掉(drop)原索引,再新建(add)索引
4.复合索引
即一个索引包含多个列

复合索引创建语法

(1)随表一起建索引:

CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),//这是单值索引
  UNIQUE (customer_name),
  KEY (customer_no,customer_name)//复合索引
);2

)单独建索引:

CREATE  INDEX idx_no_name ON customer(customer_no,customer_name); 

(3)删除索引:

DROP INDEX idx_no_name  on customer ;

基本语法总结

1.创建
CREATE [UNIQUE ] INDEX [indexName] ON table_name(column)) ;
2.删除
DROP INDEX [indexName] ON mytable;
3.查看
SHOW INDEX FROM table_name\G;
\G表示纵向显示。
4.使用ALTER命令
有四种方式来添加数据表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。

4.6哪些情况需要创建索引

1.主键自动建立唯一索引;
2.频繁作为查询条件的字段应该创建索引;
3.查询中与其它表关联的字段,外键关系建立索引;
4.单键/组合索引的选择问题, 组合索引性价比更高;
5.查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度;
6.查询中统计或者分组字段。

五、 索引优化分析

5.1使用方法

Explain + SQL语句
包含的信息如下:
MySQL高级(一)mysql架构及索引
看索引是否合适,在不考虑分组和排序情况下,主要看type、key、key_len、rows四项。

5.1.1各字段解释

1.id

id是select查询的***,包含一组数字,表示查询中执行select子句或操作表的顺序。(1)id相同时,从上往下依次查询;(2)id不同,先执行id号大的再执行id小的。
关注点: id号每个号码,表示一趟独立的查询。一个sql 的查询趟数越少越好。

2.select_type

查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询
(1)SIMPLE
简单的 select 查询,查询中不包含子查询或者UNION
(2)PRIMARY
查询中若包含任何复杂的子部分,最外层查询则被标记为Primary
(3)DERIVED 衍生
FROM列表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询, 把结果放在临时表里。
(4)SUBQUERY
SELECT或WHERE列表中包含了子查询
(5)DEPENDENT SUBQUERY
在SELECT或WHERE列表中包含了子查询,子查询基于外层
(6)UNCACHEABLE SUBQUERY
不能缓存的子查询。例如where后跟的条件是变量
(7)UNION
第二个SELECT出现在UNION之后,则被标记为UNION;
若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
(8)UNION RESULT
从UNION表获取结果的SELECT

3.table

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

4.type

type显示的是访问类型,显示查询使用了何种类型。是较为重要的一个指标,结果值从最好到最坏依次是:
system > const > eq_ref > ref > range > index > ALL
(1)system
表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计
(2)const
表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快如将主键置于where列表中,MySQL就能将该查询转换为一个常量
(3)eq_ref
唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
(4) ref
非唯一性索引扫描,返回匹配某个单独值的所有行.本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
(5)range
只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引
一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好
(6)index
出现index是sql使用了索引但是没用通过索引进行过滤,一般是使用了覆盖索引或者是利用索引进行了排序分组
(7)all
Full Table Scan,将遍历全表以找到匹配的行。
备注: 一般来说,得保证查询至少达到range级别,最好能达到ref。

5. possible_key

显示可能应用在这张表中的索引,一个或多个。
查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

6. key

实际使用的索引。如果为NULL,则没有使用索引。
查询中若使用了覆盖索引,则该索引和查询的select字段重叠

什么是覆盖索引

简单说就是,select 到 from 之间查询的列 <=使用的索引列+主键

7. key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。
如何计算
1 、先看索引上字段的类型+长度比如 int=4 ; varchar(20) =20 ; char(20) =20
2 、如果是varchar或者char这种字符串字段,视字符集要乘不同的值,比如utf-8 要乘 3,GBK要乘2,
3 、varchar这种动态字符串要加2个字节
4、 允许为空的字段要加1个字节
== 作用:== key_len字段能够帮你检查是否充分的利用上了索引。

8. ref

显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。

9. rows

rows列显示MySQL认为它执行查询时必须检查的行数。
==原则:==越少越好

10. Extra

包含不适合在其他列中显示但十分重要的额外信息。这里列举三个出现后需要创建索引优化的情况:
(1)Using filesort
说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
MySQL中无法利用索引完成的排序操作称为“文件排序”,此时一定得需要创建索引。查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
(2)Using temporary
使了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
(3)using join buffer
使用了连接缓存:需要创建索引

六、索引查询优化

6.1慢查询日志开启

MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过 long_query_time 值的SQL,就会被记录到慢查询日志中。
我们就必须为我们的function指定一个参数。

show variables like 'log_bin_trust_function_creators';
 
set global log_bin_trust_function_creators=1;

1.开启慢查询日志后,哪些sql会被记录

这个是由参数long_query_time控制,默认情况下long_query_time的值为10秒,
命令:SHOW VARIABLES LIKE 'long_query_time%';
可以使用命令修改,也可以在my.cnf参数里面修改。
假如运行时间正好等于long_query_time的情况,并不会被记录下来。也就是说,
在mysql源码里是判断大于long_query_time,而非大于等于。

2. 设置慢的阙值时间

使用命令 set long_query_time=1
修改为阙值到1秒钟的就是慢sql

3. 查询当前系统中有多少条慢查询记录

show global status like '%Slow_queries%';

6.2 单表使用索引及索引失效

1.全值匹配我最爱

例如:

SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=4 AND emp.name = 'abcd'  

创建索引选:age、deptid、name都选上,比只索引某一个或两个好。
CREATE INDEX idx_age_deptid_name ON emp(age,deptid,NAME)

2.最佳左前缀法则

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

3.索引失效的情况

(1)不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
例如:如果只有name的单值索引,第一句有效,第二句失效!
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.name LIKE ‘abc%’

EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE LEFT(emp.name,3) = ‘abc’

(2)存储引擎不能使用索引中范围条件右边的列
例如:EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 AND emp.deptId>20 AND emp.name = 'abc' ;
如果:create index idx_age_name_deptid on emp(age,deptid,name);
索引只用到了age和deptid,因为deptid是范围条件,所以name的索引失效。
修改为:create index idx_age_name_deptid on emp(age,name,deptid);
注意: 此时age、name、deptid的顺序虽然与SELECT语句不同,但是由于WHERE条件都是AND并列结构,sql优化在不改变结果的情况下,调整顺序为:
SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 AND emp.name = ‘abc’ AND emp.deptId>20 ; 以便能使用建立的索引。
(3)mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描
(4)is not null 也无法使用索引,但是is null是可以使用索引的
(5)like以通配符开头(’%abc…’)mysql索引失效会变成全表扫描的操作
(6)字符串不加单引号索引失效

4.一般性建议

(1)对于单键索引,尽量选择针对当前query过滤性更好的索引。
(2)在选择组合索引的时候:
当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
尽量选择可以能够包含当前query中的where字句中更多字段的索引。(全值匹配我最爱)
如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面。
(3)书写sql语句时,尽量避免造成索引失效的情况。

6.3 关联查询优化

建议:
1、保证被驱动表的join字段已经被索引;
2、left join 时,选择小表作为驱动表,大表作为被驱动表。
3、inner join 时,mysql会自己帮你把小结果集的表选为驱动表。
4、子查询尽量不要放在被驱动表,有可能使用不到索引。
5、能够直接多表关联的尽量直接关联,不用子查询。

6.4 子查询优化

尽量不要使用not in 或者 not exists,用left outer join on xxx is null 替代。

6.5 排序分组优化

1.对于ORDER BY子句

尽量使用Index方式排序,避免使用FileSort方式排序!
==说明:==如果不在索引列上,filesort有两种算法:mysql就要启动双路排序和单路排序。由于单路是后出的,总体而言好过双路。
==排序三大原则: ==
(1)无过滤 不索引

create index idx_age_deptid_name on emp (age,deptid,name)
1explain  select SQL_NO_CACHE * from emp order by age,deptid; 
2explain  select SQL_NO_CACHE * from emp order by age,deptid limit 10; 

第一句查询不能去掉using filesort,第二局用到了索引,因为有LIMIT过滤。
(2)顺序错,必排序

3explain  select * from emp where age=45 order by deptid;

4explain  select * from emp where age=45 order by   deptid,name; 

5explain  select * from emp where age=45 order by  deptid,empno;

6explain  select * from emp where age=45 order by  name,deptid;
 
7explain select * from emp where deptid=45 order by age;

第3、4句sql语句用到了索引并且不会出现using filesort;但是5、6、7句sql语句都有using filesort。每一句的key_len=5。
(3)方向反 必排序

8explain select * from emp where age=45 order by  deptid desc, name desc ;
 
9explain select * from emp where age=45 order by  deptid asc, name desc ;

第8句不会出现using filesort,第九句sql有using filesort。

2.GROUP BY关键字优化

group by 使用索引的原则几乎跟order by一致 ,唯一区别是groupby 即使没有过滤条件用到索引,也可以直接使用索引。