MySQL之高性能的索引策略
来源:SegmentFault
时间:2023-02-24 20:05:10 327浏览 收藏
来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习数据库相关编程知识。下面本篇文章就来带大家聊聊《MySQL之高性能的索引策略》,介绍一下MySQL,希望对大家的知识积累有所帮助,助力实战开发!
一、索引列必须单独
让我们先看看下面的这段MySQL代码:
mysql> SELECT id FROM user WHERE id + 1 = 2;
我们会发现,在上面这段SQL查询语句中,我们给的条件是一个有变量的表达式,如果我们此时的id列上是存在索引的,那上面的语句能不能使用到索引呢?
答案是不能的,因为MySQL无法自动的解析 id + 1 = 2 这个条件语句,尽管我们可以一眼的看出来此时等价于 id = 1,但是这种做法是无法使用到索引的,因此我们在查询的时候,应该使得索引列不能是表达式的一部分,也不能是函数的参数。
二、前缀索引和索引选择性
如果我们需要在某一列,例如存放url的一列数据上添加索引来加快查询的速度,我们先看看url数据的特点,长,类似的还有TEXT类型的数据等,这些都是一些很长,占据很大空间的数据,而且会使得对应的索引大且慢。这时候我们可以使用一些优化的索引策略,例如前缀索引。前缀索引与一般的索引不同,他在查询的时候并不会比对该列数据的所有值,而只是比对它的前面的一部分数据。这样会使得索引变得更加灵活有效率,但是却降低了索引选择性。
什么是索引选择性呢?我们给定一个公示:
索引选择性 = 不同的索引值 / 数据表的记录总数
首先思考,为什么会有所谓的不同索引值和相同的索引值?这都要建立在我们是使用前缀索引这种方式建立索引的基础上。例如有两个数据,“abcalkjsdhgasdfasdf”和“abcalaasdasdqwe”。很显然这两个数据是截然不同的,但是如果我们规定前缀索引的长度是数据的前五个字符,那么会发现这两个数据的索引值都是“abcal”,即这两个数据的索引值是一样的。因此也就降低了索引选择性。简单来说,索引选择性越高,我们通过索引值能查找到唯一的数据的可能性就越大,索引选择性越低,我们通过索引值能查找到的唯一的数据的可能性就越小。那么这是否就意味着前缀索引是一个很差的选择呢?并不,因为一般情况下使用恰当的前缀索引,也是可以准确的进行数据的查询,并且能够节省空间的,而且对于BLOB,TEXT或者很长的VARCHAR类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度。
三、多列索引
首先,如果一个数据表有3个列,那么我们为每一个列都单独的创建一个索引,是不是就能够使得在查询的时候,无论进行怎样的查找,我们都能获得最快的效率呢?进行下面的表格建立语句:
mysql> CREATE TABLE temp (c1 INTEGER,c2 INTEGER,KEY (c1),KEY (c2));
事实证明,在实际的操作中,这种为每一个列都建立一个索引的“单纯”的想法,对查找的效率提升非常的有限,与最佳的索引方案往往效率差距了几个量级。
在MySQL5.0之后的版本多出了“索引合并”的策略,一定程度上是帮程序员优化了这种在一个数据表上创建许多单列索引的操作,但是还是不建议使用这种索引策略。在MySQL5.0之前的版本,如果我们为表film_actor的字段film_id和actor_id分别创建一个单列索引,然后执行以下的查询操作:
mysql> SELECT film_id, actor_id FROM film_actor WHERE film_id = 1 OR actor_id = 1;
事实上,在这个查询语句中,我们所定义的两个单列索引都无法帮忙提高效率,甚至于在老版本的MySQL中,将会使用全表扫描来完成这个查询,这就使得这个索引策略变得完全没有意义。
除非将上面的语句改写成以下的形式:
mysql> SELECT actor_id, film_id FROM film_actor WHERE actor_id = 1 UNION ALL SELECT actor_id, film_id FROM film_actor WHERE film_id = 1
即将查询改写成两个查询的交集,即每个查询都只是用一个列作为判断的条件,拿着整MySQL就会很自然的运用这个列的索引。
因此我们还是建议在需要在多个列上建立索引的时候不要单独的给每个列建立一个索引,而是选择建立一个多列索引。
mysql> CREATE TABLE temp (c1 INTEGER,c2 INTEGER,KEY (c1, c2));
这个时候选择一个合适的索引列顺序就显得非常重要了,因为我们知道,如果我们使用默认的B-Tree建立一个多列索引的话,MySQL会按照我们创建时候指定的顺序建立索引,即先排c1,再在c1排列好的基础上排列c2。
而且查询的时候每次都是从左开始扫描,意味着如果你第一个查询的列并不是索引的最左列,那这个索引对于你来说就形同虚设。
我们看看如下的一个查询语句:
mysql> SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;
那我们是应该建立一个(staff_id, customer_id)的多列索引还是将他们的顺序颠倒过来呢?我们可以先看看这两个条件的数据量有多大:
sum(staff_id = 2) = 7992 sum(customer_id = 584) = 30
根据经验,我们应该将索引列customer_id 放到前面,因为对应条件值的数据量更小。因此如何选择索引列的顺序还是应该根据具体的情况来确定,没有唯一的准则。
四、聚簇索引(介绍)
聚簇索引其实只是一种特殊的B-Tree索引,他并不是一种区别于其他索引的单独的索引形式,而是一种存储方式。
当使用聚簇索引的时候,所有的数据都存储在索引树的叶子节点上。
下图展示了聚簇索引中的记录是如何存放的:
在创建聚簇索引时,InnoDB使用主键作为索引列聚集数据。如果数据表没有定义主键,则会选择一个唯一的非空索引代替,如果没有这样的索引InnoDB则会隐式定义一个主键来作为聚簇索引。
聚簇索引有以下的优点:
- 可以吧相关的数据保存在一起。例如实现电子邮箱时,可以根据用户的id来聚集数据,这样只需要从磁盘读取少量的数据页就能获取某个用户的全部邮件,如果没有使用聚簇索引,那么邮件的排列就是混乱的,有可能每封邮件都要导致一次磁盘IO。
- 数据访问更快。聚簇索引将索引和数据保存在同一个B-Tree中(普通索引只保存索引和指向数据行的指针,不保存数据),因此聚簇索引的查询会更快。
- 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。
同时,聚簇索引也有以下的缺陷:
- 聚簇索引最大限度地提高了IO密集型应用的性能,但如果数据全部存放在内存中,则访问的顺序就没有那么重要了,聚簇索引就没有什么优势了。
- 插入速度严重依赖插入顺序。
- 更新聚簇索引列的代价很高。
- 基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临“页分裂”的问题。当行的主键要求必须将这一行插入到某个已满的页面时,存储引擎会将该页分裂成两个页面容纳该行,页分裂将会倒是表占据更多的磁盘空间。
- 聚簇索引可能导致全表扫描变慢,特别是在行比较稀疏的时候,或者由于页分裂导致存储不连续的时候。
- 二级索引(非聚簇索引)可能比想象中的大没因为耳机索引的叶子节点包含了引用行的主键列。
- 二级索引访问需要两次索引查找,而不是一次。
由于聚簇索引的内容比较多,会专门出一篇来较为深入的将聚簇索引。
今天带大家了解了MySQL的相关知识,希望对你有所帮助;关于数据库的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
301 收藏
-
461 收藏
-
143 收藏
-
261 收藏
-
176 收藏
-
258 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习