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

mysql 索引使用及优化详情【mysql特性】

这篇文章主要介绍了mysql 索引使用及优化详情,合理使用索引,能大大提升sql查询的性能,可以这么讲,随着业务数据量的不断增长,优化系统的响应速度,很大程度上可以说就

前言

索引对有一定开发经验的同学来说并不陌生,合理使用索引,能大大提升sql查询的性能,可以这么讲,随着业务数据量的不断增长,优化系统的响应速度,很大程度上可以说就是集中在索引的优化上;

mysql索引原理

在正式了解与学习mysql索引之前,先对mysql的索引原理再次回顾下;

我们知道,目前大多数使用的mysql引擎为 innodb,而innodb引擎使用的是 B+ Tree,下面通过几张图快速了解下 B+ Tree的结构,

假如存在下面这张表:

那么通过 B+ Tree构建出来的 “ 以ID为主键索引的树形结构如下:

说明:

  • 叶子节点存放的是ID对应的一条完整的记录;
  • 查找一条记录时,如果是按照ID搜索,则会采用类似二叉树的方式,最终定位到叶子节点的ID对应的那条记录;

也就是说,如果查询的时候,以ID为参数,则效率是最高的,反之,如果以非主键字段,建立索引,则B+Tree的索引结构将会如下,

说明:

  • 主键索引构建出来的 B+Tree 结构保持不变;
  • 再以主键ID之外的字段,构建出一个B+Tree结构,其叶子节点存储的是字段的值以及对应的主键值;

以上关于mysql 的inndb的索引结构原理先介绍到这儿,后文中将会用到里面的东西,还请留意;

mysql索引分类

MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等;

  • 从 功能逻辑 上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引;
  • 按照 物理实现方式 ,可以分为 2 种:聚簇索引和非聚簇索引;
  • 按照 作用字段个数 进行划分,分成单列索引和联合索引;、

常规分类

  • 主键索引,针对表的主键创建的索引,默认建表的时候,自动创建,有且只能有一个;
  • 唯一索引,为了避免一个表中的某列数据出现重复的值,可以有多个,关键字:UNIQUE;
  • 常规索引,用于快速定位特定字段的数据,可以有多个;全文索引,
  • 全文索引常用于查找文本中的关键词,而不是比较索引中的值,可以有多个,关键字FULLTEXT;

补充说明

不同的存储引擎支持的索引类型也不一样

  • InnoDB :支持 B-tree、Full-text 等索引,不支持 Hash索引;
  • MyISAM : 支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  • Memory :支持 B-tree、Hash 等索引,不支持 Full-text 索引;
  • NDB :支持 Hash 索引,不支持 B-tree、Full-text 等索引;
  • Archive :不支持 B-tree、Hash、Full-text 等索引;

索引创建语法

数据准备,有如下建表sql

CREATE TABLE `user` (
	`user_id` VARCHAR (32) NOT NULL COMMENT "用户ID",
	`user_name` VARCHAR (64) DEFAULT NULL COMMENT "用户姓名",
	`passwd` VARCHAR (64) NOT NULL COMMENT "密码",
	`email` VARCHAR (64) DEFAULT NULL COMMENT "邮箱",
	`mobile` VARCHAR (32) DEFAULT NULL COMMENT "手机号",
	`address` VARCHAR (128) DEFAULT NULL COMMENT "地址",
	`ID` VARCHAR (18) DEFAULT NULL COMMENT "身份证号",
	`sex` INT (11) DEFAULT NULL COMMENT "用户性别 1:男 2:女",
	PRIMARY KEY (`user_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;

1、创建索引

CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name ON table_name (col_name[length],...) [ASC | DESC]
  • UNIQUE 、 FULLTEXT 和 SPATIAL 为可选参数,分别表示唯一索引、全文索引和空间索引;
  • index_name 指定索引的名称,为可选参数,如果不指定,那么MySQL默认col_name为索引名;
  • col_name 为需要创建索引的字段列,该列必须从数据表中定义的多个列中选择;
  • length 为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度;
  • ASC 或 DESC 指定升序或者降序的索引值存储;

比如给上表的user_name创建一个唯一索引

CREATE UNIQUE INDEX user_name_idx on `user`(user_name); 

2、查看索引

SHOW INDEX FROM table_name;

3、删除索引

DROP INDEX index_name ON table_name;

删除上面的表创建的唯一索引:

4、为 username和password创建联合索引

create index name_passwd_index on `user`(user_name, passwd);

5、给user表添加一个info的字段,并为这个字段添加全文索引

ALTER  TABLE  `user`  ADD  FULLTEXT ( `info` );

全文索引用match+against方式查询:

SELECT * FROM `user` WHERE MATCH(字段名称) AGAINST (‘查询字符串");

注意点:

使用全文索引前,搞清楚版本支持情况,不同的版本可能对全文索引支持不一样;全文索引比 like + % 快 N 倍,但是可能存在精度问题;如果需要全文索引的是大量数据,建议先添加数据,再创建索引;

已经存在的表创建、删除索引等

现实中,如果之前已经给表创建过相关的索引,现在需要修改或删除,或重新创建,该如何做呢?

1、使用ALTER TABLE语句创建索引

ALTER TABLE table_name ADD [UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY] [index_name] (col_name[length],…) [ASC | DESC]

比如给 user表的mobile字段添加索引

ALTER TABLE `user` ADD INDEX `idx_mobile` (`mobile`);

2、使用ALTER TABLE语句删除索引

ALTER TABLE table_name DROP INDEX index_name;

或者直接使用drop语句

DROP INDEX index_name ON table_name;

说明

删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除。如果组成索引的所有列都被删除,则整个索引将被删除;

常用的索引设计原则

在实际开发中,索引并不是越多越好,创建索引需结合业务情况进行综合考量,下面结合实际经验列举出一些常用的索引设计原则,作为创建索引时的参考;

1、字段值在业务中具备唯一性

业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引,比如用户表中,可以通过身份证号或微信号作为 唯一索引

2、频繁作为 WHERE 查询条件的字段

某字段在SELECT语句WHERE 条件中频繁使用,就需要给这个字段创建索引。尤其是数据量大时,创建索引就可以大幅提升查询的效率;比如用户表的 user_id

3、UPDATE、DELETE 的 WHERE 条件列

我们知道,如果更新的这个列创建了索引,在真正执行数据更新的时候,不仅要更新数据,还要更新这个列的索引信息,在这种情况下,建议只对查询的那个字段创建索引(非索引字段更新的时候速度更快);

4、经常 GROUP BY 和 ORDER BY 的列

索引是让数据按某种顺序进行存储或检索,因此使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据排序的时候,就需要 对分组或者排序的字段进行索引 。如果待排序的列有多个,那么可以在这些列上建立 组合索引 ,以提升速度;

5、对DISTINCT 字段创建索引

有时候需要使用DISTINCT对某字段进行去重,那么对这个字段创建索引,也会提升查询效率。这是因为索引会对数据按照某种顺序进行排序,所以有了索引在去重的时候会快很多。

6、使用列的类型小的创建索引

7、使用字符串前缀创建索引

举个例子来说,在user表中存在一个邮箱eamil字段,通常来说,对于一个稳定的业务系统,user标准的email格式可以说格式上是固定的,比如 数字@qq.com,名称@163.com等;

我们知道索引也是要占用存储空间的,字段的长度越长,创建的索引最终占用的空间也越大,当表的数据量大到一定程度,查询时就算是检索走索引也会花费较长时间,这时候就可以考虑使用字符串前缀创建索引了;

可以使用下面的语句创建字符串前缀创建索引:

alter table useradd index(email(6));

这里的问题是,截取多少呢?截取多了,达不到节省索引存储空间的目的;截取少了,重复内容太多,字段的散列度(选择性)会降低。怎么计算不同的长度的选择性呢?下面给出一个经验公式作为参考,

先看一下字段在全部数据中的选择度:

select count(distinct email) / count(*) from user;

通过不同长度去计算,与全表的选择性对比:

count(distinct left(列名, 索引长度))/count(*)

8、使用最频繁的列放到联合索引的左侧

这样可以尽可能的向查询时的最佳左前缀原则靠拢;

9、在多字段都要创建索引的情况下,联合索引优于单值索引

试想当user表中,当user_name,mobile都需要创建索引时,分别创建两个单列索引带来的开销,与创建一个联合索引带来的开销哪个更大呢?

10、 区分度高(散列性高)的列适合作为索引

如果表中存在性别这样的字段,就不太适合创建索引,这个需要注意;

11、多表 JOIN 时,创建索引注意事项

  • 连接表数量尽量不要超过 3 张 ,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率;
  • 尽可能对 WHERE 后面的条件字段创建索引 ,因为 WHERE 才是对数据条件的过滤;
  • 对于连接字段创建索引 ,并且该字段在多张表中的类型必须一致 ,字段类型不一致将会带来较大的查询性能损耗;

12、有大量重复数据的列上不要建立索引

在这样的列上一旦创建了索引,比如表中有50万数据,你需要先访问 50 万次索引,然后再访问 50 万次数据表,这样加起来的开销比不使用索引可能还要大;

一个经验值

当数据重复度大,比如高于 10% 的时候,就不需要对这个字段创建索引;

13、避免对经常更新的表创建过多索引

这个算是常识性的经验了,更新数据时候,不经要更新数据本身,还需要更新索引;

14、不建议用无序的值作为索引

例如身份证、UUID(在索引比较时需要转为ASCII,并且插入时可能造成页分裂)、MD5、HASH、无序长字符串等;

15、不要定义冗余或重复的索引

即对同一个字段而言,不要创建多个不同名称的索引,这样只会增加维护的成本,并不会对搜索有什么好处;

16、及时删除不再使用或者很少使用的索引

减少索引存储空间对整个表空间的开销

索引失效情况总结

1、不要在索引列上进行操作计算

计算、函数、类型转换(自动或手动)导致索引失效,上面的user表中,我们给phone创建了索引,但如果使用下面的函数进行查询,索引将会失效;

explain select * from `user` where SUBSTRING(mobile,10,2) = "12"

2、字符串查询时一定要加引号

字符串查询不加引号时存在隐式转换,将会使索引失效

3、模糊匹配

如果仅仅是尾部的模糊匹配,仍然会走索引(即后like);但如果是头部匹配,索引将会失效;

like在前

like在后

前后都有like

很多同学在实际开发中,习惯性的就写上前后都带有 like的语句,而在表数据量越来越大的情况下,效率将会非常低;

4、使用 or 连接的条件

用or分开的字段作为条件查询时,如果or前面的列有索引,但是后面的列没有索引,那么整条查询将不会使用到索引;

explain select * from `user` where mobile = "13366767812"  or email = "16678623@qq.com" 

usr表中,email由于没有建索引,所以or 的查询结果中,最终没有用到索引;

5、范围条件右边的列索引失效

对于某些联合索引,如果用到了范围查询但是查询条件的字段未按照联合索引的字段顺序,将会使得索引失效;

如下,user_name 和age创建了联合索引,使用下面这条sql分析可知

explain select * from `user` where age > 25 and user_name = "张小华"

在这种情况下,需要将范围查询条件放置语句最后;

6、不等于(!= 或者<>)索引失效

尽量避免在大数据量的查询中使用 != 这种查询

7、 is null可以使用索引,is not null无法使用索引

CREATE index idx_mobile on `user`(mobile)

分别使用下面的sql进行分析

8、使用 not in和not exists 这样的关键字导致索引失效

9、不合理的使用order by导致索引失效

其实,这个也很好理解,毕竟需要对全表数据进行排序处理,在网络上看到有说如果order by条件满足最左匹配则会正常走索引, 在当前8.0.18版本中并未出现。所以,在基于order by和limit进行使用时,要特别留意。是否走索引不仅涉及到数据库版本,还要看Mysql优化器是如何处理的。

但是使用主键进行排序,则是可以走索引的,这一点需要注意

10、其他情况

当然,还有其他一些是否走索引的规则,这与索引的类型是B-tree索引还是位图索引也有关系;

这里要说的其他,可以总结为:

Mysql优化器的其他优化策略,比如优化器认为在某些情况下,全表扫描比走索引快,则它就会放弃索引;针对这种情况,一般不用过多理会,当发现问题时再定点排查即可

尽量使用覆盖索引

最后,补充一个知识点,即使用覆盖索引

即查询要返回的结果字段中,尽可能的在索引中使用到(或全部用到),减少使用 "select * " 这样的查询;

比如下面这条语句

select address,age from `user` where user_id = "1" 

尽管使用到了主键查询,但是查询的字段 address 和 age并未包含在索引列中,所以这种情况下,innodb引擎最终需要通过回表的方式才能将数据的结果查出来;

还记得本文开头的这张图吧

当我们查询一条记录时,使用的条件为 name = ‘名字’,尽管对name创建了索引,但是name的索引结构中,其叶子节点存储的是name值以及id值,假如这时,你的查询语句中需要返回的字段不是id和name,而是address和mobile,这时,就需要拿到id再次去查询,一直查到id的索引树下的完整的记录,这就是所谓的回表;

当表的数据量足够大的时候,回表耗费的时间是很长的,这个对整体的查询响应来说一定会成为一个需要优化的点;

到此这篇关于mysql 索引使用及优化详情的文章就介绍到这了,更多相关mysql 索引优化内容请搜索编程笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程笔记!


推荐阅读
  • MySQL索引详解及其优化策略
    本文详细解析了MySQL索引的概念、数据结构及管理方法,并探讨了如何正确使用索引以提升查询性能。文章还深入讲解了联合索引与覆盖索引的应用场景,以及它们在优化数据库性能中的重要作用。此外,通过实例分析,进一步阐述了索引在高读写比系统中的必要性和优势。 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 七款高效编辑器与笔记工具推荐:KindEditor自动换行功能解析
    本文推荐了七款高效的编辑器与笔记工具,并详细解析了KindEditor的自动换行功能。其中,轻笔记QingBiJi是一款完全免费的记事本软件,用户可以通过其简洁的界面和强大的功能轻松记录和管理日常事务。此外,该软件还支持多平台同步,确保用户在不同设备间无缝切换。 ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 数据库多表联合查询:内连接与外连接详解
    在数据库的多表查询中,内连接和外连接是两种常用的技术手段。内连接用于检索多个表中相互匹配的记录,即只有当两个表中的记录满足特定的连接条件时,这些记录才会被包含在查询结果中。相比之下,外连接则不仅返回匹配的记录,还可以选择性地返回不匹配的记录,具体取决于左外连接、右外连接或全外连接的选择。本文将详细解析这两种连接方式的使用场景及其语法结构,帮助读者更好地理解和应用多表查询技术。 ... [详细]
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • Netty框架中运用Protobuf实现高效通信协议
    在Netty框架中,通过引入Protobuf来实现高效的通信协议。为了使用Protobuf,需要先准备好环境,包括下载并安装Protobuf的代码生成器`protoc`以及相应的源码包。具体资源可从官方下载页面获取,确保版本兼容性以充分发挥其性能优势。此外,配置好开发环境后,可以通过定义`.proto`文件来自动生成Java类,从而简化数据序列化和反序列化的操作,提高通信效率。 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
  • 深入探讨:Java 8 中 HashMap 链表为何选择红黑树而非 AVL 树
    深入探讨:Java 8 中 HashMap 链表为何选择红黑树而非 AVL 树 ... [详细]
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
author-avatar
圣峰冰寒_869
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有