MYSQL执行SQL的流程
SQL的执行过程;mysql
客户端发送一条查询给服务器;
服务器经过权限检查以后,先会检查查询缓存,若是命中了缓存,则当即返回存储在缓存中的结果。不然进入下一阶段;
服务器端进行SQL解析、预处理,再由优化器根据该SQL所涉及到的数据表的统计信息进行计算,生成对应的执行计划;
MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;
将结果返回给客户端。
SQL执行的最大瓶颈在于磁盘的IO,即数据的读取;不一样SQL的写法,会形成不一样的执行计划的执行,而不一样的执行计划在IO的上面临彻底不同的数量级,从而形成性能的差距; 因此,咱们说,优化SQL,其实就是让查询优化器根据程序猿的计划选择匹配的执行计划,来减小查询中产生的IO;算法
schema(表结构)对性能的影响
冗余数据的处理; 适当的数据冗余能够提升系统的总体查询性能(在P2P中,在userinfo对象中有realname和idnumber); 关系数据库的三范式: 第一范式(1NF)是对关系模式的基本要求,不知足第一范式(1NF)的数据库就不是关系数据库,是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值; 第二范式(2NF)要求数据库表中的每一个实例或行必须能够被唯一地区分。 第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。 (不容许有冗余数据)sql
大表拆小表,有大数据的列单独拆成小表;数据库
在一个数据库中,通常不会设计属性过多的表;
在一个数据库中,通常不会有超过500/1000万数据的表(拆表,按照逻辑拆分,按照业务拆分);
有大数据的列单独拆成小表(富文本编辑器,CKeditor);
根据需求的展现设置更合理的表结构;缓存
把经常使用属性分离成小表;服务器
在P2P项目中,咱们把logininfo和userinfo和account表拆成了三张表;
减小查询经常使用属性须要查询的列;
便于经常使用属性的集中缓存;
####索引和索引的优化: 1,索引的原理:把无序的数据变成有序的查询;网络
索引:
索引的物理结构:数据结构
数据库文件存储的位置:my.ini配置文件中dataDir对应的数据目录中;
每个数据库一个文件夹;
MYISAM引擎:每个表(table_name)--> table_name.MYI:存放的是数据表对应的索引信息和索引内容; table_name.FRM:存放的是数据表的结构信息; table_name.MYD:存放的是数据表的内容;
InnoDB引擎:每个表(table_name)--> table_name.frm:存放的是数据表的结构信息; 数据文件和索引文件都是统一存放在ibdata文件中;
索引文件都是额外存在的,对索引的查询和维护都是须要消耗IO的;
索引的结构:并发
默认状况下,一旦建立了一个表,这个表设置了主键,那么MYSQL会自动的为这个主键建立一个unique的索引;
索引类型:
Normal:普通的索引;容许一个索引值后面关联多个行值;
UNIQUE:惟一索引;容许一个索引值后面只能有一个行值;以前对列添加惟一约束其实就是为这列添加了一个unique索引;当咱们为一个表添加一个主键的时候,其实就是为这个表主键列(设置了非空约束),并为主键列添加了一个惟一索引;
Fulltext:全文检索,mysql的全文检索只能用myisam引擎,而且性能较低,不建议使用;
索引的方法(规定索引的存储结构): (数据结构,算法基础)
b-tree:是一颗树(二叉树,平衡二叉树,平衡树(B-TREE)) 使用平衡树实现索引,是mysql中使用最多的索引类型;在innodb中,存在两种索引类型,第一种是主键索引(primary key),在索引内容中直接保存数据的地址;第二种是其余索引,在索引内容中保存的是指向主键索引的引用;因此在使用innodb的时候,要尽可能的使用主键索引,速度很是快; b-tree中保存的数据都是按照必定顺序保存的数据,是能够容许在范围以内进行查询; select * from accountflow where account_id <100;
hash:把索引的值作hash运算,并存放到hash表中,使用较少,通常是memory引擎使用;优势:由于使用hash表存储,按照常理,hash的性能比B-TREE效率高不少。 hash索引的缺点: 1,hash索引只能适用于精确的值比较,=,in,或者<>;没法使用范围查询; 2,没法使用索引排序; 3,组合hash索引没法使用部分索引; 4,若是大量索引hash值相同,性能较低;
索引的利弊:
索引的好处: 1,提升表数据的检索效率; 2,若是排序的列是索引列(若是查询的列==排序的列[而且在这列上作了索引]),大大下降排序成本; 3,在分组操做中若是分组条件是索引列,也会提升效率;
索引的问题: 索引须要额外的维护成本;由于索引文件是单独存在的文件,对数据的增长,修改,删除,都会产生额外的对索引文件的操做,这些操做须要消耗额外的IO,会下降增/改/删的执行效率;
怎么建立索引?
较频繁的做为查询条件的字段应该建立索引;
惟一性太差的字段不适合单首创建索引,即便频繁做为查询条件; 做为索引的列,若是不能有效的区分数据,那么这个列就不适合做为索引列;好比(性别,状态很少的状态列) 举例:SELECT sum(amount) FROM accountflow WHERE accountType = 0; 假如把accountType做为索引列,由于accountType只有14种,因此,若是根据accountType来建立索引,最多只能按照1/14的比例过滤掉数据;可是,若是可能出现,只按照该条件查询,那咱们就要考虑到其余的提高性能的方式了; 第一种方案:单首创建一个系统摘要表;在这个里面有一个列叫作系统总充值金额;每次充值成功,增长这个列的值;之后要查询系统总充值金额,只须要从这个系统摘要表中查询;(缺陷:若是充值频率过快,会致使表的锁定问题;) 第二种方案:流水一旦发生了,是不会随着时间改变的;针对这种信息,咱们就可使用增量查询(结算+增量查询); 1,建立一张日充值表;记录每一天的充值总金额(beginDate,endDate,totalAmount),天天使用定时器对当前的充值记录进行结算;日充值报表里面记录只能记录截止昨天的数据; 2,建立一张月充值表;记录每个月的充值总金额(beginDate,endDate,totalAmount),每个月最后一天使用定时器对当月的充值记录进行结算(数据源从日充值报表来); 3,要查询系统总充值,从月报表中汇总(当前月以前的总充值金额),再从日充值报表中查询当天以前的日报表数据汇总;再从流水中查询当前截止查询时间的流水;使用另一张当天流水表记录当天的流水;再把三个数据累加;
更新很是频繁的字段不适合建立索引;缘由,索引有维护成本;
不会出如今WHERE 子句中的字段不应建立索引;
索引不是越多越好;(只为必要的列建立索引) 1,无论你有多少个索引,一次查询至多采用一个索引;(索引和索引之间是独立的) 2,由于索引和索引之间是独立的,因此说每个索引都应该是单独维护的;数据的增/改/删,会致使全部的索引都要单独维护;
索引的使用限制:
BLOB 和TEXT 类型的列只能建立前缀索引
MySQL 目前不支持函数索引(在MYSQL中,索引只能是一个列的原始值,不能把列经过计算的值做为索引); 实例:请查询1981年入职的员工: SELECT * FROM emp WHERE year(hire_date)='1981'; 问题:查询的列是在过滤以前通过了函数运算;因此,就算hire_date做为索引,year(hire_date)也不会使用索引; 解决方案: 1,SELECT * FROM emp WHERE hire_date BETWEEN '1981-01-01' AND '1981-12-31'; 2,在建立一列,这列的值是year(hire_date),而后把这列的值做为索引; 3. 使用不等于(!= 或者<>)的时候MySQL 没法使用索引
过滤字段使用了函数运算后(如abs(column)),MySQL 没法使用索引]
Join 语句中Join 条件字段类型不一致的时候MySQL 没法使用索引
使用LIKE 操做的时候若是条件以通配符开始( '%abc...')MySQL 没法使用索引 1,字符串是能够用来做为索引的; 2,字符串建立的索引按照字母顺序排序; 3,若是使用LIKE,实例:SELECT * FROM userinfo WHERE realName LIKE '吴%';这种状况是可使用索引的; 可是LIKE '_嘉' 或者LIKE '%嘉'都是不能使用索引的;
使用非等值查询的时候MySQL 没法使用Hash 索引
单列索引和复合索引:
由于一个查询一次至多只能使用一个索引,因此,若是都使用单值索引(一个列一个索引),在数据量较大的状况下,不能很好的区分数据;
因此,MYSQL引入了多值索引(复合索引); 复合索引就是由多列的值组成的索引;而且(注意),多列的索引是有顺序的!!!!
复合索引的原理:就是相似orderby(orderby后面能够跟多个排序条件order by hire_date,username desc); 就是在排序和分组(建立倒排表的时候),按照多个列进行排序和合并; SELECT * FROM accountflow WHERE actionTime < 'xxxxx' AND account_id = 5 可使用actionTime+account_id的复合索引; SELECT * FROM accountflow WHERE actionTime < 'xxxxx' 可使用actionTime+account_id的复合索引; SELECT * FROM accountflow WHERE account_id = 5 不可使用actionTime+account_id的复合索引; SELECT * FROM accountflow WHERE account_id = 5 AND actionTime < 'xxxxx' 不可使用actionTime+account_id的复合索引;
复合索引,在查询的时候,遵照向左原则;只要在查询的时候,是按照复合索引从左到右的顺序依次查询,无论查询条件是否彻底知足全部的符合索引的列,均可以使用部分的符合索引;
在实际应用中,基本上都使用复合索引;
查看MYSQL的执行计划和执行明细状态(explain+profiling)
Explain:可让咱们查看MYSQL执行一条SQL所选择的执行计划;
Profiling:能够用来准肯定位一条SQL的性能瓶颈;
EXPLAIN:
使用方式: explain SQL;
返回结果:
ID:执行查询的序列号;
select_type:使用的查询类型
DEPENDENT SUBQUERY:子查询中内层的第一个SELECT,依赖于外部查询的结果集;
DEPENDENT UNION:子查询中的UNION,且为UNION 中从第二个SELECT 开始的后面全部SELECT,一样依赖于外部查询的结果集;
PRIMARY:子查询中的最外层查询,注意并非主键查询;
SIMPLE:除子查询或者UNION 以外的其余查询;
SUBQUERY:子查询内层查询的第一个SELECT,结果不依赖于外部查询结果集;
UNCACHEABLE SUBQUERY:结果集没法缓存的子查询;
UNION:UNION 语句中第二个SELECT 开始的后面全部SELECT,第一个SELECT 为PRIMARY 8,UNION RESULT:UNION 中的合并结果;
table:此次查询访问的数据表;
type:对表所使用的访问方式:
all:全表扫描
const:读常量,且最多只会有一条记录匹配,因为是常量,因此实际上只须要读一次;
eq_ref:最多只会有一条匹配结果,通常是经过主键或者惟一键索引来访问;
fulltext:全文检索,针对full text索引列;
index:全索引扫描;
index_merge:查询中同时使用两个(或更多)索引,而后对索引结果进行merge 以后再读取表数据;
index_subquery:子查询中的返回结果字段组合是一个索引(或索引组合),但不是一个主键或者惟一索引;
rang:索引范围扫描;
ref:Join 语句中被驱动表索引引用查询;
ref_or_null:与ref 的惟一区别就是在使用索引引用查询以外再增长一个空值的查询;
system:系统表,表中只有一行数据;
unique_subquery:子查询中的返回结果字段组合是主键或者惟一约束;
possible_keys:可选的索引;若是没有使用索引,为null;
key:最终选择的索引;
key_len:被选择的索引长度;
ref:过滤的方式,好比const(常量),column(join),func(某个函数);
rows:查询优化器经过收集到的统计信息估算出的查询条数;
Extra:查询中每一步实现的额外细节信息
Distinct:查找distinct 值,因此当mysql 找到了第一条匹配的结果后,将中止该值的查询而转为后面其余值的查询;
Full scan on NULL key:子查询中的一种优化方式,主要在遇到没法经过索引访问null值的使用使用;
Impossible WHERE noticed after reading const tables:MySQL Query Optimizer 经过收集到的统计信息判断出不可能存在结果;
No tables:Query 语句中使用FROM DUAL 或者不包含任何FROM 子句;
Not exists:在某些左链接中MySQL Query Optimizer 所经过改变原有Query 的组成而使用的优化方法,能够部分减小数据访问次数;
Select tables optimized away:当咱们使用某些聚合函数来访问存在索引的某个字段的时候,MySQL Query Optimizer 会经过索引而直接一次定位到所需的数据行完成整个查询。固然,前提是在Query 中不能有GROUP BY 操做。如使用MIN()或者MAX()的时候;
Using filesort:当咱们的Query 中包含ORDER BY 操做,并且没法利用索引完成排序操做的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。
Using index:所须要的数据只须要在Index 便可所有得到而不须要再到表中取数据;
Using index for group-by:数据访问和Using index 同样,所需数据只须要读取索引便可,而当Query 中使用了GROUP BY 或者DISTINCT 子句的时候,若是分组字段也在索引中,Extra 中的信息就会是Using index for group-by;
Using temporary:当MySQL 在某些操做中必须使用临时表的时候,在Extra 信息中就会出现Using temporary 。主要常见于GROUP BY 和ORDER BY 等操做中。
Using where:若是咱们不是读取表的全部数据,或者不是仅仅经过索引就能够获取全部须要的数据,则会出现Using where 信息;
Using where with pushed condition:这是一个仅仅在NDBCluster 存储引擎中才会出现的信息,并且还须要经过打开Condition Pushdown 优化功能才可能会被使用。控制参数为engine_condition_pushdown 。
profiling:
Query Profiler是MYSQL5.1以后提供的一个很方便的用于诊断Query执行的工具,可以准确的获取一条查询执行过程当中的CPU,IO等状况; 1. 开启profiling:set profiling=1; 2. 执行QUERY,在profiling过程当中全部的query均可以记录下来; 3. 查看记录的query:show profiles; 4. 选择要查看的profile:show profile cpu, block io for query 6编辑器
status是执行SQL的详细过程; Duration:执行的具体时间; CPU_user:用户CPU时间; CPU_system:系统CPU时间; Block_ops_in:IO输入次数; Block_ops_out:IO输出次数; profiling只对本次会话有效;
JOIN:
1,JOIN的原理: 在mysql中使用Nested Loop Join来实现join; A JOIN B:经过A表的结果集做为循环基础,一条一条的经过结果集中的数据做为过滤条件到下一个表中查询数据,而后合并结果; 2,JOIN的优化原则: 1,尽量减小Join 语句中的Nested Loop 的循环总次数,用小结果集驱动大结果集; 2,优先优化Nested Loop 的内层循环; 3,保证Join 语句中被驱动表上Join 条件字段已经被索引; 4,扩大join buffer的大小;
SQL优化原则:
[原则一:选择须要优化的SQL]
选择须要优化的SQL:不是全部的SQL都须要优化,在优化的过程当中,首选更须要优化的SQL; 怎么选择? 优先选择优化高并发低消耗的SQL;
1小时请求1W次,1次10个IO;
1小时请求10次,1次1W个IO; 考虑:
从单位时间产生的IO总数来讲,相同的;
针对一个SQL,若是我能把10个IO变成7个IO,一小时减小3W个IO; 针对第二个SQL,若是能把1W个IO变成7K个IO,一小时减小3W个IO;
从优化难度上讲,1W->7K难的多;
从总体性能上来讲,第一个SQL的优化可以极大的提高系统总体的性能;第二个SQL慢一点,无非也就是10个链接查询慢一点;
定位性能瓶颈;
SQL运行较慢有两个影响缘由,IO和CPU,明确性能瓶颈所在;
明确优化目标;
[原则二:从Explain和Profile入手]
任何SQL的优化,都从Explain语句开始;Explain语句可以获得数据库执行该SQL选择的执行计划;
首先明确须要的执行计划,再使用Explain检查;
使用profile明确SQL的问题和优化的结果;
[原则三:永远用小结果集驱动大的结果集]
[原则四:在索引中完成排序]
[原则五:使用最小Columns]
减小网络传输数据量;
特别是须要使用column排序的时候.为何?MYSQL排序原理,是把全部的column数据所有取出,在排序缓存区排序,再返回结果;若是column数据量大,排序区容量不够的时候,就会使用先column排序,再取数据,再返回的屡次请求方式;
[原则六:使用最有效的过滤条件]
过多的WHERE条件不必定可以提升访问性能;
必定要让where条件使用本身预期的执行计划;
[原则七:避免复杂的JOIN和子查询]
复杂的JOIN和子查询,须要锁定过多的资源,MYSQL在大量并发状况下处理锁定性能降低较快;
不要过多依赖SQL的功能,把复杂的SQL拆分为简单的SQL;
MySQL子查询性能较低,应尽可能避免使用;