MySQL 学习四 SQL优化
MySQL逻辑架构:
第一层:客户端层,连接处理,授权认证,安全等功能。
第二层:核心层,查询解析,分析,优化,缓存,内置函数(时间,数学,加密),存储过程,触发器,视图
第三层:存储引擎。负责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 使用枚举或整数代替字符串类型
OR
改写成 IN
: OR
的效率是n级别, IN
的效率是log(n)级别,in的个数建议控制在200以内
MySQL 学习四 SQL优化的更多相关文章
- python之MySQL学习——防止SQL注入
python之MySQL学习——防止SQL注入 学习了:https://www.cnblogs.com/xiaomingzaixian/p/7126840.html https://www.cnblo ...
- (6)MySQL进阶篇SQL优化(MyISAM表锁)
1.MySQL锁概述 锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算资源 (如 CPU.RAM.I/O 等)的抢占以外,数据也是一种供许多用户共享的资源.如何保证数 据并 ...
- MySql学习—— 查询性能优化 深入理解MySql如何执行查询
本篇深入了解查询优化和服务器的内部机制,了解MySql如何执行特定查询,从中也可以知道如何更改查询执行计划,当我们深入理解MySql如何真正地执行查询,明白高效和低效的真正含义,在实际应用中就能扬长避 ...
- MySql学习(六) —— 数据库优化理论(二) —— 查询优化技术
逻辑查询优化包括的技术 1)子查询优化 2)视图重写 3)等价谓词重写 4)条件简化 5)外连接消除 6)嵌套连接消除 7)连接消除 8)语义优化 9)非SPJ优化 一.子查询优化 1. ...
- [转]Mysql中的SQL优化与执行计划
From : http://religiose.iteye.com/blog/1685537 一,如何判断SQL的执行效率? 通过explain 关键字分析效率低的SQL执行计划. 比如: expla ...
- MySQL系列(七)--SQL优化的步骤
前面讲了如何设计数据库表结构.存储引擎.索引优化等内存,这篇文章会讲述如何进行SQL优化,也是面试中关于数据库肯定会被问到的, 这些内容不仅仅是为了面试,更重要的是付诸实践,最终用到工作当中 之前的M ...
- (4)MySQL进阶篇SQL优化(常用SQL的优化)
1.概述 前面我们介绍了MySQL中怎么样通过索引来优化查询.日常开发中,除了使用查询外,我们还会使用一些其他的常用SQL,比如 INSERT.GROUP BY等.对于这些SQL语句,我们该怎么样进行 ...
- mysql学习之 sql语句的技巧及优化
一.sql中使用正则表达式 select name,email from user where email Regexp "@163[.,]com$"; sql语句中使用Regex ...
- MYSQL学习笔记——sql语句优化之索引
上一篇博客讲了可以使用慢查询日志定位耗时sql,使用explain命令查看mysql的执行计划,以及使用profiling工具查看语句执行真正耗时的地方,当定位了耗时之后怎样优化呢?这篇博客会介绍my ...
随机推荐
- idea创建git分支
此时只是在本地创建好了分支,修改源代码后add,commit将本地分支提交到远程仓库 分支已创建,其它成员此时就可以从git拉分支
- mvn库
http://maven.aliyun.com/nexus/content/groups/public/ http://mvnrepository.com/http://search.maven.or ...
- Codeforces Round #367 (Div. 2) A , B , C
A. Beru-taxi time limit per test 1 second memory limit per test 256 megabytes input standard input o ...
- HTML5 拖放---drag和drop
拖放四步走:第一步:设置元素可拖放,即把 draggable属性设置为 true: 例:<div id="div" draggable="true"&g ...
- SQL Server 性能优化之RML Utilities:快速入门(Quick Start)(1)
SQL Server 性能优化之RML Utilities:快速入门(Quick Start)(1) 安装Quick Start工具 RML(Replay Markup Language)是MS ...
- RelativeSource={RelativeSource TemplatedParent}
<!--按钮样式开始--> <Style x:Key="NotifyBtnStyle" TargetType="{x:Type commondC ...
- ng-file-upload - samples
<script src="angular.min.js"></script> <!-- shim is needed to support non-H ...
- Webstorm2016使用技巧——SVN插件使用(svnToolBox)
1. 安装SVN 我这里使用的是TortoiseSVN-1.9.4.27285-x64-svn-1.9.4,安装过程需要注意的是,默认安装没有选择”command line client tools” ...
- this license has been cancelled
是因为IDEA注册码的问题, 解决方案: 修改此路径的hosts文件:C:\Windows\System32\drivers\etc\hosts 在其最后一行加入:“0.0.0.0 account.j ...
- 二维码的扫描和生成--第三方开源--ZXing
ZXing的二维码功能的提取lib下载地址:https://github.com/xuyisheng/ZXingLib 1.扫描二维码: 我们扫描就是要用到这个CaptureActivity类,直接把 ...