Java小白之MySQL 查询优化之道

几天前更过一篇技术笔记“MySQL全备份如何只恢复一个库或者一个表?

今日来讲讲 :MySQL 查询优化之道

一、查询优化器模块

查询优化器的任务是发现执行 SQL 查询的最佳方案。大多数查询优化器,要么基于规则、要么基于成本。

大多数查询优化器,包含 MySQL 的查询优化器,总或多或少地在所有可能的查询评估方案中搜索最佳方案。

MySQL 中 MySQL Query Optimizer 是优化器的核心,当 MySQL 数据拿到一个 Query 语句之后会交给 Query Optimizer 去解析,并产生一个最优的执行计划(这个是 Optimizer 认为是最优的,但不一定是真正最优的,就跟 Oracle 数据库会估算错 rows 一样)。

然后数据库按照这个执行计划去执行查询语句。

在 SQL 语句整个执行过程中,Optimizer 是最耗时的,但是也有第三方工具为了提高性能绕开 MySQL 的 Query Optimizer 模块,比如:handlersocket。

对于多表关联查询,MySQL 优化器所查询的可能方案数随查询中引用的表的数目成指数增长。对于小数量的表,这不是一个问题。

然而,当提交的查询需要的结果集很大时,查询优化所花的时间会很容易地成为服务器性能的瓶颈。

查询优化的一个更加灵活的方案时容许用户控制优化器详细地搜索最佳查询评估方案。一般思想是调查的方案越少,它编译一个查询所花费的时间越少。

另外,由于优化器跳过一些方案,它可能错过一个最佳方案。优化器关于方案数量评估的行为可以通过两个系统变量来控制:

optimizer_prune_level 变量告诉优化器根据对每个表访问的行数的估计跳过一些方案。我们的试验显示该类 “有根据的猜测” 很少错过最佳方案,并且可以大大降低查询编辑次数。

这就是为什么默认情况该选项为 on(optimizer_prune_level=1)。

然而,如果你认为优化器错过了一个更好的查询方案,则该选项可以关闭(optimizer_prune_level=0),风险是查询编辑花费的时间更长。

请注意即使使用该启发,优化器仍然可以探测呈指数数目的方案。

timizer_search_depth 变量告诉优化器对于每个未完成的 “未来的” 方案,应查看多深,以评估是否应对它进一步扩大。

optimizer_search_depth 值较小会使查询编辑次数大大减小。

例如,如果optimizer_search_depth 接近于查询中表的数量,对 12、13 或更多表的查询很可能需要几小时甚至几天的时间来编译。

同时,如果用 optimizer_search_depth 等于 3 或 4 编辑,对于同一个查询,编译器编译时间可以少于 1 分钟。

如果不能确定合理的 optimizer_search_depth 值,该变量可以设置为 0,告诉优化器自动确定该值。

二、查询优化的基本思路

不管做项目设计还是产品设计都需要先有思路,才能规避一些问题。

当然 MySQL 查询优化也需要研发或者 DBA 拥有一些思路,唯有思路指导书写,才会更加合理。

1. 优化更需要优化的 Query 语句

应该优化并发高的 Query 语句,不至于高并发下,由于 SQL 导致应用程序卡死,比如 php-fpm 的大量等待,而且一个高并发的 Query 语句,如果走错执行计划,本来只需要扫描几百行,结果扫描了几百万行,可能会有灾难性的后果,更加会导致业务卡顿,尤其是核心业务下出现的高并发 Query 语句。

2. 查看执行计划调整 Query 语句

根据 explain extended SQL 分析查询语句,就能查看执行计划,这个时候需要关注执行计划中的一些要素:

id:查询的序列化

select type

depent subquery:说明该查询是子查询中的第一个 Select, 依赖与外部查询的结果集

PRIMARY:子查询的最外层查询,注意不是主键查询

simple:除子查询或者 UNION 之外的其它查询

table:访问数据表的名称,书写 SQL 的人,需要明确此表是否是核心表、是否是大数据量表等

type 扫描方式

all:全表扫描

const:读常量,且最多只有一条记录匹配。由于是常量只需要读一次

index:全索引扫描

eq_ref:最多只有一条匹配结果 通过主键和唯一索引来访问的

range:索引范围扫描

possible_keys:该查询可以利用到的索引有哪些

key:优化器模块选择用了哪个索引,有索引不一定就会用到,看执行计划才知道用了哪个。

key_len:索引长度

rows:返回的行数

extra:附加信息,比如 using filesort---> 说明用了排序算法

filtered:列给出了一个百分比的值,这个百分比值和 rows 列的值一起使用,可以估计出那些将要和 QEP 中的前一个表进行连接的行的数目。前一个表就是指 id 列的值比当前表的 id 小的表。这一列只有在 EXPLAIN EXTENDED 语句中才会出现。

3. 学会查看性能损耗(cpu 消耗、io 消耗)

当发现有慢 Query 语句时,需要定位到底是哪里慢,CPU 还是 IO 等:

mysql>set profiling=1;mysql>show profiles;mysql>show profile cpu,block io for query n;

三、查询的基本原则

1. 永远用小结果集驱动大结果集

做 join 查询时,驱动表,一定是条件限定后记录较少的表。

MySQL 的 join 只有一种算法 nested loop 也就是程序中的 for 循环,通过嵌套循环实现,驱动结果集越大,所需要循环的次数越多,访问被驱动表的次数也越多。降低 IO 同时降低 CPU。

2. 只查询需要的列

只查询需要的列,可以让 IO 降低,列和排序算法也有关系。

3. 仅仅使用最有效的过滤条件

前提是用 a 条件 查询出结果 用 b 条件查询出结果,a、b 都用查询出结果,这三次结果都一样。

到底是用 a 条件还是 b 条件,还是两个条件都限定,只能看执行计划。

4. 尽量避免复杂的 join 和子查询

5. 尽量在索引列上完成排序和查询

在索引列上排序:索引列上是排好序的,不需要启动额外的排序的算法降低了 CPU 的损耗。

在索引列上查询:降低了 IO 的损耗

创建索引,优化器模块并不一定会用,但可以 SQL 中加上 force index(强制走那个索引)

四、索引利弊及索引分类

万事万物都有利弊,一个东西的出现,比如会在不同场景下有好好坏,就看如何权衡。

好处:

通过索引列查询数据,能够提高数据检索的效率,降低数据库的 IO 成本。

通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU 的消耗。

坏处:

假设表 a 其中有列 column ca 给其创建索引 indxaca:

每次更新 ca 的操作,都会调整因为更新所带来的键值变化后的索引信息,这样就会增加 IO 损耗,索引列也是要占用空间的,a 列数据的增多,indxaca 索引占用的空间也会不断增长。所以索引还会带来存储空间资源的消耗。

五、索引分类

b-tree 索引:根据平衡二叉树演变来的

hash 索引:

hash 索引只能满足 "="、"in" <> 查询,不能支持范围查询

hash 索引无法被利用进行排序操作

hash 索引不能利用部分索引键查询

hash 索引不能避免表扫描

full-text 索引:只有 myisam 存储引擎支持 ---> 只有 char 、varchar、text 支持,但是在 MySQL 5.7,innodb 存储引擎也支持啦。

R-Tree 索引:主要解决空间数据检索问题,极少使用。

六、索引相关优化

1. 如何判断是否需要创建索引

频繁作为查询条件的字段应该创建索引。

唯一性太差的字段不适合单独创建索引。比如该字段重复上千万;即使你创建了索引优化器模块是不会选择使用的;会有极大的性能问题 有很多重复值,会带来大量的随机 IO 甚至是重复 IO。

更新非常频繁的字段不适合创建索引:不仅仅更新表中的数据,还需要更新索引数据 IO 访问增大。

不会出现在 where 字句中的字段不该创建索引。

单键索引还是组合索引。

2. MySQL 中索引的限制

是否用到了索引可以查看执行计划

在任何索引列上做计算、函数、类型转换(哪怕是自动的)都会使得索引失效而转向全表扫描操作:不要在索引列上做任何操作因为可能为导致索引失效。

MySQL 在使用不等于 (!= or <>) 的时候无法使用索引会导致全表扫描。

is null ,is not null 也无法使用索引。

join 语句中 join 条件字段类型不一致的时候 MySQL 无法使用索引。

模糊查询的时候 (like 操作) 如果以通配符开头 ('%abc...')MySQL 索引失效会变成全表扫描的操作。

如果使用的是 hash 索引,在做非等值连接时候无法使用索引,会是全表扫描的操作。

在 MySQL 中 BLOB 和 Text 类型的列只能创建前缀索引。

MyISAM 存储引擎的话索引键长度总和不能超过 1000 字节。(好像从 5.7 之后,大多默认 innodb 存储引擎)

当有唯一性索引和非唯一性索引都存在时,往往只会选择唯一性索引。

组合索引,查询时组合索引第一列出现的时候会使用索引。

3. 使用索引的一些建议

对于单键索引,尽量选择针对当前 Query 过滤性更好的索引。

在选择组合索引的时候,当前 Query 中过滤性最好的字段在索引字段顺序中,位置越靠前越好。

在选择组合索引的时候,尽量选择可以能够包含当前 Query 中的 where 字句中更多字段的索引。

尽可能通过分析统计信息和调整 Query 的写法来达到选择合适索引的目的。减少通过使用 Hint 认为控制索引的选择,如果使用 Hint 会使得后期维护成本比较高。

综上所述,大致简单明了的阐述了 MySQL 查询优化一些相关的东西,至少对于中小型企业,可以作为研发人员的数据库规范,避免后期迁移或扩容时的一些问题。一切相关问题可以在读者圈交流,谢谢翻到文末的众人。

来源:科多大数据

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容