type
status
password
date
slug
summary
category
URL
tags
icon
mysql体系结构
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
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的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。
索引
索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优势 | 劣势 |
提高数据检索的效率,降低数据库的IO成本 | 索引列也是要占用空间的 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗 | 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行 INSERT 、UPDATE 、DELETE 时,效率降低 |
索引结构
索引结构 | 描述 |
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+树索引 |
Hash索引 | 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
R- tree(空间索引) | 空间索引是MySAM引擎的一个特殊索引类型,主要用于地理空间数据类型,使用较少 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr、ES |
为什么InnoDB存储引擎选择使用B+tree索引
二叉树
相对于二叉树,层级更少,搜索效率高
B-tree
相对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低
Hash
相对Hash索引,B+tree支持范围匹配及排序操作
b+树索引
二叉树
- 二叉树:顺序插入时,会形成一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢
- 红黑树:大数据情况下,层级较深,检索速度慢
B-Tree
B+Tree
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
相对于B-Tree区别
- 所有的数据都会出现在叶子节点
- 叶子节点形成一个单向链表
hash索引
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突,可以通过链表来解决。
hash索引特点
- hash索引只能用于对等比较(=,in),不支持范围查询(between,<,>,…)
- 无法利用索引完成排序操作
- 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
存储引擎支持
在MySQL中,支持hash索引的是Memory引擎,而在InnoDB中具有自适应hash功能,hash索引是根据B+Tree索引在指定条件下自动构建的
索引分类
分类 | 含义 | 特点 | 关键字 |
主键索引 | 针对表中主键创建的索引 | 默认自动创建,只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | INDEX |
全文索引 | 查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
InnoDB索引分类
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类 | 含义 | 特点 |
聚集索引 | 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 |
二级索引 | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则:
- 如果存在主键,主键索引就是聚集索引
- 如果不存在主键,将第一个唯一索引作为聚集索引
- 如果不存在主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
索引语法
查看索引
创建索引
删除索引
示例
按照下列的需求,完成索引的创建
id | name | phone | email | profession | age | status |
1 | 吕布 | 13719932947 | 1371993@163.com | 软件工程 | 23 | 6 |
2 | 曹操 | 13729952971 | 1372995@163.com | 通讯工程 | 25 | 0 |
- name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
- phone手机号的值,是非空,且唯一的,该字段的值创建唯一索引
- 为profession、age、status创建联合索引
索引在where语句中的使用规则
联合索引
联合索引要遵守最左前缀法则
最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。对下面使用profession、age、status字段的联合索引,且联合索引的顺序为pro_age_sta
范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效
字符串索引
字符串不加引号
字符串类型字段使用时。不加引号,虽然可以使用;但是索引会失效。
模糊查询
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
前缀索引
当字段类型为字符串(
varchar
、text
等)时,有时候需要索引很长的字符串,这会让索引变得很大;查询时,浪费大量的磁盘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
进行比较,相同则返回数据;然后根据链表从辅助索引查找下一个索引是xiaoy
与lvbu6
不匹配,停止搜索。索引失效
不要在索引列上进行运算操作,索引将失效
or 条件只要出现了非索引列,将会全表扫描。
如果MySQL评估使用索引比全表更慢,则不使用索引
索引在select语句中的使用规则(覆盖索引)
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少
select *
。下面的例子中使用了联合索引idx_user_pro_age_sta
回表查询示例
gender字段需要回表进行查询。首先从辅助索引
name
中找到Arm
对应的聚焦索引id
,然后根据聚焦索引id
查找对应的数据row
,并从row
中查找对应的gender
。sql提示(指定使用哪些索引)
在MySQL中经常使用多个索引。例如profession存在idx_user_pro索引和idx_user_pro_age_sta联合索引
索引设计原则
- 针对于数据量较大,且查询比较频繁的表建立索引。
- 针对于常作为查询条件(
where
)、排序(order by
)、分组(group by
)操作的字段建立索引
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点建立前缀索引
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
- 如果索引不能存储
NULL
值,请在创建表时使用NOT NULL
约束它。当优化器知道每列是否包含NULL
值时,它可以更好地确定哪个索引有效地用于查询。
MySQL优化
插入优化
insert 优化
批量插入
手动提交事务
主键顺序插入
大批量插入数据
如果一次性需要插入大批量数据,使用
insert
语句插入性能较低,此时可以使用MySQL数据库提供点load
指令进行插入。大批量导出数据
主键优化
InnoDB的数据存储方式
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表。
- 页分裂
页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据太大,会行溢出),根据主键排列
- 页合并
当删除一行记录时,实际上记录并没有被物理删除。只是记录被标记为删除,并且它的空间变得允许被其他记录声明使用
当页中删除的记录达到MERGE_THRESHOLD(默认页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
MERGE_THRESHOLD:合并页的阈值,可以自己设置,在创建表或创建索引时指定
主键设计原则
- 满足业务需求的情况下,尽量降低主键的长度。
- 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
- 尽量不要使用UUID做主键或者是其他自然主键,如身份证号。
- 业务操作时,避免对主键的修改。
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字段
- id相同时,执行顺序由上至下
- 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
复合情况
先看
id
应该是S表最先被读取,SC和C表id
相同,然后table中SC更靠上,所以第二张读取的表应当是SC,最后读取C。S-SC-Cselect_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
NULL
MySQL能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引
SYSTEM
表只有一行记录(等于系统表),这是const
类型的特列,平时不大会出现,可以忽略。
const
表示通过索引一次就找到了,const
用于比较primary key
或uique
索引,因为只匹配一行数据,所以很快,如主键置于where
列表中,MySQL就能将该查询转换为一个常量。
eq_ref
用于联表查询的情况,按联表的主键或唯一键联合查询(与ref的区别)
多表
join
时,对于来自前面表的每一行,在当前表中只能找到一行。以上面查询为例,我们观察
id
和table
会知道,先是从SC表中取出一行数据,然后再S表查找匹配的数据,我们观察,SC中取出cid和S表中的id比较,毫无疑问因为id是S表中的主键(不重复),所以只能出现一个id与cid的值相同。所以满足条件 S 表的 type
为eq_ref
。ref
可以用于单表扫描或者连接。如果是连接的话,驱动表的一条记录能够在被驱动表中通过非唯一(主键)属性所在索引中匹配多行数据,或者是在单表查询的时候通过非唯一(主键)属性所在索引中查到一行数据。
ref_or_null
类似ref,但是可以搜索值为NULL
的行
index_merge
表示查询使用了两个以上的索引,最后取交集或者并集,常见and
,or
的条件使用了不同的索引,官方排序这个在ref_or_null
之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range
。
range
索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。
index
只遍历索引树,通常比All
快。虽然all
和index
都是读全表,但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 by
和group by
Using where、Using index
查找使用了索引,需要的数据都在索引列中能找到,所以不需要回表查询数据。
Using index condition
查找使用了索引,但是需要回表查询数据。
Using join buffer
表明使用了连接缓存,比如说在查询的时候,多表join
的次数非常多,那么将配置文件中的缓冲区的join buffer
调大一些。
impossible where
筛选条件没能筛选出任何东西
distinct
优化distinct
操作,在找到第一匹配的元组后即停止找同样值的动作