当前位置: 首页 > news >正文

07MySQL存储引擎与索引优化

目录

MYSQL体系结构

存储引擎

常用的几种存储引擎

1.InnoDB引擎

2.MyISAM引擎

3.Memory引擎

存储引擎选择

索引

索引结构

B-Tree(多路平衡查找树)

B+Tree

hash

为什么 InnoDB存储引擎选择使用B+ tree索引结构?

索引分类

索引语法

SQL性能分析

查看执行频次

慢查询日志

Show profiles

explain执行计划

索引使用规则

最左前缀法则

范围查询

索引失效

SQL提示

覆盖索引&回表查询

什么是回表查询

思考

前缀索引

单列索引和联合索引

索引设计原则


MYSQL体系结构

mysql体系结构从上到下分为连接层,服务层,引擎层,存储层,如下图:

连接层:最上层是一些客户端和链接服务,主要完成一些连接处理,授权认证,及相关的安全方案,服务器也会为安全接入的每个客户端验证它所具备的操作权限。

服务层:主要完成大部分核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行,所有跨存储引擎的功能也在这一层实现,如过程,函数等。

引擎层:真正的负责了mysql中数据的存储和获取,服务器通过API和存储引擎进行通信,不同的存储引擎具有不同的功能,这样我么可以根据自己的需求,来选择合适的存储引擎。

数据层:主要将数据存储在文件系统上,并完成与存储引擎的交互。

存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。

如何查看一张表的引擎:

show  create  table  表名;

查看engine=???

建表默认是InnoDB引擎,可以指定引擎

查看当前数据库支持存储引擎:

show engines;

常用的几种存储引擎

1.InnoDB引擎

InnoDB是一种兼顾高可靠性和靠性能的通用存储引擎,在mysql 5.5版本之后,InnoDB是默认的存储引擎。

特点:DML操作遵循ACID模型,支持事务;支持行级锁,提高并发性能;支持外键foreign key约束,保证数据的完整性。

文件:InnoDB引擎的每张表都会对应一个以ibd结尾的表空间文件,该文件中存储了表的结构,数据和索引,可用innodb_file_per_table参数来控制。

逻辑存储结构如下图:

2.MyISAM引擎

MyISAM是mysql早期默认的存储引擎。

特点:不支持事务;不支持外键;支持表锁,不支持行锁;访问速度快。

文件:sdi文件存储表结构信息,MYD文件存储数据,MYI文件存储引擎。

3.Memory引擎

Memory引擎的表数据存储在内存中,由于受到硬件问题,或断点问题的影响,只能将这些表作为临时表或缓存作用。

特点:内存存放;支持hash索引(默认);

文件:sdi文件存储表结构信息;

存储引擎选择

以上三种的存储引擎特点如下图:

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

 InnoDB:是Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。

 MySAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。

 MEMORY:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。

总结:

  1. InnoDB 适合需事务保障、高并发写或依赖外键的场景(如电商订单、金融转账),是多数核心业务的首选;
  2. MyISAM 适合读多写少、无事务需求的静态场景(如官网文章、日志统计),优势是读性能优;
  3. MEMORY 适合临时缓存、高频临时查询场景(如秒杀临时库存、会话数据),速度快但数据不持久,仅存临时数据。

索引

索引是帮助mysql高效获取数据的数据结构,这样可以在这些数据结构上实现高级查询算法,例子如下图:

备注:上述二次树索引结构的只是一个示意图,并不是真实的索引结构

优势:提高数据检索的效率,降低数据库的IO成本;通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

劣势:索引列也是要占用空间;索引大大提高了查询效率,但是却降低了更新效率,如对表进行insert,update,delete时,效率降低。

索引结构

mysql的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要有以下几种:

不同存储引擎支持的数据结构如下图:

B-Tree(多路平衡查找树)

以一颗最大度数(max-degree)为5(5阶)的b-tree为例(每个节点最多存储4个 key,5个指针):
--度:树的度数指的是一个节点的子节点个数。

B+Tree

B+tree是特殊的Btree,就是所有数据都存储在叶子节点,其他节点不存储数据,只存储检索用的索引值,并且在叶子节点存在单向链表,可以提高遍历速度,以4阶B+tree为例如下图:

hash

hash索引就是通过一定的hash计算,将键值换算成hash值,映射到对应的槽位,然后存储在hash表中。如果多个键值映射到同一个槽位上,就成为hash冲突,可以通过链表来解决,示例如下图:

--特点:hash索引只持支对等比较,不支持范围查询;无法利用该索引完成排序操作;查询效率高,通常只需要一次检索,效率通常高于B+tree索引。

--在mysql中,支持hash索引的是memory引擎,而InnoDB中具有自适应的hash功能,hash索引是存储引擎根据B+tree索引在指定条件下自动构建的。

为什么 InnoDB存储引擎选择使用B+ tree索引结构?

相对于二叉树,层级更少,搜索效率高;

页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;

相对Hash索引,B+tree支持范围匹配及排序操作;

索引分类

按照约束分类,mysql的索引通常分为以下四类:

--InnoDB引擎中,根据索引的存储形式可以分为以下几类:

聚集索引选取规则:

如果存在主键,主键索引就是聚集索引。
如果不存在主键,将使用第一个唯一 (UNIQUE) 索引作为聚集索引。
如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。

回表查询:举例如下图中执行左侧的SQL语句时,它会先在name字段创建的二级索引中查找Arm的数据对应的主键id,应为返回的是对应id的完整数据,索引会进行回表查询,在聚集索引中查询对应id的完整数据。

避免回表:在设计索引和编写查询语句时,尽量让查询的字段 “被索引覆盖”(即查询字段都在索引中),避免需要回表才能拿到完整数据,从而减少磁盘 IO,提升查询性能。

索引语法

创建索引

CREATE [UNIQUE|FULLTEXT]  INDEX 索引名 ON 表名(字段名);

字段名顺序不可以乱写

查看索引

SHOW INDEX FROM 表名;

删除索引

DROP INDEX 索引名 ON 表名;

SQL性能分析

查看执行频次

可以查看当前数据库增删改查的执行频次

show global status like 'Com_ _ _ _ _ _ _'

慢查询日志

 慢查询日志记录了所有执行时间超过指定参数(long query time,单位:秒,默认10秒)的所有SQL语句的日志。

查看是否开启慢查询日志

 show variables like 'slow_query_log';

慢查询配置文件/etc/my.cnf

开启MySQL慢日志查询开关
slow_query_log=1


设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2

更改配置后要重启mysqld服务

在/var/lib/mysql的目录下查看日志

Show profiles

show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。

查询当前mysql是否支持profile操作

SELECT   @@have_profiling

开启profile,默认是关闭

set profiling=1

查看每一条SQL的执行耗时情况。

show profiles;

查看指定的语句id的耗时情况

show profile for query 语句id;

查看指定的语句id的cpu消耗情况

show profile cpu for query  语句id;

explain执行计划

在sql语句前加上explain或者desc可以查看sql语句执行计划

--id:select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上向下;id不同,值越大,越先执行)

--select_type:表示select的类型,参考意义不大。

--type:表示连接类型,性能由好到坏的连接类型为null,system,const,eq_ref,ref,range,index,all。不访问任何表为null;访问系统表为system,根据主键或者唯一索引访问为const,使用非唯一索引为ref,使用了索引但是遍历了整个索引为index;没有用到索引,全表扫描为all。

--possible_key:显示条件的查询可能应用在这张表上的索引,一个或多个。

--key:实际使用的索引,如果为null,就是没有使用索引。

--key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,再不损失精度的前提下,长度越短越好。

--rows:mysql认为必须要执行查询的行数,在InnoDB引擎的表中,是一个估计值,可能不总是准确的。

--filtered:表示返回结果的行数占需读取行数的百分比,filter的值越大越好。 --extra:展示额外的信息。

索引使用规则

---

最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列索引将部分失效(后面的字段索引失效)

例如我创建了profession,age,status顺序的联合索引,下图中的条件是按照最左前缀来查询数据,所以用到了联合索引中的所有字段。

下图中因为跳过了age,所以导致status字段无法使用到索引,索引部分失效

如果查询条件只有profession或者只有profession和age,依然可以使用索引

这是因为联合索引在 B + 树中是按照定义的顺序逐层排序的,先按profession排序,相同profession再按age排序,最后按status排序。

范围查询

联合索引中出现范围查询(<>大于小于号),范围查询的右侧列索引会失效。

例如下图中使用了范围查询,和上述第一幅图比起来可知,导致status字段的索引失效。

在mysql中使用>=或者<=由explain可以看到右侧的索引不会失效,如下图:

因此在使用范围查询时,应优先使用>=或者<=,这个差异的原因在于:

  • ><会产生一个开放区间,MySQL 无法确定范围终点,因此无法有效利用后续索引
  • >=<=是闭合区间,MySQL 可以更精确地定位数据范围,从而继续使用右侧索引

索引失效

(1)在索引列上进行运算操作时,会使索引失效,如下图:

(2)字符串类型字段使用时,不加引号,索引将失效,如下图:

(3)如果是尾部模糊查询,索引不会失效;如果是头部模糊查询,索引失效,这是因为B+tree的排序默认就是根据数据头来排序,如下图:

(4)用or分开的条件,如果or的两边不是都有索引,那么涉及的索引都不会被用到,因为一边没有索引就会进行全表扫描,既然有一个字段要进行全表扫描,那就可以顺便把有索引的字段也查找出来,如下图中,age有索引,phone无索引,索引全部失效:

(5)如果mysql评估使用索引比全表扫描更慢,则不使用索引,如下图中,phone的大部分数据都大于17799990010,,profession都不为null,所以没必要走索引,直接全表扫描更快:

SQL提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

use  index:建议查询时使用索引

ignore index:查询时忽略索引

force index:强制查询时使用指定索引

覆盖索引&回表查询

尽量使用覆盖索引(查询使用了索引,并且需要返回的列在该索引中可以全部找到),减少select *。

举例如下图:根据extra可知上面sql语句查找使用了索引,所有数据都在索引列中,不需要回表。下面的sql语句查找也是用了索引,但是因为name字段在该索引中不存在,所以要回表到聚集索引中查询name字段。

Extra中:

using index condition:查找使用了索引,但是需要回表查询数据
using where; using index:查找使用了索引,但是需要的数据都在索引中能找到,所以不需要回表查询数据

什么是回表查询

我们结合下面这张图片来看

第一条语句select * from tb_user where id = 2;id是聚集索引,通过聚集索引可以直接获取到整行数据,不需要回表

第二条语句select id, name from tb_user where name ='Arm';,辅助索引(name)中已经包含了idname这两个需要查询的列,所以也不需要回表,直接从辅助索引就能获取到所需数据。

第三条语句select id,name,gender from tb_user where name ='Arm';需要回表查询。因为辅助索引(name)里只包含了idname的信息,而查询还需要gender列的数据。此时,需要先通过辅助索引找到对应的id(这里id=2),然后再根据id去聚集索引(主键索引)中查询gender列的值,这个过程就是回表

思考

 一张表,有四个字段(id, username, password, status),由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:

因为id为主键(聚集索引),所以为username和password建立联合索引,这样查询时满足覆盖索引,辅助索引下面指向聚集索引,可直接提取,不用回表查询聚集索引。最终,这个联合索引完美满足 “覆盖索引” 的要求,既实现了高效查询,又避免了索引冗余(不用重复存储 id),是最优设计。

前缀索引

当字段类型为字符串时,当为该字段建立索引时,会让索引变的很大,查询时,浪费大量的磁盘I/O,影响查询效率。此时可以将字符串的一部分前缀建立索引,这样可以节省空间,从而提高索引效率。

创建前缀索引

create index 索引名 on 表名(字段名(前缀长度),...);

前缀长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值 (基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

我们可以借助以下两个公式查看索引的选择性

select count(distinct email) / count(*) from tb_user ;
select count(distinct substring(email,1,5))/ count(*) from tb_user;

举例如下图:用email字段的前五个作为索引前缀建立索引,当按照email查询数据时,它会截取条件中email的前五个字符Ivbu6,先到email的前缀索引中查找对应的主键id,然后回表到聚集索引中查询到完整的email值,通过对比判断是否等于Ivuu666@163.com,然后在通过前缀索引的链表继续遍历下一个,按照以上操作循环往复,直到链表的下一个节点的前缀值不等于Ivbu6则结束。

也就是说,前缀索引需要回表查询

总结:前缀索引说白了就是“效率换换空间”,舍弃部分查询效率,换取更多存储空间

单列索引和联合索引

单列索引就是一个索引只包含单个列,联合索引就是一个索引包含多个列。

在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建立联合索引,而非单列索引。

举例如下图:下图中对phone和name都建立了单列索引,但是在查询时只是用到了phone的单列索引,并不会用到name的单列索引,我个人的理解是因为在使用到phone的单列索引时会进行回表查询,回表查询时就会把name字段查出来,直接比较就可以了,所以用不到name的单列索引。

当我们为phone和name建立了联合索引时,他会先按照phone排序,phone相同时按照name排序,所以当条件中有这两个字段的条件,可以通过该联合索引快速定位到对应的主键id,不需要回表查询,如下图:

索引设计原则


1.针对于数据量较大,且查询比较频繁的表建立索引。

 2. 针对于常作为查询条件 (where)、排序 (order by)、分组 (group by) 操作的字段建立索引。

3.尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。

4.如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。

5.尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

6.要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。

7.如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

这是我的个人学习笔记,主要用于记录自己对知识点的理解和梳理。由于目前仍在学习探索阶段,内容中难免存在理解偏差或表述疏漏,恳请各位大佬不吝赐教,多提宝贵意见~ 若有不同看法,欢迎理性交流探讨,感谢包容与指正!


文章转载自:

http://oqq4DrkE.dqbpf.cn
http://A3dQV4qk.dqbpf.cn
http://Y74qrl5I.dqbpf.cn
http://4ne7jfZt.dqbpf.cn
http://PqYEQv4Z.dqbpf.cn
http://yyktHsC3.dqbpf.cn
http://0xXGTYlI.dqbpf.cn
http://SI8Ne8uh.dqbpf.cn
http://OJ6cqqNa.dqbpf.cn
http://quTwJ3f2.dqbpf.cn
http://lJ9KpCPl.dqbpf.cn
http://5lZV9rqV.dqbpf.cn
http://YkakyYPo.dqbpf.cn
http://Bn3QGra3.dqbpf.cn
http://ASoXRNBh.dqbpf.cn
http://JDzkeSkq.dqbpf.cn
http://SSkkxhEy.dqbpf.cn
http://MVbkjyDs.dqbpf.cn
http://ut9ie1Xm.dqbpf.cn
http://fu6gNahe.dqbpf.cn
http://qMkHId2V.dqbpf.cn
http://IMOV5JeU.dqbpf.cn
http://RPcKos2B.dqbpf.cn
http://harGFXyg.dqbpf.cn
http://f5RkbKxa.dqbpf.cn
http://z7Ei0Nj3.dqbpf.cn
http://PJ24rE4X.dqbpf.cn
http://wryNLyjS.dqbpf.cn
http://W2EbNjfu.dqbpf.cn
http://9zmGamZb.dqbpf.cn
http://www.dtcms.com/a/375491.html

相关文章:

  • 面向OS bug的TypeState分析
  • 【文献笔记】Task allocation for multi-AUV system: A review
  • 小红书批量作图软件推荐运营大管家小红书批量作图工具
  • ArrayList详解与实际应用
  • 德意志飞机公司与DLR合作完成D328 UpLift演示机地面振动测试
  • MongoDB 备份与恢复终极指南:mongodump 和 mongorestore 深度实战
  • ctfshow - web - 命令执行漏洞总结(二)
  • 基于STM32的GPS北斗定位系统
  • 2025年大陆12寸晶圆厂一览
  • VMware Workstation Pro 安装教程
  • Java Spring @Retention三种保留策略
  • 低代码平台的核心组件与功能解析:红迅低代码平台实战探秘
  • linux sudo权限
  • PM2 管理后端(设置项目自启动)
  • 中国香港服务器中常提到的双向/全程CN2是什么意思?
  • DCS+PLC协同优化:基于MQTT的分布式控制系统能效提升案例
  • Backend
  • 分布式专题——6 Redis缓存设计与性能优化
  • 《智能网联汽车交通仿真软件可信度评估》团标启动会圆满举办
  • 无人机云台电压类型及测量方法
  • 光伏无人机3D设计——高效出方案的快速设计方式!
  • K8s角色权限管理全解析
  • Postgresql 发送数据到Splunk
  • [网络入侵AI检测] CNN-LSTM混合模型
  • 使用列表推导式取代map和filter的最佳实践 (Effective Python 第27条)
  • Promise状态和方法都有哪些,以及实现原理
  • jquery基础知识总结
  • Qwen-VL系列-国产大模型开眼看世界
  • OpenEuler部署gitlab(小白的“升级打怪”成长之路)
  • 内存视角看「类、原型、实例」