1. MySQL的架构介绍
1.1 MySQL简介:
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司。
MySQL是一种关联数据库管理系统,将数据保存在不一样的表中,而不是将全部数据放在一个大仓库内,这样就增长了速度并提升了灵活性。
Mysql是开源的,因此你不须要支付额外的费用。
Mysql支持大型的数据库。能够处理拥有上千万条记录的大型数据库。
MySQL使用标准的SQL数据语言形式。
Mysql能够容许于多个系统上,而且支持多种语言。这些编程语言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。
Mysql对PHP有很好的支持,PHP是目前最流行的Web开发语言。
MySQL支持大型数据库,支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64位系统支持最大的表文件为8TB。
Mysql是能够定制的,采用了GPL协议,你能够修改源码来开发本身的Mysql系统(其实阿里巴巴的“去IOE”中,使用的数据库就是本身开发的阿里版的MySQL)。
1.2 一个高级的J2E工程师须要面对MySQL要有那些基本功夫呢?
首先,你要逐步理解mysql内核;其次,sql优化、mysql服务器的优化和各类参数常量设定与各类参数常量设定。而后,要会主从复制、容灾备份、sql编程和相应的软硬件升级。固然,这只是一个高级的J2E工程师面对MySQL要有的基本功夫。由于完整的mysql优化须要很深的功底,大公司甚至有专门的DBA工程师进行负责。html
2. 工欲善其事必先利其器(Mysql Linux版的安装)mysql
我我的是比较推荐去官网下载软件和对应的文档的。官网下载地址:http://dev.mysql.com/downloads/mysql/linux
MySQL Serverios
MySQL Client算法
下载完成之后,准备安装之前首先,检查当前系统是否安装过mysql:sql
查询命令:rpm -qa|grep -i mysql 删除命令:rpm -e RPM软件包名(该名字是上一个命令查出来的名字)数据库
在安装过程当中,安装mysql服务端(注意提示),而后再安装mysql客户端。安装完成之后,能够查看MySQL安装时建立的mysql用户和mysql组:编程
安装完毕之后,咱们能够根据本身的须要来进行mysql服务的启和停:windows
1 # ps -ef|grep mysql 缓存
2 # service mysql start
3 # service mysql stop
mysql服务启动后,开始链接(注意这里,由于MySQL默认没有密码,因此这里咱们没有输入密码就直接连上了):
因此咱们在按照安装Server时,根据提示修改登陆密码便可。
若是咱们常常要使用MySQL来进行开发,就须要将其设置为自启动mysql服务:
只要是搞开发过来的,确定遇到过乱码之类的问题。因此怎么解决呢?
1 查看字符集
1 show variables like 'character%';
2 show variables like '%char%';
看看出现的结果:
默认的是客户端和服务器都用了latin1,因此会乱码。
2 修改
1 [client] 2 #password = your_password 3 port = 3306
4 socket = /var/lib/mysql/mysql.sock 5 default-character-set=utf8 6
7 # The MySQL server 8 [mysqld] 9 port = 3306
10 character_set_server=utf8 11 character_set_client=utf8 12 collation-server=utf8_general_ci 13 socket = /var/lib/mysql/mysql.sock 14 skip-external-locking 15 key_buffer_size = 384M 16 max_allowed_packet = 1M 17 table_open_cache = 512
18 sort_buffer_size = 2M 19 read_buffer_size = 2M 20 read_rnd_buffer_size = 8M 21 myisam_sort_buffer_size = 64M 22 thread_cache_size = 8
23 query_cache_size = 32M 24 # Try number of CPU's*2 for thread_concurrency
25 thread_concurrency = 8
26
27 [mysql] 28 no-auto-rehash 29 default-character-set=utf8
3 重启mysql
1 service mysql stop;
2 service mysql start;
4 从新链接后从新create databse并使用新建库,而后再从新建表试试
5 仍是乱码的话就设值init_connect='SET NAMES utf8' ##设定链接mysql是UTF8编码
在windows系统下咱们能够很快找到MySQL的安装路径,那在linux下呢?
这其实也很简单,在linux下查看安装目录的命令以下:
1 ps -ef|grep mysql
3. Mysql主要配置文件含义及如何配置
3.1 二进制日志log-bin (主从复制)
3.2 错误日志log-error
默认是关闭的,记录严重的警告和错误信息,每次启动和关闭的详细信息等。
3.3 查询日志log
默认关闭,记录查询的sql语句,若是开启会减低mysql的总体性能,由于记录日志也是须要消耗系统资源的
3.4 数据文件
frm文件:存放表结构
myd文件:存放表数据
myi文件:存放表索引
4. Mysql逻辑架构简单聊聊
4.1 整体概览
和其它数据库相比,MySQL有点不同凡响,它的架构能够在多种不一样场景中应用并发挥良好做用。主要体如今存储引擎的架构上,
插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构能够根据业务的需求和实际须要选择合适的存储引擎。
4.1.1.链接层
最上层是一些客户端和链接服务,包含本地sock通讯和大多数基于客户端/服务端工具实现的相似于tcp/ip的通讯。主要完成一些相似于链接处理、受权认证、及相关的安全方案。在该层上引入了线程池的概念,为经过认证安全接入的客户端提供线程。一样在该层上能够实现基于SSL的安全连接。服务器也会为安全接入的每一个客户端验证它所具备的操做权限。
4.1.2.服务层
第二层架构主要完成核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部份内置函数的执行。全部跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并建立相应的内部解析树,并对其完成相应的优化如肯定查询表的顺序,是否利用索引等,最后生成相应的执行操做。若是是select语句,服务器还会查询内部的缓存。若是缓存空间足够大,这样在解决大量读操做的环境中可以很好的提高系统的性能。
4.1.3.引擎层
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器经过API与存储引擎进行通讯。不一样的存储引擎具备的功能不一样,这样咱们能够根据本身的实际须要进行选取(MyISAM和InnoDB)。
4.1.4.存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
4.2 查询说明
首先,mysql的查询流程大体是:
mysql客户端经过协议与mysql服务器建链接,发送查询语句,先检查查询缓存,若是命中,直接返回结果,不然进行语句解析有一系列预处理,好比检查语句是否写正确了,而后是查询优化(好比是否使用索引扫描,若是是一个不可能的条件,则提早终止),生成查询计划,而后查询引擎启动,开始执行查询,从底层存储引擎调用API获取数据,最后返回给客户端。怎么存数据、怎么取数据,都与存储引擎有关。而后,mysql默认使用的BTREE索引,而且一个大方向是,不管怎么折腾sql,至少在目前来讲,mysql最多只用到表中的一个索引。
5. Mysql存储引擎
1 #看你的mysql如今已提供什么存储引擎: mysql> show engines;
2 #看你的mysql当前默认的存储引擎: 5 mysql> show variables like '%storage_engine%';
6. 阿里巴巴、淘宝用哪一个???
Percona 为 MySQL 数据库服务器进行了改进,在功能和性能上较 MySQL 有着很显著的提高。该版本提高了在高负载状况下的 InnoDB 的性能、为 DBA 提供一些很是有用的性能诊断工具;另外有更多的参数和命令来控制服务器行为。
该公司新建了一款存储引擎叫xtradb彻底能够替代innodb,而且在性能和并发上作得更好,阿里巴巴大部分mysql数据库其实使用的percona的原型加以修改。
7. 影响mysql的性能因素
7.1 业务需求对mysql的影响(合适合度)
举一个例子吧:
【要求】
统计对该产品的评价帖子,要实时的!
【问题】
假如这个数据量很小OK,能够实时查询,假如愈来愈多(例如淘宝店),积累了好多万客户评论帖子
1 select count(*)对于刚开始数量很小时能够,若是数据增大已经有千万级的,一个查询就哭吧,成为系统性能瓶颈。
2 你正在统计时候也有买家卖家修改增删评论,你不能要求用户说我统计的时候大家不准动。
【解决】
单独一个表,单独一个字段来保存这个帖子数目。 每个买家新增一条评论就是须要更新一个这个数字,各买家是多个(高并发)不定时的在发布帖子,实时修改更新后再统计select count(*)。 就算咱们使用的是Innodb存储引擎,一个update数字他就是行锁,高并发的瓶颈出现了
【结论】
不少的统计信息都是准实时的而不是实时统计,网站的一些数量信息、分页信息、排序信息、点击率信息等等通常都不是实时的而是准实时的。
7.2 存储定位对mysql的影响
7.3 Schema设计对系统的性能影响
两个尽可能原则:尽可能减小对数据库访问的请求;尽可能减小无用数据的查询请求
7.4 硬件环境对系统性能的影响
7.4.1 典型OLTP应用系统
什么是OLTP:OLTP即联机事务处理,就是咱们常常说的关系数据库,意即记录即时的增、删、改、查,就是咱们常常应用的东西,这是数据库的基础
对于各类数据库系统环境中你们最多见的OLTP系统,其特色是并发量大,总体数据量比较多,但每次访问的数据比较少,且访问的数据比较离散,活跃数据占整体数据的比例不是太大。对于这类系统的数据库其实是最难维护,最难以优化的,对主机总体性能要求也是最高的。由于不只访问量很高,数据量也不小。
针对上面的这些特色和分析,咱们能够对OLTP的得出一个大体的方向。
虽然系统整体数据量较大,可是系统活跃数据在数据总量中所占的比例不大,那么咱们能够经过扩大内存容量来尽量多的将活跃数据cache到内存中;虽然IO访问很是频繁,可是每次访问的数据量较少且很离散,那么咱们对磁盘存储的要求是IOPS 【 (Input/Output Operations Per Second),即每秒进行读写(I/O)操做的次数】表现要很好,吞吐量是次要因素;并发量很高,CPU每秒所要处理的请求天然也就不少,因此CPU处理能力须要比较强劲;虽然与客户端的每次交互的数据量并非特别大,可是网络交互很是频繁,因此主机与客户端交互的网络设备对流量能力也要求不能太弱。
7.4.2 典型OLAP应用系统
用于数据分析的OLAP系统的主要特色就是数据量很是大,并发访问很少,但每次访问所须要检索的数据量都比较多,并且数据访问相对较为集中,没有太明显的活跃数据概念。
什么是OLAP:OLAP即联机分析处理,是数据仓库的核心部心,所谓数据仓库是对于大量已经由OLTP造成的数据的一种分析型的数据库,用于处理商业智能、决策支持等重要的决策信息;数据仓库是在数据库应用到必定程序以后而对历史数据的加工与分析
基于OLAP系统的各类特色和相应的分析,针对OLAP系统硬件优化的大体策略以下:
数据量很是大,因此磁盘存储系统的单位容量须要尽可能大一些;
单次访问数据量较大,并且访问数据比较集中,那么对IO系统的性能要求是须要有尽量大的每秒IO吞吐量,因此应该选用每秒吞吐量尽量大的磁盘;
虽然IO性能要求也比较高,可是并发请求较少,因此CPU处理能力较难成为性能瓶颈,因此CPU处理能力没有太苛刻的要求;
虽然每次请求的访问量很大,可是执行过程当中的数据大都不会返回给客户端,最终返回给客户端的数据量都较小,因此和客户端交互的网络设备要求并非过高;
此外,因为OLAP系统因为其每次运算过程较长,能够很好的并行化,因此通常的OLAP系统都是由多台主机构成的一个集群,而集群中主机与主机之间的数据交互量通常来讲都是很是大的,因此在集群中主机之间的网络设备要求很高。
8. 查询与索引优化分析
8.1 为何要优化?
现象:性能降低SQL慢、执行时间长、等待时间长。
缘由:
查询语句写的烂
索引失效(单值、复合)
关联查询太多join(设计缺陷或不得已的需求)
关联查询太多join(设计缺陷或不得已的需求)
9. 常见通用的Join查询
9.1 SQL执行顺序
手写:
机读:
结论:
9.2 Join图
1 A、B两表共有 select * from tbl_emp a inner join tbl_dept b on a.deptId = b.id; 2 A、B两表共有+A的独有 select * from tbl_emp a left join tbl_dept b on a.deptId = b.id; 3 A、B两表共有+B的独有 select * from tbl_emp a right join tbl_dept b on a.deptId = b.id; 4 A的独有 select * from tbl_emp a left join tbl_dept b on a.deptId = b.id where b.id is null; 5 B的独有 select * from tbl_emp a right join tbl_dept b on a.deptId = b.id where a.deptId is null; #B的独有 6 AB全有 #MySQL Full Join的实现 由于MySQL不支持FULL JOIN,下面是替代方法 #left join + union(可去除重复数据)+ right join
SELECT * FROM tbl_emp A LEFT JOIN tbl_dept B ON A.deptId = B.id UNION
SELECT * FROM tbl_emp A RIGHT JOIN tbl_dept B ON A.deptId = B.id 7 A的独有+B的独有 SELECT * FROM tbl_emp A LEFT JOIN tbl_dept B ON A.deptId = B.id WHERE B.`id` IS NULL
UNION
SELECT * FROM tbl_emp A RIGHT JOIN tbl_dept B ON A.deptId = B.id WHERE A.`deptId` IS NULL;
9.4 建表SQL
1 CREATE TABLE `tbl_dept` ( 2 `id` INT(11) NOT NULL AUTO_INCREMENT, 3 `deptName` VARCHAR(30) DEFAULT NULL, 4 `locAdd` VARCHAR(40) DEFAULT NULL, 5 PRIMARY KEY (`id`) 6 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 7
8 CREATE TABLE `tbl_emp` ( 9 `id` INT(11) NOT NULL AUTO_INCREMENT, 10 `name` VARCHAR(20) DEFAULT NULL, 11 `deptId` INT(11) DEFAULT NULL, 12 PRIMARY KEY (`id`), 13 KEY `fk_dept_id` (`deptId`) 14 #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `tbl_dept` (`id`) 15 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 16
17
18
19 INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11); 20 INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12); 21 INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13); 22 INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14); 23 INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15); 24
25
26 INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1); 27 INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1); 28 INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1); 29
30 INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2); 31 INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2); 32
33 INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3); 34
35 INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4); 36
37 INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51); 38
10. 什么是索引
10.1 是什么
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。能够获得索引的本质:索引是数据结构。
索引的目的在于提升查询效率,能够类比字典,若是要查“mysql”这个单词,咱们确定须要定位到m字母,而后从下往下找到y字母,再找到剩下的sql。若是没有索引,那么你可能须要a----z,若是我想找到Java开头的单词呢?或者Oracle开头的单词呢?是否是以为若是没有索引,这个事情根本没法完成?因此你能够简单理解为“排好序的快速查找结构”。
在数据以外,数据库系统还维护着知足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,
这样就能够在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址
为了加快Col2的查找,能够维护一个右边所示的二叉查找树,每一个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就能够运用二叉查找在必定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。
通常来讲索引自己也很大,不可能所有存储在内存中,所以索引每每以索引文件的形式存储的磁盘上。咱们日常所说的索引,若是没有特别指明,都是指B+树结构组织的索引。其中汇集索引,次要索引,覆盖索引,复合索引,前缀索引,惟一索引默认都是使用B+树索引,统称索引。固然,除了B+树这种类型的索引以外,还有哈稀索引(hash index)等。
10.2 优缺点
优势:经过索引列对数据进行排序,下降数据排序的成本,下降了CPU的消耗;提升数据检索的效率,下降数据库的IO成本
缺点:实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,因此索引列也是要占用空间的;虽然索引大大提升了查询速度,同时却会下降更新表的速度,如对表进行INSERT、UPDATE和DELETE。由于更新表时,MySQL不只要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整由于更新所带来的键值变化后的索引信息;索引只是提升效率的一个因素,若是你的MySQL有大数据量的表,就须要花时间研究创建最优秀的索引,或优化查询语句
10.3 mysql索引分类
单值索引:即一个索引只包含单个列,一个表能够有多个单列索引。
惟一索引:索引列的值必须惟一,但容许有空值。
复合索引:即一个索包含多个列。
基本语法:
建立:
1. CREATE [UNIQUE ] INDEX indexName ON mytable(columnname(length)); -->若是是CHAR,VARCHAR类型,length能够小于字段实际长
度;若是是BLOB和TEXT类型,必须指定length。
2. ALTER mytable ADD [UNIQUE ] INDEX [indexName] ON (columnname(length))
删除:
DROP INDEX [indexName] ON mytable;
查看:
SHOW INDEX FROM table_name
使用ALTER命令(有四种方式来添加数据表的索引):
1. ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是惟一的,且不能为NULL。
2. ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 这条语句建立索引的值必须是惟一的(除了NULL外,NULL可能会出现屡次)。
3. ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现屡次。
4. ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。
10.4 mysql索引结构(BTree索引、Hash索引、full-text全文索引、R-Tree索引)
BTree索引检索原理:
【初始化介绍】
一颗b+树,浅蓝色的块咱们称之为一个磁盘块,能够看到每一个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),
如磁盘块1包含数据项17和35,包含指针P一、P二、P3,
P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。
非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如1七、35并不真实存在于数据表中。
【查找过程】
若是要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找肯定29在17和35之间,锁定磁盘块1的P2指针,内存时间由于很是短(相比磁盘的IO)能够忽略不计,经过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,经过指针加载磁盘块8到内存,发生第三次IO,同时内存中作二分查找找到29,结束查询,总计三次IO。
真实的状况是,3层的b+树能够表示上百万的数据,若是上百万的数据查找只须要三次IO,性能提升将是巨大的,若是没有索引,每一个数据项都要发生一次IO,那么总共须要百万次的IO,显然成本很是很是高。
10.5 哪些状况须要建立索引
1. 主键自动创建惟一索引
2. 频繁做为查询条件的字段应该建立索引
3. 查询中与其它表关联的字段,外键关系创建索引
4. 频繁更新的字段不适合建立索引(由于每次更新不仅仅是更新了记录还会更新索引,加剧了IO负担)
5. Where条件里用不到的字段不建立索引
6. 单键/组合索引的选择问题,who?(在高并发下倾向建立组合索引)
7. 查询中排序的字段,排序字段若经过索引去访问将大大提升排序速度
8. 查询中统计或者分组字段
10.6 哪些状况不要建立索引
1. 表记录太少
2. 常常增删改的表(由于提升了查询速度,同时却会下降更新表的速度,如对表进行INSERT、UPDATE和DELETE。由于更新表时,MySQL不只要保存数据,还要保存一下索引文件)
3. 数据重复且分布平均的表字段,所以应该只为最常常查询和最常常排序的数据列创建索引。注意,若是某个数据列包含许多重复的内容,为它创建索引就没有太大的实际效果。
11. MySql Query Optimizer
11.1 Mysql中有专门负责优化SELECT语句的优化器模块,主要功能:经过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划(他认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间)
11.2 当客户端向MySQL 请求一条Query,命令解析器模块完成请求分类,区别出是 SELECT 并转发给MySQL Query Optimizer时,MySQL Query Optimizer 首先会对整条Query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对 Query 中
的查询条件进行简化和转换,如去掉一些无用或显而易见的条件、结构调整等。而后分析 Query 中的 Hint 信息(若是有),看显示Hint信息是否能够彻底肯定该Query 的执行计划。若是没有 Hint 或Hint 信息还不足以彻底肯定执行计划,则会读取所涉及对象的统计信
息,根据 Query 进行写相应的计算分析,而后再得出最后的执行计划。
11.3 MySQL常见瓶颈
CPU:CPU在饱和的时候通常发生在数据装入内存或从磁盘上读取数据时候;
IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候;
服务器硬件的性能瓶颈:能够经过(Linux命令)top,free, iostat和vmstat命令来查看系统的性能状态。
11.4 EXPLAIN关键字
使用EXPLAIN关键字能够模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。经过它能够获得表的读取顺序、数据读取操做的操做类型、哪些索引可使用、哪些索引被实际使用、表之间的引用、每张表有多少行被优化器查询。
那怎么用呢?
其实,Explain + SQL语句便可。
上图各字段解释(执行计划包含的信息)以下:
1. id
select查询的序列号,包含一组数字,表示查询中执行select子句或操做表的顺序。
三种状况:
1.1. id相同,执行顺序由上至下
1.2. id不一样,若是是子查询,id的序号会递增,id值越大优先级越高,越先被执行
若是是子查询,id的序号会递增,id值越大优先级越高,越先被执行。
1.3. id相同不一样,同时存在
id若是相同,能够认为是一组,从上往下顺序执行;在全部组中,id值越大,优先级越高,越先执行。(衍生 = DERIVED)
2. 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。
3. table
显示这一行的数据是关于哪张表的
4. type
访问类型排列:
type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,
通常来讲,得保证查询至少达到range级别,最好能达到ref。
显示查询使用了何种类型,从最好到最差依次是:
system>const>eq_ref>ref>range>index>ALL
system:表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也能够忽略不计;
const:表示经过索引一次就找到了,const用于比较primary key或者unique索引。由于只匹配一行数据,因此很快如将主键置于where列表中,MySQL就能将该查询转换为一个常量;
eq_ref:惟一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配。常见于主键或惟一索引扫描;
ref:非惟一性索引扫描,返回匹配某个单独值的全部行.本质上也是一种索引访问,它返回全部匹配某个单独值的行,然而,它可能会找到多个符合条件的行,因此他应该属于查找和扫描的混合体;
range:只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪一个索引通常就是在你的where语句中出现了between、<、>、in等的查询这种范围扫描索引扫描比全表扫描要好,由于它只须要开始于索引的某一点,而结束语另外一点,不用扫描所有索引;
index:Full Index Scan,index与ALL区别为index类型只遍历索引树。这一般比ALL快,由于索引文件一般比数据文件小。(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读的);
all:Full Table Scan,将遍历全表以找到匹配的行。
备注:通常来讲,得保证查询至少达到range级别,最好能达到ref。
5. possible_keys
显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不必定被查询实际使用
6. key
实际使用的索引。若是为NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出如今key列表中(以下图所示:)
7. key_len
表示索引中使用的字节数,可经过该列计算查询中使用的索引的长度。在不损失精确性的状况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并不是实际使用长度,即key_len是根据表定义计算而得,不是经过表内检索出的
8. ref
显示索引的哪一列被使用了,若是可能的话,是一个常数。哪些列或常量被用于查找索引列上的值(示例以下:)
由key_len可知t1表的idx_col1_col2被充分使用,col1匹配t2表的col1,col2匹配了一个常量,即 'ac'
9. rows
根据表统计信息及索引选用状况,大体估算出找到所需的记录所须要读取的行数
10. Extra
包含不适合在其余列中显示但十分重要的额外信息
Using filesort :
说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中没法利用索引完成的排序操做称为“文件排序”。
Using temporary:
使了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
USING index:
表示相应的select操做中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!若是同时出现using where,代表索引被用来执行索引键值的查找;若是没有同时出现using where,代表索引用来读取数据而非执行查找动做。
覆盖索引(Covering Index):
覆盖索引(Covering Index),一说为索引覆盖。
理解方式一:就是select的数据列只用从索引中就可以取得,没必要读取数据行,MySQL能够利用索引返回select列表中的字段,而没必要根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。
理解方式二:索引是高效找到行的一个方法,可是通常数据库也能使用索引找到一个列的数据,所以它没必要读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能经过读取索引就能够获得想要的数据,那就不须要读取行了。一个索引包含了(或覆盖了)知足查询结果的数据就叫作覆盖索引。
注意:
若是要使用覆盖索引,必定要注意select列表中只取出须要的列,不可select *,由于若是将全部字段一块儿作索引会致使索引文件过大,查询性能降低。
Using where:
代表使用了where过滤。
using join buffer:
使用了链接缓存;
impossible where:
where子句的值老是false,不能用来获取任何元组;
select tables optimized away:
在没有GROUPBY子句的状况下,基于索引优化MIN/MAX操做或者对于MyISAM存储引擎优化COUNT(*)操做,没必要等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
distinct:
优化distinct操做,在找到第一匹配的元组后即中止找一样值的动做。
举个例子,热热身吧!讲了那么多,是否是有些晕了。不要紧,让咱们经过一个小例子看看。
第一行(执行顺序4):id列为1,表示是union里的第一个select,select_type列的primary表 示该查询为外层查询,table列被标记为<derived3>,表示查询结果来自一个衍生表,其中derived3中3表明该查询衍生自第三个select查询,即id为3的select。【select d1.name......】
第二行(执行顺序2):id为3,是整个查询中第三个select的一部分。因查询包含在from中,因此为derived。【select id,name from t1 where other_column=''】
第三行(执行顺序3):select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】
第四行(执行顺序1):select_type为union,说明第四个select是union里的第二个select,最早执行【select name,id from t2】
第五行(执行顺序5):表明从union的临时表中读取行的阶段,table列的<union1,4>表示用第一个和第四个select的结果进行union操做。【两个结果union操做】
12. 索引优化
12.1 索引分析
1. 单表
建表SQL:
1 CREATE TABLE IF NOT EXISTS `article` ( 2 `id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 3 `author_id` INT(10) UNSIGNED NOT NULL, 4 `category_id` INT(10) UNSIGNED NOT NULL, 5 `views` INT(10) UNSIGNED NOT NULL, 6 `comments` INT(10) UNSIGNED NOT NULL, 7 `title` VARBINARY(255) NOT NULL, 8 `content` TEXT NOT NULL
9 ); 10
11 INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES
12 (1, 1, 1, 1, '1', '1'), 13 (2, 2, 2, 2, '2', '2'), 14 (1, 1, 3, 3, '3', '3'); 15
16 SELECT * FROM article;
案例分析:
1 #查询 category_id 为 1 且 comments 大于 1 的状况下,views 最多的 article_id。 2 EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; 3
4 #结论:很显然,type 是 ALL,即最坏的状况。Extra 里还出现了 Using filesort,也是最坏的状况。优化是必须的。 5
6
7 #开始优化: 8 # 1.1 新建索引+删除索引 9 #ALTER TABLE `article` ADD INDEX idx_article_ccv ( `category_id` , `comments`, `views` ); 10 create index idx_article_ccv on article(category_id,comments,views); 11 DROP INDEX idx_article_ccv ON article 12
13
14 # 1.2 第2次EXPLAIN 15 EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments >1 ORDER BY views DESC LIMIT 1; 16 EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments =3 ORDER BY views DESC LIMIT 1
17 #结论: 18 #type 变成了 range,这是能够忍受的。可是 extra 里使用 Using filesort 还是没法接受的。 19 #可是咱们已经创建了索引,为啥没用呢? 20 #这是由于按照 BTree 索引的工做原理, 21 # 先排序 category_id, 22 # 若是遇到相同的 category_id 则再排序 comments,若是遇到相同的 comments 则再排序 views。 23 #当 comments 字段在联合索引里处于中间位置时, 24 #因comments > 1 条件是一个范围值(所谓 range), 25 #MySQL 没法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。 26
27
28 # 1.3 删除第一次创建的索引 29 DROP INDEX idx_article_ccv ON article; 30
31 # 1.4 第2次新建索引 32 #ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` , `views` ) ; 33 create index idx_article_cv on article(category_id,views); 34
35 # 1.5 第3次EXPLAIN 36 EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; 37 #结论:能够看到,type 变为了 ref,Extra 中的 Using filesort 也消失了,结果很是理想。 38 DROP INDEX idx_article_cv ON article;
2. 两表
建表SQL:
1 CREATE TABLE IF NOT EXISTS `class` ( 2 `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 3 `card` INT(10) UNSIGNED NOT NULL, 4 PRIMARY KEY (`id`) 5 ); 6 CREATE TABLE IF NOT EXISTS `book` ( 7 `bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 8 `card` INT(10) UNSIGNED NOT NULL, 9 PRIMARY KEY (`bookid`) 10 ); 11
12 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 13 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 14 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 15 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 16 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 17 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 18 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 19 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 20 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 21 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 22 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 23 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 24 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 25 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 26 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 27 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 28 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 29 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 30 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 31 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 32
33 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 34 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 35 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 36 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 37 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 38 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 39 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 40 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 41 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 42 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 43 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 44 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 45 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 46 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 47 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 48 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 49 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 50 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 51 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 52 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 53
54
55
案例分析:
1 # 下面开始explain分析 2 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; 3 #结论:type 有All 4
5 # 添加索引优化 6 ALTER TABLE `book` ADD INDEX Y ( `card`); 7
8 # 第2次explain 9 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; 10 #能够看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。 11 #这是由左链接特性决定的。LEFT JOIN 条件用于肯定如何从右表搜索行,左边必定都有, 12 #因此右边是咱们的关键点,必定须要创建索引。 13
14 # 删除旧索引 + 新建 + 第3次explain 15 DROP INDEX Y ON book; 16 ALTER TABLE class ADD INDEX X (card); 17 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; 18
19
20 # 而后来看一个右链接查询: 21 #优化较明显。这是由于 RIGHT JOIN 条件用于肯定如何从左表搜索行,右边必定都有,因此左边是咱们的关键点,必定须要创建索引。 22 EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card; 23 DROP INDEX X ON class; 24 ALTER TABLE book ADD INDEX Y (card); 25 # 右链接,基本无变化 26 EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card; 27
3. 三表
建表SQL:
1 CREATE TABLE IF NOT EXISTS `phone` ( 2 `phoneid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 3 `card` INT(10) UNSIGNED NOT NULL, 4 PRIMARY KEY (`phoneid`) 5 ) ENGINE = INNODB; 6
7 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 8 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 9 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 10 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 11 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 12 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 13 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 14 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 15 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 16 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 17 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 18 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 19 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 20 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 21 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 22 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 23 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 24 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 25 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 26 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
案例:
1 ALTER TABLE `phone` ADD INDEX z ( `card`); 2
3 ALTER TABLE `book` ADD INDEX Y ( `card`);#上一个case建过一个一样的 4
5
6 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card = phone.card; 7
8 # 后 2 行的 type 都是 ref 且总 rows 优化很好,效果不错。所以索引最好设置在须要常常查询的字段中。 9 ==================================================================================
10 【结论】 11 Join语句的优化 12
13 尽量减小Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。 14 优先优化NestedLoop的内层循环; 15 保证Join语句中被驱动表上Join条件字段已经被索引; 16 当没法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;
12.2 索引失效(应该避免)
建表SQL:
1 CREATE TABLE staffs ( 2 id INT PRIMARY KEY AUTO_INCREMENT, 3 NAME VARCHAR (24) NOT NULL DEFAULT '' COMMENT '姓名', 4 age INT NOT NULL DEFAULT 0 COMMENT '年龄', 5 pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '职位', 6 add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间'
7 ) CHARSET utf8 COMMENT '员工记录表' ; 8
9
10 INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW()); 11 INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW()); 12
13 SELECT * FROM staffs; 14
15 ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);
案例(索引失效):
1. 全值匹配我最爱
1 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July'; 2 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25; 3 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev';
2. 最佳左前缀法则
若是索引了多列,要遵照最左前缀法则。指的是查询从索引的最左前列开始而且不跳过索引中的列。
1 EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = 'dev'; 2
3 EXPLAIN SELECT * FROM staffs WHERE pos = 'dev';
3. 不在索引列上作任何操做(计算、函数、(自动or手动)类型转换),会致使索引失效而转向全表扫描
1 EXPLAIN SELECT * FROM staffs WHERE left(NAME,4) = 'July'; 2
3 索引列上使用了表达式,如where substr(a, 1, 3) = 'hhh',where a = a + 1,表达式是一大忌讳,再简单mysql也不认。 4 有时数据量不是大到严重影响速度时,通常能够先查出来,好比先查全部有订单记录的数据,再在程序中去筛选
4. 存储引擎不能使用索引中范围条件右边的列
5. 尽可能使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减小select *
6. mysql 在使用不等于(!= 或者<>)的时候没法使用索引会致使全表扫描
7. is null ,is not null 也没法使用索引
8. like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操做
9. 字符串不加单引号索引失效
10. 少用or,用它来链接时会索引失效
因此咱们来一个小小的总结:
接着咱们以一个例子来深入体会体会:
SQL以下:
【建表语句】 create table test03( id int primary key not null auto_increment, c1 char(10), c2 char(10), c3 char(10), c4 char(10), c5 char(10) ); insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5'); insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5'); insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5'); insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5'); insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5'); select * from test03; 【建索引】 create index idx_test03_c1234 on test03(c1,c2,c3,c4); show index from test03; 问题:咱们建立了复合索引idx_test03_c1234 ,根据如下SQL分析下索引使用状况? explain select * from test03 where c1='a1'; explain select * from test03 where c1='a1' and c2='a2'; explain select * from test03 where c1='a1' and c2='a2' and c3='a3'; explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 1) explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 2) explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3'; 3) explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4'; 4) explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3'; 5) explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3; c3做用在排序而不是查找 6) explain select * from test03 where c1='a1' and c2='a2' order by c3; 7) explain select * from test03 where c1='a1' and c2='a2' order by c4; 出现了filesort 8) 8.1 explain select * from test03 where c1='a1' and c5='a5' order by c2,c3; 只用c1一个字段索引,可是c二、c3用于排序,无filesort 8.2 explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; 出现了filesort,咱们建的索引是1234,它没有按照顺序来,3 2 颠倒了 9) explain select * from test03 where c1='a1' and c2='a2' order by c2,c3; 10) explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c2,c3; 用c一、c2两个字段索引,可是c二、c3用于排序,无filesort explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c3,c2; 本例有常量c2的状况,和8.2对比 explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; filesort 11) explain select * from test03 where c1='a1' and c4='a4' group by c2,c3; 12) explain select * from test03 where c1='a1' and c4='a4' group by c3,c2; Using where; Using temporary; Using filesort
咱们不可贵出:
定值、范围仍是排序,通常order by是给个范围,group by 基本上都须要进行排序,会有临时表产生。
12.3 Show Profile
是什么:是mysql提供能够用来分析当前会话中语句执行的资源消耗状况。能够用于SQL的调优的测量。默认状况下,参数处于关闭状态,并保存最近15次的运行结果。
分析步骤;
1. 是否支持,看看当前的mysql版本是否支持
Show variables like 'profiling'; //默认是关闭,使用前须要开启
2. 开启功能,默认是关闭,使用前须要开启
3. 运行SQL
4. 查看结果,show profiles;
5. 诊断SQL,show profile cpu,block io for query 上一步前面的问题SQL数字号码;
6. 平常开发须要注意的地方总结(若有遗漏,还望同行不吝赐教)
converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。
create tmp table 建立临时表,这个要注意。
Copying to tmp table on disk 把内存临时表复制到磁盘。
locked 是否上锁,这个也要注意。
12.4 索引优化的须要注意的一些
对于单键索引,尽可能选择针对当前query过滤性更好的索引;
在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
在选择组合索引的时候,尽可能选择能够可以包含当前query中的where字句中更多字段的索引。
尽量经过分析统计信息和调整query的写法来达到选择合适索引的目的。
少用Hint强制索引。
13. 查询优化
13.1 永远小表驱动大表,相似嵌套循环Nested Loop
13.2 order by关键字优化
1. ORDER BY子句,尽可能使用Index方式排序,避免使用FileSort方式排序
建表SQL:
1 CREATE TABLE tblA( 2 #id int primary key not null auto_increment, 3 age INT, 4 birth TIMESTAMP NOT NULL
5 ); 6
7 INSERT INTO tblA(age,birth) VALUES(22,NOW()); 8 INSERT INTO tblA(age,birth) VALUES(23,NOW()); 9 INSERT INTO tblA(age,birth) VALUES(24,NOW()); 10
11 CREATE INDEX idx_A_ageBirth ON tblA(age,birth); 12
13 SELECT * FROM tblA;
案例:
MySQL支持二种方式的排序,FileSort和Index,Index效率高。它指MySQL扫描索引自己完成排序。FileSort方式效率较低。
ORDER BY知足两状况,会使用Index方式排序:
ORDER BY 语句使用索引最左前列;使用Where子句与Order BY子句条件列组合知足索引最左前列
2. 尽量在索引列上完成排序操做,遵守索引建的最佳左前缀
3. 若是不在索引列上,filesort有两种算法:mysql就要启动双路排序和单路排序
双路排序:MySQL 4.1以前是使用双路排序,字面意思就是两次扫描磁盘,最终获得数据,读取行指针和orderby列,对他们进行排序,而后扫描已经排序好的列表,按照列表中的值从新从列表中读取对应的数据输出。从磁盘取排序字段,在buffer进行排序,再从磁盘取其余字段。
取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,因此在mysql4.1以后,出现了第二种改进的算法,就是单路排序。
单路排序:从磁盘读取查询须要的全部列,按照order by列在buffer对它们进行排序,而后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。而且把随机IO变成了顺序IO,可是它会使用更多的空间,由于它把每一行都保存在内存中了。
4. 结论及引伸出的问题
因为单路是后出的,整体而言好过双路;可是用单路有问题(在sort_buffer中,方法B比方法A要多占用不少空间,由于方法B是把全部字段都取出, 因此有可能取出的数据的总大小超出了sort_buffer的容量,致使每次只能取sort_buffer容量大小的数据,进行排
序(建立tmp文件,多路合并),排完再取取sort_buffer容量大小,再排……从而屡次I/O。原本想省一次I/O操做,反而致使了大量的I/O操做,反而得不偿失。)
5. 单路有问题,因此咱们要用一些优化策略
增大max_length_for_sort_data参数的设置;
增大sort_buffer_size参数的设置;
提升Order By的速度
1. Order by时select * 是一个大忌只Query须要的字段, 这点很是重要。在这里的影响是:
1.1 当Query的字段大小总和小于max_length_for_sort_data 并且排序字段不是 TEXT|BLOB 类型时,会用改进后的算法——单路排序, 不然用老算法——多路排序。
1.2 两种算法的数据都有可能超出sort_buffer的容量,超出以后,会建立tmp文件进行合并排序,致使屡次I/O,可是用单路排序算法的风险会更大一些,因此要提升sort_buffer_size。
2. 尝试提升 sort_buffer_size
无论用哪一种算法,提升这个参数都会提升效率,固然,要根据系统的能力去提升,由于这个参数是针对每一个进程的
3. 尝试提升 max_length_for_sort_data
提升这个参数, 会增长用改进算法的几率。可是若是设的过高,数据总容量超出sort_buffer_size的几率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率.
6. 总结
13.3 GROUP BY关键字优化
1. group by实质是先排序后进行分组,遵守索引建的最佳左前缀
2. 当没法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置
3. where高于having,能写在where限定的条件就不要去having限定了。
/***一个高级的J2E工程师须要面对MySQL要有那些基本功夫呢<上>***/
一个高级的J2E工程师须要面对MySQL要有那些基本功夫呢?这个系列第一篇就到此结束了,第一篇对mysql的架构、mysql性能因素进行介绍,并对查询与索引优化进行了分析。再接下来的中、下两篇中,我再会为介绍大数据量处理理论、分区分库分表、MySql锁机制和主从复制等。不要急,敬请期待下一篇博文!
/***一个高级的J2E工程师须要面对MySQL要有那些基本功夫呢<上>***/
本文为博主原创文章,转载请注明出处!
http://www.cnblogs.com/libingbin/
感谢您的阅读。