🕢MySql存储引擎、索引与优化
2022-7-16
| 2023-2-6
0  |  阅读时长 0 分钟
type
status
password
date
slug
summary
category
URL
tags
icon

mysql体系结构

notion image

mysql存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式,存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。
命令
描述
show create table account
查询建表语句,默认存储引擎:InnoDB
show engines
查询当前数据库支持的存储引擎
create table myisam( id int, name varchar(10) ) engine=MyISAM
建表并制定存储引擎

不同的存储引擎

InnoDB

  • 介绍:InnoDB是一种兼顾高可靠性和高性能的通用存储引擎。在MySQL 5.5之后,InnoDB是默认的MySQL存储引擎。
  • 特点
    • DML操作遵循ACID模型,支持事物;
    • 行级锁,提高并发访问性能
    • 支持外键 FOREIGN KEY 约束,保证数据的完整性和正确性
  • 文件
    • xxx.ibd: xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引
      • 参数:innodb_file_per_table
notion image

MySAM

  • 介绍:MySAM是MySQL早期的默认存储引擎。
  • 特点
    • 不支持事物,不支持外键
    • 支持表锁,不支持行级锁
    • 访问速度快
  • 文件
    • xxx.sdi: 存储该表结构信息
    • xxx.MYD: 存储数据
    • xxx.MYI: 存储索引

Memory

  • 介绍:Memory引擎的表数据存储在内存中,由于受到硬件问题、断电问题的影响,只能将这些表作为临时表或缓存使用。
  • 特点
    • 内存存放
    • hash索引(默认)
  • 文件
    • xxx.sdi: 存储该表结构信息

总结

特点
InnoDB
MySAM
Memory
存储限制
64TB
事务安全
支持
-
-
锁机制
行锁
表锁
表锁
B+tree索引
支持
支持
支持
Hash索引
-
-
支持
全文索引
支持(5.6版本之后)
支持
-
空间使用
N/A
内存使用
中等
批量插入速度
支持外建
支持
-
-
存储引擎选择
  • InnoDB:是Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包括很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。
  • MyISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎非常合适
  • MEMORY:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。

索引

notion image
索引(index)是帮助MySQL高效获取数据数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优势
劣势
提高数据检索的效率,降低数据库的IO成本
索引列也是要占用空间的
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗
索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERTUPDATEDELETE时,效率降低

索引结构

索引结构
描述
B+Tree索引
最常见的索引类型,大部分引擎都支持B+树索引
Hash索引
底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询
R- tree(空间索引)
空间索引是MySAM引擎的一个特殊索引类型,主要用于地理空间数据类型,使用较少
Full-text(全文索引)
是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr、ES
notion image
 
为什么InnoDB存储引擎选择使用B+tree索引
二叉树
相对于二叉树,层级更少,搜索效率高
B-tree
相对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低
Hash
相对Hash索引,B+tree支持范围匹配及排序操作

b+树索引

二叉树
notion image
  • 二叉树:顺序插入时,会形成一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢
  • 红黑树:大数据情况下,层级较深,检索速度慢
B-Tree
notion image
B+Tree
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
notion image
相对于B-Tree区别
  1. 所有的数据都会出现在叶子节点
  1. 叶子节点形成一个单向链表

hash索引

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突,可以通过链表来解决。
notion image
hash索引特点
  1. hash索引只能用于对等比较(=,in),不支持范围查询(between,<,>,…)
  1. 无法利用索引完成排序操作
  1. 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
存储引擎支持
在MySQL中,支持hash索引的是Memory引擎,而在InnoDB中具有自适应hash功能,hash索引是根据B+Tree索引在指定条件下自动构建的

索引分类

分类
含义
特点
关键字
主键索引
针对表中主键创建的索引
默认自动创建,只能有一个
PRIMARY
唯一索引
避免同一个表中某数据列中的值重复
可以有多个
UNIQUE
常规索引
快速定位特定数据
可以有多个
INDEX
全文索引
查找的是文本中的关键词,而不是比较索引中的值
可以有多个
FULLTEXT

InnoDB索引分类

在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类
含义
特点
聚集索引
将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据
必须有,而且只有一个
二级索引
将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键
可以存在多个
聚集索引选取规则:
  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将第一个唯一索引作为聚集索引
  • 如果不存在主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
notion image
notion image

索引语法

查看索引

创建索引

删除索引

示例

按照下列的需求,完成索引的创建
id
name
phone
email
profession
age
status
1
吕布
13719932947
1371993@163.com
软件工程
23
6
2
曹操
13729952971
1372995@163.com
通讯工程
25
0
  1. name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
  1. phone手机号的值,是非空,且唯一的,该字段的值创建唯一索引
  1. 为profession、age、status创建联合索引

索引在where语句中的使用规则

联合索引

联合索引要遵守最左前缀法则
最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。对下面使用profession、age、status字段的联合索引,且联合索引的顺序为pro_age_sta
范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效

字符串索引

字符串不加引号
字符串类型字段使用时。不加引号,虽然可以使用;但是索引会失效。
模糊查询
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
前缀索引
当字段类型为字符串(varchartext等)时,有时候需要索引很长的字符串,这会让索引变得很大;查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
💡
如何选择前缀长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值;索引选择性越高则查询效率越高;唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。 select count(distinct email)/count(1) from tb_user; select count(distinct substring(email,1,5))/count(1) from tb_user;
前缀索引查询流程
查询email=lvbu666z@163.com。从辅助索引email(5)搜索lvbu6找到id=1,根据聚焦索引id=1找到数据row,并与lvbu666@163.com进行比较,相同则返回数据;然后根据链表从辅助索引查找下一个索引是xiaoylvbu6不匹配,停止搜索。
notion image

索引失效

不要在索引列上进行运算操作,索引将失效
or 条件只要出现了非索引列,将会全表扫描。
如果MySQL评估使用索引比全表更慢,则不使用索引

索引在select语句中的使用规则(覆盖索引)

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *。下面的例子中使用了联合索引idx_user_pro_age_sta
回表查询示例
gender字段需要回表进行查询。首先从辅助索引name中找到Arm对应的聚焦索引id,然后根据聚焦索引id查找对应的数据row,并从row中查找对应的gender
notion image

sql提示(指定使用哪些索引)

在MySQL中经常使用多个索引。例如profession存在idx_user_pro索引和idx_user_pro_age_sta联合索引

索引设计原则

  1. 针对于数据量较大,且查询比较频繁的表建立索引。
  1. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
  1. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
  1. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点建立前缀索引
  1. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  1. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
  1. 如果索引不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引有效地用于查询。

MySQL优化

插入优化

insert 优化

批量插入
手动提交事务
主键顺序插入

大批量插入数据

如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供点load指令进行插入。

大批量导出数据

主键优化

InnoDB的数据存储方式

在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表。
notion image
  • 页分裂
    • 页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据太大,会行溢出),根据主键排列
  • 页合并
    • 当删除一行记录时,实际上记录并没有被物理删除。只是记录被标记为删除,并且它的空间变得允许被其他记录声明使用
      当页中删除的记录达到MERGE_THRESHOLD(默认页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
      💡
      MERGE_THRESHOLD:合并页的阈值,可以自己设置,在创建表或创建索引时指定

主键设计原则

  1. 满足业务需求的情况下,尽量降低主键的长度。
  1. 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
  1. 尽量不要使用UUID做主键或者是其他自然主键,如身份证号。
  1. 业务操作时,避免对主键的修改。

order by优化

  • 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
  • 尽量使用覆盖索引。
  • 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
  • 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_ buffer_size(默认256k)。
💡
explain 语句中 Extra 字段 1. Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。 2. Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。 3. Backward index scan:反向扫描索引

group by优化

count优化

按照效率排序的话,count(字段)<count(主键 id) < count(1) 、 count(*),所以尽量使用 count(*)
  • count(主键) InnoDB 引|擎会遍历整张表,把每一行的 主键id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为nul)。
  • count(字段) 没有not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。 有not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。
  • count(1) InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字 “1” 进去,直接按行进行累加。
  • count (*) InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。

limit优化

一个常见又非常头疼的问题就是 Limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000 -2000010的记录,其他记录丢弃,查询排序的代价非常大。 优化思路:一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

update优化

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。

sql性能分析

增删改查的执行频率

MySQL客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信息。通过如下命令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次。

慢查询日志

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。
开启 MySQL的慢查询日志
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
配置完毕之后,通过systemctl restart mysqld指令重新启动MySQL服务器进行测试
慢查询记录信息
查看慢日志中记录的信息 /var/lib/mysql/localhost-slow.log

profile

MySQL 的 Query Profiler 是一个使用非常方便的 Query 诊断分析工具,通过该工具可以获取一条Query 在整个执行过程中多种资源的消耗情况,如 CPU,IO,IPC,SWAP 等,以及发生的 PAGE FAULTS,CONTEXT SWITCHE 等等,同时还能得到该 Query 执行过程中 MySQL 所调用的各个函数在源文件中的位置。

explain

explain可以模拟优化器执行SQL查询语句,从而知道MySQL怎么处理你的SQL语句的,分析你的查询语句和表结构的性能瓶颈。
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
1
SIMPLE
student
(NULL)
index
(NULL)
name_age
68
(NULL)
30
100.00
Using index
  • EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
  • EXPLAIN不考虑各种Cache
  • EXPLAIN不能显示MySQL在执行查询时所作的优化工作
  • 部分统计信息是估算的,并非精确值
  • EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。

id字段

  1. id相同时,执行顺序由上至下
  1. 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
复合情况
先看id应该是S表最先被读取,SC和C表id相同,然后table中SC更靠上,所以第二张读取的表应当是SC,最后读取C。S-SC-C

select_type

SIMPLE(简单SELECT,不使用UNION或子查询等)
PRIMARY(查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)。 SUBQUERY(子查询中的第一个SELECT)。 DEPENDENT SUBQUERY(子查询中的第一个SELECT,取决于外面的查询) ???
UNION(UNION中的第二个或后面的SELECT语句) DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询) UNION RESULT(UNION的结果)
DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生)
S和SC表的select_type都是DERIVED,这说明S和SC都被用来做衍生查询,而这两张表查询的结果组成了名为<derived2>的衍生表,而衍生表的命名就是<select_type + id>
UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)

table

显示这一行的数据是关于哪张表的,有时不是真实的表名字,看到的是derivedx(x是个数字,我的理解是第几步执行的结果)

type

表示MySQL在表中找到所需行的方式,又称“访问类型”。常用的类型有: ALL, index,  range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)
NULL > system > const > eq_ref > ref > ref_or_null > index_merge > range > index > ALL
NULLMySQL能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引
SYSTEM 表只有一行记录(等于系统表),这是const类型的特列,平时不大会出现,可以忽略。
const 表示通过索引一次就找到了,const用于比较primary keyuique索引,因为只匹配一行数据,所以很快,如主键置于where列表中,MySQL就能将该查询转换为一个常量。
eq_ref 用于联表查询的情况,按联表的主键或唯一键联合查询(与ref的区别)
多表join时,对于来自前面表的每一行,在当前表中只能找到一行
以上面查询为例,我们观察idtable会知道,先是从SC表中取出一行数据,然后再S表查找匹配的数据,我们观察,SC中取出cid和S表中的id比较,毫无疑问因为id是S表中的主键(不重复),所以只能出现一个id与cid的值相同。所以满足条件 S 表的 typeeq_ref
ref 可以用于单表扫描或者连接。如果是连接的话,驱动表的一条记录能够在被驱动表中通过非唯一(主键)属性所在索引中匹配多行数据,或者是在单表查询的时候通过非唯一(主键)属性所在索引中查到一行数据。
ref_or_null 类似ref,但是可以搜索值为NULL的行
index_merge表示查询使用了两个以上的索引,最后取交集或者并集,常见and or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range
range 索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。
index只遍历索引树,通常比All快。虽然allindex都是读全表,但index是从索引中读取的,而all是从硬盘读的。
ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
 

possible_keys字段(表中存在,但不一定是使用的索引)

指出MySQL能使用哪个索引,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用该列完全独立于EXPLAIN输出所示的表的次序,这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。

key字段(表中存在且被使用的索引)

这个表里面存在且被使用的索引。如果为null,则没有使用索引,否则会显示你使用了哪些索引,查询中若使用了覆盖索引(查询的列刚好是索引),则该索引仅出现在key列表。

ref字段

显示哪些列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

rows字段和Filter字段

rows是根据表的统计信息和索引的选用情况,优化器大概帮你估算出你执行这行函数所需要查询的行数。
Filter是查询的行数与总行数的比值。其实作用与rows差不多,都是数值越小,效率越高。

Extra字段

该列包含MySQL解决查询的详细信息,有以下几种情况:
Using filesort 表示当SQL中有一个地方需要对一些数据进行排序的时候,优化器找不到能够使用的索引,所以只能全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。反之呢?由于索引的底层是B+Tree实现的,他的叶子节点本来就是有序的,这样的查询能不爽吗?
Using tempporary 表示在对MySQL查询结果进行排序时,使用了临时表,这样的查询效率是比外部排序更低的,常见于order bygroup by
  • Using where、Using index 查找使用了索引,需要的数据都在索引列中能找到,所以不需要回表查询数据。
  • Using index condition 查找使用了索引,但是需要回表查询数据。
  • Using join buffer 表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的join buffer调大一些。
  • impossible where 筛选条件没能筛选出任何东西
  • distinct 优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作
  • sql
  • sql事务性MySql视图
    Loading...
    目录