MySQL 学习四 SQL优化

 MySQL逻辑架构:mysql

  第一层:客户端层,链接处理,受权认证,安全等功能。sql

    第二层:核心层,查询解析,分析,优化,缓存,内置函数(时间,数学,加密),存储过程,触发器,视图数据库

    第三层:存储引擎。负责MySQL中数据的存储和提取。缓存

  

MySQL查询过程安全

  • 客户端/服务端通讯协议:须要注意的是,若是查询实在是太大,服务端会拒绝接收更多数据并抛出异常,于是在实际开发中,尽可能保持查询简单且只返回必需的数据,减少通讯间数据包的大小和数量是一个很是好的习惯,这也是查询中尽可能避免使用 SELECT * 以及加上 LIMIT 限制的缘由之一。
  • 查询缓存: 缓存命中的状况下,查询不会被解析,不会生成执行计划,更不会被执行。因此两个查询在任何字符上的不一样(例如:空格、注释),都会致使缓存不会命中。若是查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果都不会被缓存。查询缓存什么时候失效呢?MySQL 的查询缓存系统会跟踪查询中涉及的每一个表,若是这些表(数据或结构)发生变化,那么和这张表相关的全部缓存数据都将失效。正由于如此,在任何的写操做时,MySQL 必须将对应表的全部缓存都设置为失效。

    缓存对系统的额外消耗也不只仅在写操做,读操做也不例外:性能优化

    • 任何的查询语句在开始以前都必须通过检查,即便这条 SQL 语句永远不会命中缓存。服务器

    • 若是查询结果能够被缓存,那么执行完成后,会将结果存入缓存,也会带来额外的系统消耗。架构

      基于此,咱们要知道并非什么状况下查询缓存都会提升系统性能,缓存和失效都会带来额外消耗,只有当缓存带来的资源节约大于其本            身消耗的资源时,才会给系统带来性能提高。函数

 

    最后的忠告是不要轻易打开查询缓存,特别是写密集型应用。若是你实在是忍不住,能够将 query_cache_type 设置为 DEMAND。工具

 

    这时只有加入 SQL_CACHE 的查询才会走缓存,其余查询则不会,这样能够很是自由地控制哪些查询须要被缓存。

 

  • 语法解析和预处理 

   解析语法,生成解析树。进行合法校验。

  • 查询优化:    SQL解析,预处理,优化生成执行计划。
  • 查询执行引擎: 生成执行计划,并执行查询。
  • 返回结果给客户端:同时缓存结果。

 (不要听信你看到的关于优化的“绝对真理”,包括本文所讨论的内容,而应该是在实际的业务场景下经过测试来验证你关于执行计划以及响应时间的假设。

 

1 学习使用EXPLAIN 

2 建立正确的索引

  数据库的索引像书的索引同样,他们的位置信息被保存,而且包含数据库的主要信息。可使用EXPLAIN来查找

缺失的索引。

3 拒绝默认的设置:有三个关于MySQL性能优化的设置:

  innodb_buffer_pool_size:数据和索引被用做缓存的缓冲池。当数据库服务器有大量的系统内存时,能够用。

                                                这个设置不要过大,也不要频繁的引发交换。      

  innodb_log_file_size:单个InnoDB日志文件大小。

  max_connections:最大链接数

4 将数据库载入内存

  将频繁访问的数据放入内存(好比30%的数据放入内存)

5 SSD存储

6 横向扩展??

  纵向扩展

  横向扩展

7 追求可视化

  数据库受到流量负荷的影响,应用程序等致使的错误,为了快速、有效的解决问题,须要有监控机制。

  经常使用的监测工具:    MySQL企业监控器 /  Monyog /    Percona

8 Scheme设计与数据类型优化

  选择数据类型只要遵循小而简单的原则就好,越小的数据类型一般会更快,占用更少的磁盘、内存,处理时须要的 CPU 周期也更少。好比,整型就比字符操做代价低,于是会使用整型来存储 ip 地址,使用 DATETIME 来存储时间,而不是使用字符串。 

  • 在列上建立索引的话,应该把NULL改成NOT NULL: 不然索引的效率会大打折扣。
  • 对整数类型指定宽度,没有任何做用
  • UNSIGNED表示不容许负值:
  • 没有太大必要使用DECIMAL数据类型。就算在须要存储财务数据时,任然可使用BIGINT。好比精确到万分之一,那么能够将数据乘以一百万后使用BIGINT存储。这样能够避免浮点数计算不许确和DECIMAL精确计算代价高的问题。
  • TIMESTAMP使用4个字节存储,DATETIME使用8个字节存储。可是TIMESTAMP只能表示1970-2038年,而且TIMESTAMP的值因时区不一样而不一样  
  • 大多数状况下,没有必要使用枚举类型,缺点是:枚举的字符串列表是固定的,添加和删除必须使用ALTER TABLE
  • schema的列不要太多。
  • 大表ALTER TABLE很是耗时。

 9 建立高性能索引

  索引:一般说的索引时B-Tree索引。InnoDB用的是B+Tree.

  平衡二叉树: 若是想二叉树的查询性能高,须要二叉树是平衡二叉树。

  为何MYSQL不用平衡二叉树,而是用B+Tree树?随着数据库中数据的增长,索引自己大小随之增长,不可能所有存储在内存中,所以索引每每以索引文件的形式存储在磁盘上。这样的话,索引查找过程当中就要产生磁盘 I/O 消耗,相对于内存存取,I/O 存取的消耗要高几个数量级。能够想象一下一棵几百万节点的二叉树的深度是多少?若是将这么大深度的一颗二叉树放磁盘上,每读取一个节点,须要一次磁盘的 I/O 读取,整个查找的耗时显然是不可以接受的。那么如何减小查找过程当中的 I/O 存取次数?一种行之有效的解决方法是减小树的深度,将二叉树变为 m 叉树(多路搜索树),而 B+Tree 就是一种多路搜索树

   B+Tree树特征:

  • 全部关键字都存在叶子节点:   每一个节点设置为页的整数倍(预读取时,能够读一页)
  •  叶子节点由指针链接:这样便于区间查找。

10 MySQL不使用索引的状况:非独立的列

   “独立的列” :索引列不能是表达式的一部分,也不能是函数的参数

11 前缀索引

      列很长的状况下,索引开始的部分字符,有效节约索引空间(须要前面的部分有必定的区分度)

12 多列索引和索引顺序:

       在多数状况下,在多个列上创建独立的索引并不能提升查询性能,理由很是简单,MySQL 不知道选择哪一个索引的查询效率更好(由于数据库中有多个索引的B+Tree, MySQL只能选择一个树作索引)。

  多个列之间用AND时:联合索引优于独立索引。

  多个列之间用OR、uniton时:每每会不走索引。

  这种状况下,创建包含多个列的联合索引更加高效

       建立联合索引时, 把选择性更高的放在前面(由于这样,经过第一个过滤条件就能过滤掉大读书数据)。

13 避免多个范围条件

     只能用其中一个索引。

14 覆盖索引

  若是一个索引包含或者说覆盖全部须要查询的字段的值(就是select 后面的列),那么就没有必要再回表查询,这就称为覆盖索引。

15 使用索引扫描来排序

  • 对结果集进行排序的操做:
  • 按照索引顺序扫描得出的结果天然是有序的

       扫描索引自己很快,由于只须要从一条索引记录移动到相邻的下一条记录。但若是索引自己不能覆盖全部须要查询的列,那么就不得不每扫描一条索引记录就回表查询一次对应的行。

  在设计索引时,若是一个索引既可以知足排序,又知足查询,是最好的!!只有当索引的列顺序和 ORDER BY 子句的顺序彻底一致,而且全部列的排序方向也同样时,才可以使用索引来对结果作排序。

  若是查询须要关联多张表,则只有 ORDER BY 子句引用的字段所有为第一张表时,才能使用索引作排序。

       ORDER BY 子句和查询的限制是同样的,都要知足最左前缀的要求。

16 避免冗余和重复索引

17 删除长期未使用的索引

18 查询优化

  • count:两个做用,其一是统计某个列值的数量,其二是统计行数。统计列值时,要求列值是非空的,它不会统计 NULL。若是确认括号中的表达式不可能为空时,实际上就是在统计行数。若是要统计行数,直接使用 COUNT(*),意义清晰,且性能更好。一般来讲,执行 COUNT() 都须要扫描大量的行才能获取到精确的数据,所以很难优化。
  • 优化关联查询:表之间经过冗余字段关联,比使用Join有更好的性能。             

          在关联查询的状况下:Group By中的表达式只涉及到一个表中的列。这样才有可能使用索引优化。

              A和B表用c类关联时,不须要在A上创建索引,在B上建索引就OK(由于,A表不管是否有索引,都要遍历,B表和C表则须要走索引去寻找匹配的记录)。

19 优化LIMIT分页

LIMIT 10000 20 这样的查询,MySQL 须要查询 10020 条记录而后只返回 20 条记录,前面的 10000 条都将被抛弃,这样的代价很是高。

优化这种查询一个最简单的办法就是尽量的使用覆盖索引扫描,而不是查询全部的列

20 优化UNION

  除非确实须要服务器去重,不然就必定要使用 UNION ALL,若是没有 ALL 关键字,MySQL 会给临时表加上 DISTINCT 选项,这会致使整个临时表的数据作惟一性检查,这样作的代价很是高。

21 假设有联合索引  (user_name, sex, age),  如下三个查询,其实谓词的顺序不重要,都会用到联合索引的,这是由于MySQL作了优化。

  可是联合索引的顺序却很重要,看下面22和23

select * from test where user_name=’test1’ and sex>0 and age =10
select * from test where sex>0 and user_name=’test1’ and age =10
select * from test where age =10 and user_name='test1' and sex>0

 22 最左原则

  mysql创建多列索引(联合索引)有最左前缀的原则,即最左优先,如:

  若是有一个2列的索引(col1,col2),则已经对(col1)、(col1,col2)上创建了索引;
  若是有一个3列索引(col1,col2,col3),则已经对(col1)、(col1,col2)、(col1,col2,col3)上创建了索引;

23  联合索引总结

  • "一个顶三个"。建了一个(a,b,c)的复合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,由于每多一个索引,都会增长写操做的开销和磁盘空间的开销。对于大量数据的表,这但是不小的开销!
  • 覆盖索引索引能覆盖全部要查询的列,不用回表)。一样的有复合索引(a,b,c),若是有以下的sql: select a,b,c from table where a=1 and b = 1。那么MySQL能够直接经过遍历索引取得数据,而无需回表,这减小了不少的随机io操做。减小io操做,特别的随机io实际上是dba主要的优化策略。因此,在真正的实际应用中,覆盖索引是主要的提高性能的优化手段之一

          (一直在思考,对于select 后面的字段是否须要创建索引, 对于上面的a,b,c索引,不须要回表;对于a,b索引,还须要回表。)

  • 引列越多,经过索引筛选出的数据越少。有1000W条数据的表,有以下sql:select * from table where a = 1 and b =2 and c = 3,假设假设每一个条件能够筛选出10%的数据,若是只有单值索引,那么经过该索引能筛选出1000W*10%=100w 条数据,而后再回表从100w条数据中找到符合b=2 and c= 3的数据,而后再排序,再分页;若是是复合索引,经过索引筛选出1000w *10% *10% *10%=1w,而后再排序、分页,哪一个更高效,一眼便知 !!!!!!!!!

24 like

  %xx   --   不走索引;    XX% -- 走索引;

25 OR

  OR操做致使,不容易优化。

26  避免 select *

27 知道什么时候使用临时表

  防止对大表查询两次。还可使用临时表,大幅减小链接大表所需的处理能力。

  若是你必须将一个表链接到大表,该大表上又有条件只需将大表中所需的那部分数据提取到临时表中,而后再与该临时表链接,就能够提高查询性能(这个在实际项目中用到过,在表join以前,先把大表过滤掉尽量多的行)

28 预暂存数据

  若是你有一个报表或存储过程(或一组)要对大表执行相似的链接操做,经过提早链接表,并将它们持久化存储到一个表中来预暂存数据,就能够对你大有帮助。

 29 批量删除和更新

30 避免嵌套视图

31 不要使用逆向搜索

  SELECT * FROMCustomers WHERE RegionID <> 3 

       索引与该查询结合使用,由于它是逆向搜索,须要借助表扫描来逐行比较。

       优化方法:SELECT * FROM Customers WHERE RegionID<3 UNION ALL SELECT * FROM Customers WHERE RegionID >3

32 索引的使用原则

  • 避免对索引字段进行计算操做
  • 避免在索引字段上使用not,  <>,  != 
  • 避免在索引列上使用IS NULL 和 IS NOT  NULL
  • 避免在索引列上出现数据类型的转换
  • 避免在索引字段上使用函数(尽可能在应用程序中实现)
  • 避免在索引的列中使用空值。

33 使用UNION all,  尽可能避免UNION(Union须要排序,而后去重

34 避免对索引列加函数修饰

where trunc(create_date)=trunc(:date1)  
原本create_date建索引了,可是加上trunc后,索引失效,改为以下:
where create_date>=trunc(:date1) and create_date<trunc(:date1)+1< pre="">
where create_date between trunc(:date1) and trunc(:date1)+1-1/(24*60*60)

35 where子句中使用in, not in, or having.
使用exist , not exist代替in, not in

这个须要实践的检验,待续
36 排序
  带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行,
耗费资源的排序(SORT)功能。 DISTINCT须要一次排序,其余的至少两次排序。
   这个深有感触,MySQL的order by效率及其低下。

 37  应尽可能避开where子句中进行null值判断:

   select id from t where num is null 能够在num上设置默认值0,确保表中num列没有null值,而后这样查询: select id from t                 where num=0

38 并非全部索引对查询都有效:

  SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即便在sex上建了索引也对查询效率起不了做用。

39 索引并非越多越好,索引当然能够提升相应的 select 的效率,但同时也下降了 insert 及 update 的效率

40  应尽量的避免更新索引数据列,由于索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将致使整个表记录的顺序的调整,会耗费至关大的资源。若应用系统须要频繁更新索引数据列,那么须要考虑是否应将该索引建为索引。  

41 尽可能使用数字型字段,若只含数值信息的字段尽可能不要设计为字符型,这会下降查询和链接的性能,并会增长存储开销。这是由于引擎在处理查询和链接时会逐个比较字符串中每个字符,而对于数字型而言只须要比较一次就够了。

42 尽量的使用 varchar/nvarchar 代替 char/nchar ,由于首先变长字段存储空间小,能够节省存储空间,其次对于查询来讲,在一个相对较小的字段内搜索效率显然要高些。

43 应尽可能避免在 where 子句中使用 or 来链接条件,不然将致使引擎放弃使用索引而进行全表扫描,如: select id from t where num=10 or num=20 能够这样查询: select id from t where num=10 union all select id from t where num=20  

44 应尽可能避免在 where 子句中对字段进行表达式操做,这将致使引擎放弃使用索引而进行全表扫描。如: select id from t where num/2=100 应改成: select id from t where num=100*2

45 不少时候用 exists 代替 in 是一个好的选择: select num from a where num in(select num from b) 用下面的语句替换: select num from a where 

exists(select 1 from b where num=a.num)

46  尽可能使用 TINYINT、 SMALLINT、 MEDIUM_INT做为整数类型而非 INT,若是非负则加上 UNSIGNED

47 VARCHAR的长度只分配真正须要的空间

 48 使用枚举或整数代替字符串类型

49OR改写成 IN: OR的效率是n级别, IN的效率是log(n)级别,in的个数建议控制在200之内

相关文章
相关标签/搜索