mysql 聚簇索引和非聚簇索引_MySQL学习之——索引

转自:CSDN

MySQL是目前绝大多数互联网公司使用的关系型数据库,它性能出色、资源丰富、成本低廉,是快速搭建互联网应用的首选关系型数据库。但是,俗话说,“好马配好鞍”,仅仅会使用MySQL是不够的,对MySQL在不同场景下使用性能的最小化使用代价,是一个重要的课题。一般,在互联网公司的大部分业务中,读写的比例大约是10:1,也就是说,查询的场景往往比更新或写入的场景多得多,那么问题来了,如何优化查询呢?

前几天公司同事发了一条SQL语句,问这个SQL有没有走索引?对于一般的开发人员而言,优化SQL的方式是,在SQL中查询条件里的字段上,添加索引。但是如何添加索引?索引的顺序如何?索引是如何匹配命中的?一般的开发人员可能只知道大概,并没有很深入的了解。

MySQL索引原理

索引的目的与原理

在日常生活中,经常有这样的场景:有一个没见到过的英文单词,我们查字典找到这个单词的意思;我们要出去旅行,查询到具体地点的航班号;诸如此类。在这样的场景中,我们都是通过不断的缩小范围来筛选出最终预期的结果,同时把随机的事件变成顺序事件:查询字典,比如查单词mysql,我们是按照一个字母一个字母的顺序来查询的;查询航班号,我们也是通过地点机场航空公司一个一个来筛选缩小范围的。我们总是通过同一种查找方式来锁定数据。

数据库也是一样,但显然要比现实生活中的场景要复杂得多,因为不仅会有等值查询(=),还有范围查询(>,

磁盘I/O与预读

刚刚提到了磁盘访问,那么这里先简单介绍一下磁盘的I/O与预读。磁盘读取数据,靠的是机械运动,每次读取数据花费的时间可以分成:寻道时间、旋转延迟、传输时间三个部分。寻道时间指的是磁臂移动到指定磁盘所需要的时间,主流的磁盘一般在5ms以下;旋转延迟指的是我们经常说的磁盘转速,比如一个磁盘7200转,表示的就是每分钟磁盘能转7200次,转换成秒也就是120次每秒,旋转延迟就是1/120/2=4.17ms;传输时间指的是从磁盘读取出数据或将数据写入磁盘的时间,一般都在零点几毫秒,相对于前两个,可以忽略不计。那么访问一次磁盘的时间,即一次磁盘I/O的时间约等于5+4.17=9.17ms,9ms左右,听起来还是不错的哈,但要知道一台500-MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说,执行一次I/O的时间可以执行40万条指令,数据库动辄百万级甚至千万级的数据,每次9ms的时间,显然是一个灾难。

03b8babbafa6e88e22e0f27bf31de0ca.png

上图是计算机硬件延迟时间的对比图。

考虑到磁盘I/O是非常高昂代价的操作,计算机系统做了一些优化,当一次I/O时,不光会把当前磁盘地址的数据读取到内存中,而且会把相邻的数据也读取到内存缓冲区中,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快访问到。每一次I/O读取的数据我们称之为一页(Page)。具体一页的数据有多大,这个跟操作系统有关,一般为4K或8K,也就是我们读取一页数据的时候,实际上才发生了一次I/O,这个理论对于索引的数据结构设计很有帮助。

索引的数据结构

上面讲了索引的基本原理,数据库的复杂性,以及操作系统的一些内容,目的就是让大家了解到,任何一种数据结构都不是凭空产生的,一定有它的背景和使用场景。那么,我们需要这些数据结构能够做什么呢?其实很简单,就是:每次查找数据的时候,把磁盘I/O次数限制在一个很小的数量级,最好是一个常量数量级。那么我们就想到,如果一个高度可控的多路搜索树,是否能够满足需求呢?在这样的背景下,B+树应运而生。

18841fe3b0462c476a0fefcda27e8d54.png

详解B+树

如上图,是一棵B+树。B+树的定义,童鞋可以自行百度,我们只说一些重点。图中浅蓝色的块,我们称之为一个磁盘,可以看到,每个磁盘块包含几个数据项(深蓝色)和指针(黄色)。如:磁盘块1包含数据17和数据35,包含指针P1,P2,P3,P1指向数据小于17的磁盘块,P2指向数据在17到35之间的数据所在磁盘块,P3指向数据大于35的数据所在的磁盘块。真实数据存在于叶子节点,即3,5,9,10,13,15,28,29,36,60,75,79,90,99 。非叶子节点不存储真实数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

B+树的查找过程

还是使用上面的B+树。假设,我们要查找数据项29,那么我们首先会把磁盘块1由磁盘加载到内存中,此时进行了一次I/O,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存计算时间由于非常短(对比于I/O)可以忽略不计,通过磁盘块1的P2指针的磁盘地址指向磁盘块3,由磁盘加载到内存,此时进行了第二次I/O,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,此时进行了第三次I/O,同时内存中计算二分查找找到29,查询结束。这一过程,一共进行了3次I/O。在真实使用场景中,三层的B+树可以表示上百万的数据,如果上百万的数据查询只需要三次I/O,性能提高将会是巨大的。B+树就是一种索引数据结构,如果没有这样的索引,每个数据项发生一次I/O,那么成本将会大大提升。

B+树的性质

在上面的查找例子中,我们可以分析出一些B+树的性质:

  1. I/O的次数取决于B+树的高度H,假设当前数据表的数据为N,每个磁盘块的数据项的数量是M,则有:H=log(M+1)N,当数据量N一定的情况下,M越大,H越小;而M=磁盘块大小/数据项大小,磁盘块大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度也就越低。这也就是为什么每个数据项,即索引字段要尽量的小,比如int占4个字节,要比bigint的8个字节小一半。这也是为什么B+树要求把真实数据放在叶子节点内而不是内层节点内,一旦放到内层节点内,磁盘块的数据项会大幅度的下降,导致树层级的增高。当数据项为1时,B+树会退化成线性表。

  2. B+树的数据项是复合性数据结构,比如(name,age,gender)的时候,B+树是按照从左到右的顺序来建立搜索树的,比如当(小张,22,女)这样的数据来检索的时候,B+树会优先比较name来确定下一步的搜索方向,如果name相同再依次比较age和gender,最后得到检索的数据。但是,当(22,女)这样没有name的数据来的时候,B+树就不知道下一步该查哪个节点,因为建立搜索树的时候,name就是第一个比较因子,必须根据name来搜索才知道下一步去哪里查询。比如,当(小张,男)这样的数据来检索时,B+树就可以根据name来指定搜索方向,但下一字段age缺失,所以只能把名字是“小张”的所有数据都找到,然后再匹配性别是“男”的数据了。这个是非常重要的一条性质,即索引的最左匹配特性。

索引的类型

在MySQL中,索引分为两大类:聚簇索引和非聚簇索引。聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引则不同;聚簇索引能够提高多行检索的速度,而非聚簇索引则对单行的检索速度很快。

在这两大类的索引类型下,还可以将索引分成四个小类:

  1. 普通索引:最基本的索引,没有任何限制,是我们大多数情况下使用到的索引。

  2. 唯一索引:与普通索引类型,不同的是唯一索引的列值必须唯一,但允许为空值。

  3. 全文索引:全文索引(FULLTEXT)仅可以适用于MyISAM引擎的数据表;作用于CHAR、VARCHAR、TEXT数据类型的列。

  4. 组合索引:将几个列作为一条索引进行检索,使用最左匹配原则。

建立索引的原则

当我们了解完索引原理之后,对慢查询的优化应该有一些想法,这里我们先总结一下建立索引的一些原则:

  1. 最左前缀匹配原则。这是非常重要、非常重要、非常重要(重要的事情说三遍)的原则,MySQL会一直向右匹配直到遇到范围查询(>, 3 AND d = 4,如果建立 (a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引,则都可以用到,a,b,d的顺序可以任意调整。

  2. 等于(=)和in 可以乱序。比如,a = 1 AND b = 2 AND c = 3 建立(a,b,c)索引可以任意顺序,MySQL的查询优化器会帮你优化成索引可以识别的模式。

  3. 尽量选择区分度高的列作为索引,区分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示字段不重复的比率,比率越大我们扫描的记录数就越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度是0。可能有人会问,这个比率有什么经验么?使用场景不同,这个值也很难确定,一般需要JOIN的字段我们要求在0.1以上,即平均1条扫描10条记录。

  4. 索引列不能参与计算,尽量保持列“干净”。比如,FROM_UNIXTIME(create_time) = '2016-06-06' 就不能使用索引,原因很简单,B+树中存储的都是数据表中的字段值,但是进行检索时,需要把所有元素都应用函数才能比较,显然这样的代价太大。所以语句要写成 :create_time = UNIX_TIMESTAMP('2016-06-06')。

  5. 尽可能的扩展索引,不要新建立索引。比如表中已经有了a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

  6. 单个多列组合索引和多个单列索引的检索查询效果不同,因为在执行SQL时,MySQL只能使用一个索引,会从多个单列索引中选择一个限制最为严格的索引。

慢查询的优化步骤

  1. 查看运行效果,是否真的很慢,主要设置SQL_NO_CACHE。

  2. WHERE条件单表查询,锁定最小返回记录表。这句话的意思是,把查询语句的WHERE都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高

  3. EXPLAIN查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)

  4. ORDER BY LIMIT 形式的SQL语句,让排序的表优先查

  5. 多去了解业务的使用场景

  6. 加索引时,要参照建立索引的几大原则

  7. 观察结果,不符合预期,则重新从1开始分析

索引的优化方法

  1. 索引不会包含有NULL值的列:只要列中包含有NULL值,都将不会被包含在索引中,组合索引中只要有一列有NULL值,那么这一列对于此条组合索引就是无效的。所以我们在数据库设计时,不要让索引字段的默认值为NULL。

  2. 使用短索引:假设,如果有一个数据类型为CHAR(255)的列,在前10个或20个字符内,绝大部分数据的值是唯一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省I/O操作。

  3. 索引列排序:MySQL查询只使用一个索引,因此如果WHERE子句中已经使用了索引的话,那么ORDER BY中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下,不要使用排序操作;尽量不要包含多个列的排序,如果需要,最好给这些列也创建组合索引。

  4. LIKE语句操作:一般情况下,不建议使用LIKE操作;如果非使用不可,如何使用也是一个研究的课题。LIKE "%aaaaa%"不会使用索引,但是LIKE "aaa%"却可以使用索引。

  5. 不要在索引列上进行运算:在建立索引的原则中,提到了索引列不能进行运算,这里就不再赘述了。

END

3bcd96b9c9663bfc7bbd1d04deec5894.png91d1bce48fb10ec5f95aaa51cc7db092.png

文字 / echo

文中贴纸 / 网络(侵删)

配图 / 网络(侵删)

排版 / PHP学习手记

d5bfb6097a1264df39175e4ca9290039.png

PHP学习手记 

科技 | 前沿 |  技术 | 学习

微信号:phpStudy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/334457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java登录界面命令_Java命令行界面(第10部分):picocli

java登录界面命令picocli主页面将picocli描述为“强大的微小命令行界面”,“ picocli”是一个单文件Java框架,用于解析命令行参数并生成精美,易于定制的用法帮助消息。 有颜色。” 这篇文章简要概述了如何使用Picocli 0.9.7处理Java代码中的命…

navicat运行sql文件慢_SQL进阶之路——入门

一、初步了解SQL数据库:用来存放数据关系数据库:1.含有多张表 2.各表之间有关系关系数据库中表的样式:a.每个表1个表名 b.每个表中包含记录列名的列和记录数据的行 c.利用主键用来标识数据的唯一性关系数据库中每个表之间如何建立联系&#x…

java登录界面命令_Java命令行界面(第19部分):jClap

java登录界面命令本系列中第19个帖子的重点是从Java代码解析命令行参数是jClap ( Java命令行参数解析器 ),不应与JCLAP库混淆,该库是我本系列前 一篇文章的重点。 上 一篇 文章介绍了Giles Winstanley( snaq.net &…

weka分类器怎么设置样本类别_【程序喵笔记】小样本学习1.0

小样本学习前几天接触小样本学习 Few-Shot Learning,感觉很是有意思。看到Shusen Wang老师的讲解,感觉很棒~持续学习~学会学习 Lean to learn小朋友去动物园,见到未知的动物,他虽然不知道类别,但是给他看一些卡片&…

maven 生成本地库_在2017年从Maven工件生成P2存储库

maven 生成本地库几年前,我写了一篇博客文章,介绍如何基于Maven工件生成P2存储库。 如今,这种描述的方法已经过时了,我想展示一种基于p2-maven-plugin的新方法,该方法是为解决此问题而创建的。 Maven构建生命周期中的…

java 状态模式 同步_JAVA设计模式之状态模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述状态(State)模式的:状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式。状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改…

python列表修改_python修改列表

广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 由于惯性思维,导致使用for循环修改列表中的值出现问题首次尝试:def make_great(orig…

python装饰器作用和功能_Python装饰器原理与用法分析

这篇文章主要介绍了Python装饰器原理与用法,结合实例形式分析了Python装饰器的概念、原理、使用方法及相关操作注意事项,需要的朋友可以参考下 本文实例讲述了Python装饰器原理与用法。分享给大家供大家参考,具体如下: 1、装饰器的本质是函数&#xff0c…

java登录界面命令_Java命令行界面(第16部分):JArgp

java登录界面命令这篇文章中介绍的基于Java的命令行参数处理库是IBM developerWorks文章Java编程动态性,第3部分,应用的反射 (第2003 部分,此归档文章于2016年“归档”,但仍可通过PDF下载 )的特色库。 。 该…

tnsnames.ora配置未生效_1分钟了解网络交换机的6种命令配置模式

我们在配置交换机的时候首先要了解的就是交换机命令模式,小编用Cisco思科交换机为例带大家了解交换机的6种配置模式。Cisco IOS提供了用户EXEC模式和特权EXEC模式两种基本的命令执行级别,同时还提供了全局配置、接口配置、Line配置和vlan数据库配置等多种…

java 线程中创建线程_如何在Java 8中创建线程安全的ConcurrentHashSet?

java 线程中创建线程在JDK 8之前,还没有办法在Java中创建大型的线程安全的ConcurrentHashSet。 java.util.concurrent包甚至没有一个名为ConcurrentHashSet的类,但是从JDK 8开始,您可以使用新添加的keySet(默认值)和ne…

docker 删除所有镜像_关于 Docker 镜像的操作,看完这篇就够啦 !(下)| 文末福利...

紧接着上篇《关于 Docker 镜像的操作,看完这篇就够啦 !(上)》,奉上下篇 !!!镜像作为 Docker 三大核心概念中最重要的一个关键词,它有很多操作,是您想学习容器技术不得不掌握的。本文将带您一步一…

python与access选哪个_从Python连接到Access

I want to be connected to a database Boreas (Access) from Python. How to be connected from Python to Access database Northwind? 解决方案 Here are 2 ways, with COM dispatch and with odbc. You will need the pywin32 extensions and/or pyodbc to use these meth…

设备唯一标识/设备码/设备标识码

文章目录一、MAC地址二、IMEI三、MEIDMEID 和 IMEI 用途的区别四、序列号(一)苹果手机序列号(二)华为手机序列号一、MAC地址 MAC地址(英语:Media Access Control Address),直译为媒…

java登录界面命令_Java命令行界面(第18部分):JCLAP

java登录界面命令Giles Winstanley的JCLAP ( Java命令行参数解析器 )是基于Java的命令行处理库的系列文章中介绍的第18个库。 这篇文章的示例基于JCLAP 1.4 ,它需要Java 8 。 JCLAP主页上指出:“ JCLAP帮助Java开发人员为其应用程…

java登录界面命令_Java命令行界面(第15部分):Jargo

java登录界面命令Jargo在其GitHub主页上定义为“一种减轻程序参数/选项处理的工具”。 当已经存在许多其他命令行处理库时,该页面为另一个命令行处理库提供了基本原理 ,该列表的顶部是“因为类型安全性,不变性和可读性很重要”。 Jargo的选项…

没学过编程能学python吗_我没学过编程,能否学会Python?

学习Python编程语言,是大家走入编程世界的最理想选择。那么我没学过编程,能否学会Python?上海Python培训肯定的回答您:of course!完全能学会!设计大师说过,留白是很好的风景,适当的留白是设计的至高境界;对于没有编程…

java登录界面命令_Java命令行界面(第13部分):JArgs

java登录界面命令JArgs 1.0的区别在于,这是我的第13篇文章的主题,该文章是关于Java命令行参数解析的。 JArgs是一个开放源代码( BSD许可 )库,主要由Steve Purcell和Ewan Mellor等 不同贡献者支持。 事实证明&#xff0…

java登录界面命令_Java命令行界面(第11部分):CmdLn

java登录界面命令这篇文章介绍了如何使用Ostermiller Java Utilities 1.08.02的CmdLn(Java命令行解析器)来处理基于Java的应用程序中的命令行参数。 Ostermiller Java实用程序包括几种不同类型的实用程序 ,但本文的重点是组件页面上描述的“命…

socket模拟http的登陆_Python网络爬虫之模拟登陆 !

为什么要模拟登陆?Python网络爬虫应用十分广泛,但是有些网页需要用户登陆后才能获取到信息,所以我们的爬虫需要模拟用户的登陆行为,在登陆以后保存登陆信息,以便浏览该页面下的其他页面。保存用户信息模拟登陆后有两种…