热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【本人秃顶程序员】开发人员不可错过的MySQL索引和查询优化

←←←←←←←←←←←←快!点关注一、索引相关1.索引基数基数是数据列所包含的不同值的数量。例如,某个数据列包含值1、3、7、4、7、3,

←←←←←←←←←←←← 快!点关注

一、索引相关

1.索引基数

基数是数据列所包含的不同值的数量。例如,某个数据列包含值1、3、7、4、7、3,那么它的基数就是4。索引的基数相对于数据表行数较高(也就是说,列中包含很多不同的值,重复的值很少)的时候,它的工作效果最好。如果某数据列含有很多不同的年龄,索引会很快地分辨数据行。如果某个数据列用于记录性别(只有"M"和"F"两种值),那么索引的用处就不大。如果值出现的几率几乎相等,那么无论搜索哪个值都可能得到一半的数据行。在这些情况下,最好根本不要使用索引,因为查询优化器发现某个值出现在表的数据行中的百分比很高的时候,它一般会忽略索引,进行全表扫描。惯用的百分比界线是"30%"。

2.索引失效原因

  • 对索引列运算&#xff0c;运算包括&#xff08;&#43;、-、*、/、&#xff01;、<>、%、like&#39;%_&#39;&#xff08;%放在前面&#xff09;
  • 类型错误&#xff0c;如字段类型为varchar&#xff0c;where条件用number。
  • 对索引应用内部函数&#xff0c;这种情况下应该建立基于函数的索引 如select * from template t where ROUND(t.logicdb_id) &#61; 1 此时应该建ROUND(t.logicdb_id)为索引&#xff0c;mysql8.0开始支持函数索引&#xff0c;5.7可以通过虚拟列的方式来支持&#xff0c;之前只能新建一个ROUND(t.logicdb_id)列然后去维护
  • 如果条件有or&#xff0c;即使其中有条件带索引也不会使用&#xff08;这也是为什么建议少使用or的原因&#xff09;&#xff0c;如果想使用or&#xff0c;又想索引有效&#xff0c;只能将or条件中的每个列加上索引
  • 如果列类型是字符串&#xff0c;那一定要在条件中数据使用引号&#xff0c;否则不使用索引&#xff1b;
  • B-tree索引 is null不会走,is not null会走,位图索引 is null,is not null 都会走
  • 组合索引遵循最左原则

3.索引的建立

  • 最重要的肯定是根据业务经常查询的语句
  • 尽量选择区分度高的列作为索引&#xff0c;区分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示字段不重复的比率&#xff0c;比率越大我们扫描的记录数就越少
  • 如果业务中唯一特性最好建立唯一键&#xff0c;一方面可以保证数据的正确性&#xff0c;另一方面索引的效率能大大提高

二、EXPLIAN中有用的信息

1.基本用法

  • desc 或者 explain 加上你的sql
  • extended explain加上你的sql&#xff0c;然后通过show warnings可以查看实际执行的语句&#xff0c;这一点也是非常有用的&#xff0c;很多时候不同的写法经过sql分析之后实际执行的代码是一样的

2.提高性能的特性

  • 索引覆盖(covering index)&#xff1a;需要查询的数据在索引上都可以查到不需要回表 EXTRA列显示using index
  • ICP特性(Index Condition Pushdown)&#xff1a;本来index仅仅是data access的一种访问模式&#xff0c;存数引擎通过索引回表获取的数据会传递到MySQL server层进行where条件过滤,5.6版本开始当ICP打开时&#xff0c;如果部分where条件能使用索引的字段&#xff0c;MySQL server会把这部分下推到引擎层&#xff0c;可以利用index过滤的where条件在存储引擎层进行数据过滤。EXTRA显示using index condition。需要了解mysql的架构图分为server和存储引擎层
  • 索引合并(index merge)&#xff1a;对多个索引分别进行条件扫描&#xff0c;然后将它们各自的结果进行合并(intersect/union)。一般用OR会用到&#xff0c;如果是AND条件&#xff0c;考虑建立复合索引。EXPLAIN显示的索引类型会显示index_merge&#xff0c;EXTRA会显示具体的合并算法和用到的索引

3.extra字段

  • using filesort&#xff1a; 说明MySQL会对数据使用一个外部的索引排序&#xff0c;而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序” &#xff0c;其实不一定是文件排序&#xff0c;内部使用的是快排
  • using temporary&#xff1a; 使用了临时表保存中间结果&#xff0c;MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by
  • using index&#xff1a; 表示相应的SELECT操作中使用了覆盖索引&#xff08;Covering Index&#xff09;&#xff0c;避免访问了表的数据行&#xff0c;效率不错。
  • impossible where&#xff1a; WHERE子句的值总是false&#xff0c;不能用来获取任何元组
  • select tables optimized away&#xff1a; 在没有GROUP BY子句的情况下基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作&#xff0c; 不必等到执行阶段再进行计算&#xff0c;查询执行计划生成的阶段即完成优化
  • distinct&#xff1a; 优化distinct操作&#xff0c;在找到第一匹配的元祖后即停止找同样值的操作

using filesort,using temporary这两项出现时需要注意下&#xff0c;这两项是十分耗费性能的&#xff0c;在使用group by的时候&#xff0c;虽然没有使用order by&#xff0c;如果没有索引&#xff0c;是可能同时出现using filesort,using temporary的&#xff0c;因为group by就是先排序在分组&#xff0c;如果没有排序的需要&#xff0c;可以加上一个order by NULL来避免排序&#xff0c;这样using filesort就会去除&#xff0c;能提升一点性能。

4.type字段

  • system&#xff1a;表只有一行记录&#xff08;等于系统表&#xff09;&#xff0c;这是const类型的特例&#xff0c;平时不会出现
  • const&#xff1a;如果通过索引依次就找到了&#xff0c;const用于比较主键索引或者unique索引。 因为只能匹配一行数据&#xff0c;所以很快。如果将主键置于where列表中&#xff0c;MySQL就能将该查询转换为一个常量
  • eq_ref&#xff1a;唯一性索引扫描&#xff0c;对于每个索引键&#xff0c;表中只有一条记录与之匹配。常见于主键或唯一索引扫描
  • ref&#xff1a;非唯一性索引扫描&#xff0c;返回匹配某个单独值的所有行。本质上也是一种索引访问&#xff0c;它返回所有匹配 某个单独值的行&#xff0c;然而它可能会找到多个符合条件的行&#xff0c;所以它应该属于查找和扫描的混合体
  • range&#xff1a;只检索给定范围的行&#xff0c;使用一个索引来选择行。key列显示使用了哪个索引&#xff0c;一般就是在你的where语句中出现between、<、>、in等的查询&#xff0c;这种范围扫描索引比全表扫描要好&#xff0c;因为只需要开始于缩印的某一点&#xff0c;而结束于另一点&#xff0c;不用扫描全部索引
  • index&#xff1a;Full Index Scan &#xff0c;index与ALL的区别为index类型只遍历索引树&#xff0c;这通常比ALL快&#xff0c;因为索引文件通常比数据文件小。 &#xff08;也就是说虽然ALL和index都是读全表&#xff0c; 但index是从索引中读取的&#xff0c;而ALL是从硬盘读取的&#xff09;
  • all&#xff1a;Full Table Scan&#xff0c;遍历全表获得匹配的行

三、字段类型和编码

  1. mysql返回字符串长度&#xff1a;CHARACTER_LENGTH方法(CHAR_LENGTH一样的)返回的是字符数&#xff0c;LENGTH函数返回的是字节数&#xff0c;一个汉字三个字节
  2. varvhar等字段建立索引长度计算语句&#xff1a;select count(distinct left(test,5))/count(*) from table; 越趋近1越好
  3. mysql的utf8最大是3个字节不支持emoji表情符号&#xff0c;必须只用utf8mb4。需要在mysql配置文件中配置客户端字符集为utf8mb4。jdbc的连接串不支持配置characterEncoding&#61;utf8mb4&#xff0c;最好的办法是在连接池中指定初始化sql&#xff0c;例如&#xff1a;hikari连接池&#xff0c;其他连接池类似spring.datasource.hikari.connection-init-sql&#61;set names utf8mb4。否则需要每次执行sql前都先执行set names utf8mb4。
  4. msyql排序规则(一般使用_bin和_genera_ci)&#xff1a;
  • utf8_genera_ci不区分大小写&#xff0c;ci为case insensitive的缩写&#xff0c;即大小写不敏感&#xff0c;
  • utf8_general_cs区分大小写&#xff0c;cs为case sensitive的缩写&#xff0c;即大小写敏感&#xff0c;但是目前MySQL版本中已经不支持类似于***_genera_cs的排序规则&#xff0c;直接使用utf8_bin替代。
  • utf8_bin将字符串中的每一个字符用二进制数据存储&#xff0c;区分大小写。

那么&#xff0c;同样是区分大小写&#xff0c;utf8_general_cs和utf8_bin有什么区别&#xff1f; cs为case sensitive的缩写&#xff0c;即大小写敏感&#xff1b;bin的意思是二进制&#xff0c;也就是二进制编码比较。 utf8_general_cs排序规则下&#xff0c;即便是区分了大小写&#xff0c;但是某些西欧的字符和拉丁字符是不区分的&#xff0c;比如ä&#61;a&#xff0c;但是有时并不需要ä&#61;a&#xff0c;所以才有utf8_bin utf8_bin的特点在于使用字符的二进制的编码进行运算&#xff0c;任何不同的二进制编码都是不同的&#xff0c;因此在utf8_bin排序规则下&#xff1a;ä<>a

  • sql yog中初始连接指定编码类型使用连接配置的初始化命令

四、SQL语句总结

1.常用的但容易忘的&#xff1a;

  • 如果有主键或者唯一键冲突则不插入&#xff1a;insert ignore into
  • 如果有主键或者唯一键冲突则更新,注意这个会影响自增的增量&#xff1a;INSERT INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf") ON DUPLICATE KEY UPDATE room_remarks&#61;"234"
  • 如果有就用新的替代&#xff0c;values如果不包含自增列&#xff0c;自增列的值会变化&#xff1a; REPLACE INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf")
  • 备份表&#xff1a;CREATE TABLE user_info SELECT * FROM user_info
  • 复制表结构&#xff1a;CREATE TABLE user_v2 LIKE user
  • 从查询语句中导入&#xff1a;INSERT INTO user_v2 SELECT * FROM user或者INSERT INTO user_v2(id,num) SELECT id,num FROM user
  • 连表更新&#xff1a;UPDATE user a, room b SET a.num&#61;a.num&#43;1 WHERE a.room_id&#61;b.id
  • 连表删除&#xff1a;DELETE user FROM user,black WHERE user.id&#61;black.id

2.锁相关(作为了解&#xff0c;很少用)

  • 共享锁&#xff1a; select id from tb_test where id &#61; 1 lock in share mode;
  • 排它锁&#xff1a; select id from tb_test where id &#61; 1 for update

3.优化时用到&#xff1a;

  • 强制使用某个索引&#xff1a; select * from table force index(idx_user) limit 2;
  • 禁止使用某个索引&#xff1a; select * from table ignore index(idx_user) limit 2;
  • 禁用缓存(在测试时去除缓存的影响)&#xff1a; select SQL_NO_CACHE from table limit 2;

4.查看状态

  • 查看字符集 SHOW VARIABLES LIKE &#39;character_set%&#39;;
  • 查看排序规则 SHOW VARIABLES LIKE &#39;collation%&#39;;

SQL编写注意

  • where语句的解析顺序是从右到左&#xff0c;条件尽量放where不要放having
  • 采用延迟关联(deferred join)技术优化超多分页场景&#xff0c;比如limit 10000,10,延迟关联可以避免回表
  • distinct语句非常损耗性能&#xff0c;可以通过group by来优化
  • 连表尽量不要超过三个表

五、踩坑

  1. 如果有自增列&#xff0c;truncate语句会把自增列的基数重置为0&#xff0c;有些场景用自增列作为业务上的id需要十分重视
  2. 聚合函数会自动滤空&#xff0c;比如a列的类型是int且全部是NULL&#xff0c;则SUM(a)返回的是NULL而不是0
  3. mysql判断null相等不能用“a&#61;null”,这个结果永远为UnKnown&#xff0c;where和having中,UnKnown永远被视为false&#xff0c;check约束中&#xff0c;UnKnown就会视为true来处理。所以要用“a is null”处理

六、千万大表在线修改

mysql在表数据量很大的时候&#xff0c;如果修改表结构会导致锁表&#xff0c;业务请求被阻塞。mysql在5.6之后引入了在线更新&#xff0c;但是在某些情况下还是会锁表&#xff0c;所以一般都采用pt工具( Percona Toolkit) 如对表添加索引&#xff1a;

pt-online-schema-change --user&#61;&#39;root&#39; --host&#61;&#39;localhost&#39; --ask-pass --alter "add index idx_user_id(room_id,create_time)" D&#61;fission_show_room_v2,t&#61;room_favorite_info --execute

七、慢查询日志

有时候如果线上请求超时&#xff0c;应该去关注下慢查询日志&#xff0c;慢查询的分析很简单&#xff0c;先找到慢查询日志文件的位置&#xff0c;然后利用mysqldumpslow去分析。查询慢查询日志信息可以直接通过执行sql命令查看相关变量&#xff0c;常用的sql如下&#xff1a;

-- 查看慢查询配置
-- slow_query_log 慢查询日志是否开启
-- slow_query_log_file 的值是记录的慢查询日志到文件中
-- long_query_time 指定了慢查询的阈值
-- log_queries_not_using_indexes 是否记录所有没有利用索引的查询
SHOW VARIABLES LIKE &#39;%quer%&#39;;-- 查看慢查询是日志还是表的形式
SHOW VARIABLES LIKE &#39;log_output&#39;-- 查看慢查询的数量
SHOW GLOBAL STATUS LIKE &#39;slow_queries&#39;;

八、查看sql进程和杀死进程

如果你执行了一个sql的操作&#xff0c;但是迟迟没有返回&#xff0c;你可以通过查询进程列表看看他的实际执行状况&#xff0c;如果该sql十分耗时&#xff0c;为了避免影响线上可以用kill命令杀死进程&#xff0c;通过查看进程列表也能直观的看下当前sql的执行状态&#xff0c;如果当前数据库负载很高&#xff0c;在进程列表可能会出现&#xff0c;大量的进程夯住&#xff0c;执行时间很长。命令如下&#xff1a;

--查看进程列表
SHOW PROCESSLIST;
--杀死某个进程
kill 183665

如果你使用的sqlyog&#xff0c;那么也有图形化的页面&#xff0c;在菜单栏-工具-显示-进程列表。在进程列表页面可以右键杀死进程。如下所示&#xff1a;

九、一些数据库性能的思考

在对公司慢查询日志做优化的时候&#xff0c;很多时候可能是忘了建索引&#xff0c;像这种问题很容易解决&#xff0c;加个索引就行了。但是有两种情况就不是简单能加索引能解决了&#xff1a;

  1. 业务代码循环读数据库&#xff1a; 考虑这样一个场景&#xff0c;获取用户粉丝列表信息 加入分页是十个 其实像这样的sql是十分简单的&#xff0c;通过连表查询性能也很高&#xff0c;但是有时候&#xff0c;很多开发采用了取出一串id&#xff0c;然后循环读每个id的信息&#xff0c;这样如果id很多对数据库的压力是很大的&#xff0c;而且性能也很低
  2. 统计sql&#xff1a;很多时候&#xff0c;业务上都会有排行榜这种&#xff0c;发现公司有很多地方直接采用数据库做计算&#xff0c;在对一些大表的做聚合运算的时候&#xff0c;经常超过五秒&#xff0c;这些sql一般很长而且很难优化&#xff0c; 像这种场景&#xff0c;如果业务允许&#xff08;比如一致性要求不高或者是隔一段时间才统计的&#xff09;&#xff0c;可以专门在从库里面做统计。另外我建议还是采用redis缓存来处理这种业务
  3. 超大分页: 在慢查询日志中发现了一些超大分页的慢查询如limit 40000,1000&#xff0c;因为mysql的分页是在server层做的&#xff0c;可以采用延迟关联在减少回表。但是看了相关的业务代码正常的业务逻辑是不会出现这样的请求的&#xff0c;所以很有可能是有恶意用户在刷接口&#xff0c;所以最好在开发的时候也对接口加上校验拦截这些恶意请求。 这篇文章就总结到这里&#xff0c;希望能够对你有所帮助&#xff01;

不止MySQL&#xff0c;想成为架构师的话可以参加下面这个专题&#xff0c;这个换题详细的解释了Linux、Netty、并发编程、JVM、Tomcat等技术&#xff01;

同时小编这边也给大家准备了一些针对以上专题的学习资料和视频&#xff0c;希望能够帮助到大家&#xff01;

资料领取方式&#xff1a;加入粉丝群963944895&#xff0c;【点击加入群聊】私信管理员即可


转:https://juejin.im/post/5c91e8f75188252da759cccb



推荐阅读
  • 本打算教一步步实现koa-router,因为要解释的太多了,所以先简化成mini版本,从实现部分功能到阅读源码,希望能让你好理解一些。希望你之前有读过koa源码,没有的话,给你链接 ... [详细]
  • 我的读书清单(持续更新)201705311.《一千零一夜》2006(四五年级)2.《中华上下五千年》2008(初一)3.《鲁滨孙漂流记》2008(初二)4.《钢铁是怎样炼成的》20 ... [详细]
  • MySQL InnoDB 存储引擎索引机制详解
    本文深入探讨了MySQL InnoDB存储引擎中的索引技术,包括索引的基本概念、数据结构与算法、B+树的特性及其在数据库中的应用,以及索引优化策略。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • 本文总结了一次针对大厂Java研发岗位的面试经历,探讨了面试中常见的问题及其背后的原因,并分享了一些实用的面试准备资料。 ... [详细]
  • 本文将深入探讨C语言代码的可重用性,解释其重要性和实现方法。通过具体示例,我们将展示如何通过封装和模块化设计提高代码的可重用性。 ... [详细]
  • 本文介绍了在Java中遍历HashMap的三种常见方法:使用entrySet()、keySet()以及Java 8引入的forEach。每种方法都有其特点和适用场景。 ... [详细]
  • 本文介绍了 JSON Schema 和 XML Schema 的基本概念,并详细讲解了如何使用 AJV 进行 JSON 数据校验。通过具体的示例和扩展方法,帮助读者更好地理解和应用这些工具。 ... [详细]
  • 本文详细介绍了 com.apollographql.apollo.api.internal.Optional 类中的 orNull() 方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
  • AI炼金术:KNN分类器的构建与应用
    本文介绍了如何使用Python及其相关库(如NumPy、scikit-learn和matplotlib)构建KNN分类器模型。通过详细的数据准备、模型训练及新样本预测的过程,展示KNN算法的实际操作步骤。 ... [详细]
  • 解决JavaScript中法语字符排序问题
    在开发一个使用JavaScript、HTML和CSS的Web应用时,遇到从SQLite数据库中提取的法语词汇排序不正确的问题,特别是带重音符号的字母未按预期排序。 ... [详细]
  • 双指针法在链表问题中应用广泛,能够高效解决多种经典问题,如合并两个有序链表、合并多个有序链表、查找倒数第k个节点等。本文将详细介绍这些应用场景及其解决方案。 ... [详细]
  • 如何将Python与Excel高效结合:常用操作技巧解析
    本文深入探讨了如何将Python与Excel高效结合,涵盖了一系列实用的操作技巧。文章内容详尽,步骤清晰,注重细节处理,旨在帮助读者掌握Python与Excel之间的无缝对接方法,提升数据处理效率。 ... [详细]
  • 在2022年11月2日的AcWing每日编程挑战中,任务是计算一个长度为n的整数序列中的逆序对数量。逆序对是指在序列中,若存在两个下标i和j(i < j),且a[i] > a[j],则称这两个元素构成一个逆序对。本题要求实现一个算法来高效地统计这些逆序对的数量。 ... [详细]
author-avatar
细妹很快乐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有