MySQL进阶篇

MySQL进阶

1. 存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。

1.1 MySQL体系结构

在这里插入图片描述

  • 连接层
    • 主要完成一些类似于连接处理、授权认证、及相关的安全方案。
  • 服务层
    • 主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,是否使用索引,部分内置函数的执行。
  • 引擎层
    • 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。数据库中的索引是在存储引擎层实现的
  • 存储层
  • 主要是将数据(如: redolog、undolog、数据、索引、二进制日志、错误日志、查询日志、慢查询日志等)存储在文件系统之上,并完成与存储引擎的交互。

1.2 存储引擎介绍

在这里插入图片描述

引擎就是类似发动机,之间没有好坏之封面,只有合不合适。

  • 存储引擎,也是一样,他是mysql数据库的核心,我们也需要在合适的场景选择合适的存储引擎。接下来就来介绍一下存储引擎。

  • ==存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。==存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。我们可以在创建表的时候,来指定选择的存储引擎,如果没有指定将自动选择默认的存储引擎。

建表时指定引擎

CREATE TABLE 表名(
字段1 字段1类型 [ COMMENT 字段1注释 ] ,
......
) ENGINE = INNODB [ COMMENT 表注释 ] ;

1.3 存储引擎的特点

1.3.1 InnoDB

  • 介绍

    • InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB是默认的
      MySQL 存储引擎。
  • 特点

    • 事务、行级锁、外键
  • 文件

    • tablename.ibd:tablename代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm-早期的 、sdi-新版的)、数据和索引。
  • 逻辑存储结构

    在这里插入图片描述

  • 表空间 : InnoDB存储引擎逻辑结构的最高层,ibd文件其实就是表空间文件,在表空间中可以 包含多个Segment段。

  • 段 : 表空间是由各个段组成的, 常见的段有数据段、索引段、回滚段等。InnoDB中对于段的管 理,都是引擎自身完成,不需要人为对其控制,一个段中包含多个区。

  • 区 : 区是表空间的单元结构,每个区的大小为1M。 默认情况下, InnoDB存储引擎页大小为 16K, 即一个区中一共有64个连续的页。

  • 页 : 页是组成区的最小单元,页也是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默 认为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。

  • 行 : InnoDB 存储引擎是面向行的,也就是说数据是按行进行存放的,在每一行中除了定义表时 所指定的字段以外,还包含两个隐藏字段(后面会详细介绍)。

1.3.2 MyIsAM

  • 是早起的默认存储引擎
  • 不支持事务、外键、行锁。访问速度快,表锁

1.4 引擎选择

InnoDB引擎与MyISAM引擎的区别 ?
①. InnoDB引擎, 支持事务, 而MyISAM不支持。

②. InnoDB引擎, 支持行锁和表锁, 而MyISAM仅支持表锁, 支持行锁。

③InnoDB引擎, 支持外键, 而MyISAM是不支持的。

2. 索引***

2.1索引概述

2.1.1 介绍

索引(index)是帮助MySQL高效获取数据的数据结构(有序)。

2.1.2 特点

  • 优点
    • 提高搜索效率
    • 有序,降低排序成本
  • 劣势
    • 占用空间
    • 增删改效率降低

2.2 索引结构

B+TREE索引、Hash索引、R-tree(空间索引)、Full-text(全文索引)、B-TREE

2.2.1 B-TREE

5阶B-TREE,每个节点最多4个key,5个指针。

在这里插入图片描述

  • 一旦节点存储的key数量到达5,就会裂变,中间元素向上分裂。
  • 在B树中,非叶子节点和叶子节点都会存放数据

2.2.2 B+TREE***

与B-TREE不同的地方

  • 所有节点均能在叶子节点找到。
  • 所有的数据只在叶子节点。
  • 所有叶子节点组成单向链表。

在这里插入图片描述

MySQL优化了的:

  • MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能,利于排序。

在这里插入图片描述

2.2.2.1 联合索引下的B+TREE

设置联合索引的字段为a,b,c

则索引数据结构图如下

在这里插入图片描述

【例1】

select ... where a = 1

先根节点匹配a=1,在左树,再找到a=1,再去左树匹配,因为没有要求b的值,所以查到一条结果(1,1,4,5)由于叶子节点是链表,紧着匹配下一个节点,a=1,匹配成功继续返回,以此类推,最后返回2条结果

【例2】

select ... where a=13 and b=12

先根节点匹配a=13,走1~56的间隙的指针到左树。继续匹配a=13,走12~21的间隙的指针到右树,找到一个结果(13,12,4),紧接匹配b=12,成功。然后顺着链表,找到(13,16,1,6),逐个匹配a,b,失败,以此类推。最后返回一条

2.2.3 其他

  • Hash索引
    • 检索数据复杂度是常数阶
    • 但是,不支持排序,<,>,between等范围查找
  • 二叉树
    • 结构简单,容易实现
    • 阶太少,数据量多时,检索深度过高

2.2.4 B+TREE好在哪?

  • 相比二叉树,后者阶数更高,检索效率高
  • 相比B-TREE,后者数据只存放在叶子节点,数据与结构分离,页固定16k,存放的节点更多,树的深度更低。所有数据组成单向链表,区间查找,排序效率提高
  • 相比Hash索引,后者支持防伪匹配以及排序

2.3 索引分类

2.3.1 索引分类

主键索引、唯一索引、常规索引、全文索引

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

2.3.2 聚集索引&二级索引

分类含义特点
聚集索引将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据每张表必须有,而且只有一个
二级索引将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键可以存在多个
  • 聚集索引
    • 存在主键,主键即聚集索引
    • 不存在主键,将使用第一个UNIQUE索引。
    • 都没有,则创建隐藏的rowid为聚集索引
  • 二级索引
    • 非聚集索引就是二级索引
    • 非主属性可以创建二级索引

在这里插入图片描述

可以看出,聚集索引的叶子节点放的是一行的数据,用其他字段来查的时候,找到id值,最终都是要依靠聚集索引来找到最终数据,亦称之为回表查询

2.4 索引语法

2.4.1 创建索引

create [UNIQUE|FULLTEXT] index index_name on table_name(col1,col2...)
  • 默认是常规索引,当字段不唯一时,为联合索引
  • UNIQUE:只能给唯一索引的字段创建索引
  • FULLTEXT:全文索引

2.4.2 删除索引

drop index index_name on table_name

2.4.3 查看索引

show index from table_name

2.5 索引性能分析

2.5.1 explain

explain <select ...>

【例】

explain select * from user where id=1;

在这里插入图片描述

详细字段

  • id
    • 查询的序号,包含一组数字,表示查询中执行select子句或操作表的顺序
      两种情况
      id相同,执行顺序从上往下
      id不同,id值越大,优先级越高,越先执行
  • select_type
    • 表示 SELECT 的类型,常见的取值有
    • SIMPLE(简单表,即不使用表连接 或者子查询)、
    • PRIMARY(主查询,即外层的查询)、
    • UNION(UNION 中的第二个或者后面的查询语句)、
    • SUBQUERY(SELECT/WHERE之后包含了子查询)
  • type
    • 表示连接类型,由好到差
    • system:表中仅有一行。
    • const:通过索引一次就找到 (主键或者唯一索引) 。
    • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于唯一索引或者主键扫描。
    • ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,可能会找多个符合条件的行,属于查找和扫描的混合体。
    • range: 对索引进行范围检索。
    • index: 物理文件全扫描,速度非常慢
    • all:遍历全表以找到匹配的行。
  • possible_keys
  • 可能使用的索引
  • key
    • 实际用到的索引
  • key_len
  • 表示索引中使用的字节数,该列计算查询中使用的索引的长度在不损失精度的情况下,长度越短越好。
  • ref
    • 显示索引的哪一列被使用了。联合索引时作用可参考
  • rows
    • MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值, 可能并不总是准确的
  • Extra
  • 包含不适合在其他列中显示,但是十分重要的额外信息
  • Using filesort:说明mysql会对数据适用一个外部的索引排序。而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成排序操作称为“文件排序”
  • Using temporary:使用了临时表保存中间结果,mysql在查询结果排序时使用临时表。常见于排序order by和分组查询group by。
  • Using index:表示相应的select操作用使用覆盖索引,避免访问了表的数据行。如果同时出现using where,表名索引被用来执行索引键值的查找;如果没有同时出现using where,表名索引用来读取数据而非执行查询动作。
  • Using where :表明使用where过滤
  • using join buffer:使用了连接缓存
  • impossible where:where子句的值总是false,不能用来获取任何元组
  • select tables optimized away:在没有group by子句的情况下,基于索引优化Min、max操作或者对于MyISAM存储引擎优化count(*),不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
  • distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作。

2.5.2 其他小技巧

\G #将查询出来的结果以列表显示

select ...\G 

2.6 索引使用原则

2.6.1 最左前缀法则

联合索引,必须要遵守最左前缀法。

最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会后续失效

具体最左的列是什么,是由索引创建时候决定的。

如有索引:

create index idx_user_name_age_sex table(name,age,sex)   

正例

select ... where name='Jack'

select ... where name='Jack' and age = '18'

select ... where name='Jack' and age = '18' and sex = '男'

select ... where age = '18' and sex = '男' and name='Jack'  #不受顺序影响

反例

select ... where age = '18' #没有第一列

select ... where sex = '男' #没有前两列

select ... where name='Jack' and sex = '男' #越过了中间列,索引前半部分生效

2.6.2 范围查询

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

select ... where name='Jack' and age > 1

优化(可能还是range级别)

select ... where name='Jack' and age >= 1

2.6.4 索引失效

2.6.4.1 索引列运算

不要在索引列上进行运算操作, 索引将失效。

反例

select ... where substring(phone,10,2) = '13';
2.6.4.2 字符串不加引号

MySQL某字段是字符串类型,但是SQL中该字段没加引号,索引将失效.

select ... where phone = 1300

SQL中非纯数字不加引号会报错!

所以可能会在以字符串形式存储纯数字的字段中出现索引失效,因为得先不报错,才能执行SQL。

2.6.4.3 模糊查询

头部模糊查询,索引失效。仅仅尾部则不会。

有效

select ... from name like 'Ja%'

失效

select ... from name like '%ck'
2.6.4.4 or 连接条件

但凡or的一侧没有索引,则索引失效

select ... where id = 1 or age='18'
#id一定有索引,但是age无索引则也是白搭,索引完全失效!

若对age创建索引,则索引有效!

2.6.4.5 数据分布影响

如果MySQL优化器评估使用索引比全表更慢,则不使用索引。

因为MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。

  1. 对于有序的列表,比如手机号,如果手机号共有100w个,phone>1开始查,那么走全表扫描。如果phone>50w,则可能走索引(具体得看优化器的阈值)
  2. is null 与is not null 当列的绝大部分都是null时,【is null】则会全表,【is not null】会使用索引,反之也成立。

2.6.5 SQL提示

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

select * from table_name <use|ignore|force> index(index_name) where ...
  • use : 建议MySQL使用的索引,类似建议java调用gc,不一定会用
  • ignore:忽略指定索引。
  • force :强制使用。

2.6.6 索引使用

2.6.6.1 覆盖索引

尽量使用覆盖索引,减少select *。 那么什么是覆盖索引呢? 覆盖索引是指 查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到 。

比如

select name,phone... where name='Jack'

因为phone没有不在name所在的联合索引,因此phone需要回表查询,效率很低!

又如

select id,username,password where username='admin'

如果username与password建立了联合索引,那么就不需要回表查询,效率高!(此时的联合索引的数据域是id)

  • explain的额外信息
Extra含义
Using where; Using Index查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据
Using index condition或者NULL查找使用了索引,但是需要回表查询数据
2.6.6.2 前缀索引

当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

  • 创建语法
create index idx_name_n on table_name(column_name(n))
  • n值确定

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

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

选择性的值小于等于1,1是最好的即唯一索引。但要考虑索引使用空间,所以上面n=5跟n=6的选择性一样时,选n=5

2.6.6.3 单列索引与联合索引选择

单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列 。

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

另外,当单例索引与联合索引同时包含存在时,要use table_name(index_name)来建立MySQL使用联合索引。

create index on table(col1,col2)
create index on table(col2,col1)

是有区别的!要考虑最左匹配原则

2.7 索引设计原则

2.7.1 什么时候建立

  1. 针对于数据量较大且查询比较频繁的表,建立索引。
  2. 针对于作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。

2.7.2 建立什么索引

  1. 尽量选择区分度高的列作为索引,尽量建立唯一索引 (区分度越高,使用索引的效率越高)
  2. 字符串类型的字段的长度较长,建立前缀索引。
  3. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

2.7.3 注意

  1. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。(当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询,比如受数据分布影响的索引失效)
  2. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。

3. SQL优化

3.1 插入数据

  • 批量插入数据

    insert into table values(col1,col2),(col1,col2)
    
  • 手动控制事务

    start transaction
    ...
    commit
    
  • 主键顺序插入

    1,2,3,4,5,78,99
    
  • 大批量插入

    -- 客户端连接服务端时,加上参数 -–local-infile
    mysql –-local-infile -u root -p
    
    -- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
    set global local_infile = 1;
    
    -- 执行load指令将准备好的数据,加载到表结构中
    load data local infile '/root/sql1.log' into table tb_user fields
    terminated by ',' lines terminated by '\n' ;
    

    在这里插入图片描述

3.2 主键优化

在这里插入图片描述

行数据,都是存储在聚集索引的叶子节点上的。而我们之前也讲解过InnoDB的逻辑结构图:

在这里插入图片描述

在InnoDB引擎中,数据行是记录在逻辑结构 page 页中的,而每一个页的大小是固定的,默认16K。那也就意味着, 一个页中所存储的行也是有限的,如果插入的数据行row在该页存储不小,将会存储到下一个页中,页与页之间会通过指针连接。

3.2.1 页分裂

页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。

  1. 欲插入50

在这里插入图片描述

  1. 过程

在这里插入图片描述

  1. 但是47所在的1#页,已经写满了,存储不了50对应的数据了。 那么此时会开辟一个新的页 3#

在这里插入图片描述

  1. 但是并不会直接将50存入3#页,而是会将1#页后一半的数据,移动到3#页,然后在3#页,插入50。

在这里插入图片描述

在这里插入图片描述

  1. 调换页

在这里插入图片描述

3.2.2 页合并

  1. 如果逻辑删除(仅仅标记为删除,不是物理删除),达到阈值,默认页的50%

在这里插入图片描述

  1. 当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用

在这里插入图片描述

在这里插入图片描述

知识小贴士:

MERGE_THRESHOLD:合并页的阈值,可以自己设置,在创建表或者创建索引时指定。

3.2.3 主键设计原则

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

3.3 order by 优化

MySQL的排序,有两种方式:

  • Using filesort

    • 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sortbuffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
  • Using index

    • 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高 。
  1. 对于没有索引情况下的排序,一定是全表扫描,为index级速度(慢)
  2. 默认情况下的联合索引都是升序的。也就是
create index index_name on table(age asc,phone asc) #asc可以省略

​ 正例(索引有效)

select age from table order by age

select age,phone from table order by age,phone

select age from table order by age desc

​ 反例

select * from table order by ... #不管安排什么字段来排序,只有用*一定索引失效。
select age,name from table order by age
# 由于name字段是不包含在idx_age_name索引中所以无法使用索引,此时需要进行文件排序,即Extra: Using filesort
select age,name from table order by age,name desc #一升一降,部分失效

select age from table order by phone #需filesort辅助

总之

  • 大前提,select中的字段要求在索引中出现。
  • 借联合索引时,要遵循最左匹配原则,当然可以先用where age 后order by name,也是遵循的
  • 借联合索引,order by的要求字段,要么同时升序要么同时降序。
  • 额外信息中出现Using index和filesort,性能都是不太达标的!

想一升一降的排序,可以这样创建索引

create index index_name on table(age ,name desc)

如此一来,一升一降就不会导致索引( 部分 )失效

3.4 group by 优化

与order by一致,有索引时候会显著提升分组速度。

  • Using temporary
    • 使用临时表来分组,效率低
  • Using index
    • 使用索引分组

同样的,

  1. select应该只出现索引有的字段
  2. 最左匹配,允许先where匹配字段,后group by

3.5 limit优化

select * from table limit 100w,10

这样的越往后的分页,效率越低。

MySQL官方建议:

覆盖索引

select id from table order by id limit 100w,10
  • 如果没有order by id默认下是按照物理磁盘的存储顺序来显示数据的!而不是安排自增id,其实不存在页合并的话,那么查出来的就是id升序

使用连表查询出id的行数据

select * from tb_sku t , (select id from table order by id limit 100w,10) a where t.id = a.id;

3.6 count优化

上结论

count(*)≈count(1)>count(主键)>count(字段)

尽量使用count(*),MySQL专门优化了性能!

3.7 update优化

使用update会使用到InnoDB的锁机制。

锁是针对索引的,如果where中没有索引或索引失效,那么将会升级为表锁,性能大大降低

update ... where name='Jack'
#若name没有索引,则是表锁;有索引,则是行锁

另外,如果name='Jack’有多条记录,这么这多条记录都会施加行锁!

3.8 汇总

3~7 本质还是针对索引优化,因此掌握索引优化,就掌握了SQL优化的80%!!!

  • 插入数据
    • 批量插入、手动控制事务、主键顺序插入
  • 主键优化
    • 长度短、顺序插入,用auto_incremrnt而不是UUID
  • order by
    • using index 直接索引返回而不是Using filesort
  • group by
    • 多字段分组要满足最左前缀匹配
  • limit
    • 覆盖索引(无需回表)+子查询
  • count
    • count(字段)<count(主键id) < count(数字) ≈ count(*)
  • update
    • 尽量根据(where)主键/索引字段进行数据更新

4. 锁

4.1 锁的概述

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

在MySQL中锁分为三类

  • 全局锁 锁定数据库中的所有表。
  • 表级锁:每次操作锁住整张表。
  • 行级锁:每次操作锁住对应的行数据

查看锁

select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;

要其他手动开启的事务加锁之后,才能查看的到。

在这里插入图片描述

4.2 全局锁

介绍

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。

其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。

语法

  1. dos【1】
#登录到MySQL
flush tables with read lock ;
  1. dos【2】
#不要登录到MySQL,而是在MySQL的bin目录下的mysqldump.exe
mysqldump -uroot –p1234 itcast > itcast.sql
  1. 回到dos【1】
unlock tables ;# 释放全局锁

4.3 表级锁

表级锁,就是锁整张表。

4.3.1 介绍

  • 表锁
  • 元数据锁(meta data lock,MDL)
  • 意向锁

4.3.2 表锁

  • 表共享锁

    • 所有事务只能读不可写
    # 加锁
    lock tables table_name read;
    # 解锁
    unlock tables;
    
  • 表独占写锁

    • 只有事务自己可读可写,其他不可读亦不可写
    # 加锁
    lock tables table_name write;
    # 解锁
    unlock tables;
    

4.3.3 元数据锁

MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。**为了避免DML与DDL冲突,保证读写的正确性。**  ==说白了,就是修改表结构的锁==
  • MDL读
    • 在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);
    • 就是,事务有操作时,其他事务不能对表结构就行修改。
  • MDL写锁
    • 当对表结构进行变更操作的时候,加MDL写锁(排他)。
    • 当对表结构就行修改时,其他事务不能读写。

4.3.4 意向锁

为了解决行锁与表锁的冲突而出现,意向锁本质是表锁,用来标识该表的行锁情况,避免其他事务要遍历表来检查行锁。

  • 说说过程
    1. 当事务A进行增删改查 (查需要手动加共享锁) 的时候,会先对需要操作的行进行加锁。并且,InnoDB会对整张表加一个意向锁。
    2. 其他事务想加表锁,不再需要逐行扫描行锁了,直接根据意向锁来解决锁是否可以兼容加锁即可,极大提高了效率。
4.3.4.1 意向锁分类
  • 意向共享锁(IS):
    • 由语句select … lock in share mode添加 。
    • 与表锁共享锁(read)兼容,与表锁排他锁(write)互斥。
    • 手动添加
  • 意向排他锁(IX):
    • 由insert、update、delete、select…for update添加 。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。
    • 自动添加

4.4 行级锁

行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。

4.4.1 行锁分类

InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:

  • 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。

在这里插入图片描述

  • 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。

在这里插入图片描述

  • 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

在这里插入图片描述

4.4.2 InnoDB的行锁

  • 共享锁(S)
    • 允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁,但是可以自己执行增删改来把行升级成排它锁
  • 排他锁(X)
    • 允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。即,自己可以读写,其他事务只可以读
    • ==注意:==要与表锁区分,表锁的写锁,其他事务既不可读也不可写。

在这里插入图片描述

常见的SQL行锁情况:

SQL行锁类型说明
INSERT …排他锁自动加锁
UPDATE …排他锁自动加锁
DELETE …排他锁自动加锁
SELECT(正常)不加任何锁
SELECT … LOCK IN SHARE MODE共享锁之后执行增删改,则会自动升级成排它锁。
SELECT … FOR UPDATE排他锁需要手动在SELECT之后加FOR UPDATE
  • 默认情况下,InnoDB的隔离级别是 REPEATABLE READ 即可重复读,另外使用了 next-key 锁 (间隙锁) 进行搜索和索引扫描,以防止幻读 。

警告

  • 因为InnoDB的行锁是针对索引的,所以如果where字段中没有索引或者索引失效,那么将升级成表锁!!
  • 不管where用的是二级还是主键索引,最后统统优化成主键索引!

4.4.3 记录锁&间隙锁&临键锁

默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,为了好好理解锁的应用场景我们分情况讨论。

  • 记录锁:

    • 主键或唯一索引 等值 命中触发
  • 间隙锁

    • 使用索引,任
锁类型命中时机范围备注
记录锁主键或者唯一索引并等值,并命中被查出来的记录本身
间隙锁使用索引,不论等值/范围,未命中左开右开间隙锁,就只有间隙
临键锁使用索引,范围查询,并命中左闭右开记录锁+临键锁的结合

在这里插入图片描述

  • 间隙锁与临键锁有时候很混乱,直接给例子说明(后缀全是lock in share mode)

  • 命中记录时

    select ... where age = 5 #锁区间:  [1,5)  [5]  [5,10),即[1,10)
    

    其实这我不理解,为什么大家都说是左开右闭,但是我反复试验,age=1的记录 (id自增) 是无论如何都无法插入,我怀疑是临键锁。

    或者说,间隙锁不会单独出现,它虽然是左开右开,但是会与记录锁共同作用成临键锁。

select ... where age > 5 #锁区间:  [5,+∞)
select ... where age = 15 #锁区间:  [10,15)  [15]   [15,+∞)
  • 记录不存在时:

    select ... where age > 15 #锁区间:  [15,+∞)
    
    select ... where age > 1000 #锁区间:  [1000,+∞)	
    
    select ... where age = 1000 #锁区间:  [1000,+∞)
    
三种锁总结:
  • 当开启事务并加锁(共享/排它),锁的都是左闭右开!!

    • 左闭:到上一条记录,记录,没有则是==- ∞==

    • 右开:到下一条记录,不含记录,没有则是 + ∞

  • 注意:如果是age>n,

    • **实际上,**存在多条记录,那么将会把所有的age>n的记录当成参照体,重新以左闭右开进行加锁,即类似级联加锁。
    • 但是,可以简单理解成:[n,+∞)

5. InnoDB引擎

理解为主

5.1 逻辑存储结构

在这里插入图片描述

参考 1.1 MySQL体系架构

5.2 架构

在这里插入图片描述

5.2.1 内存架构

在这里插入图片描述

  • Buffer Pool

    • InnoDB存储引擎基于磁盘文件存储,访问物理硬盘和在内存中进行访问,速度相差很大,为了尽可能弥补这两者之间的I/O效率的差值,就需要把经常使用的数据加载到缓冲池中,避免每次访问都进行磁盘I/O。
    • 在InnoDB的缓冲池中不仅缓存了索引页和数据页,还包含了undo页、插入缓存、自适应哈希索引以及InnoDB的锁信息等等。
    • 缓冲池 Buffer Pool,是主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后再以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度。
    • 缓冲池以Page页为单位,底层采用链表数据结构管理Page。根据状态,将Page分为三种类型:
      • free page:空闲page,未被使用。
      • clean page:被使用page,数据没有被修改过。
      • dirty page:脏页,被使用page,数据被修改过,也中数据与磁盘的数据产生了不一致。
  • Change Buffer

    • Change Buffer,更改缓冲区(针对于非唯一二级索引页),在执行DML语句时,如果这些数据Page没有在Buffer Pool中,不会直接操作磁盘,而会将数据变更存在更改缓冲区 Change Buffer中,在未来数据被读取时,再将数据合并恢复到Buffer Pool中,再将合并后的数据刷新到磁盘中。
  • Adaptive Hash Index

    • 自适应hash索引,用于优化对Buffer Pool数据的查询。MySQL的innoDB引擎中虽然没有直接支持hash索引,但是给我们提供了一个功能就是这个自适应hash索引。因为前面我们讲到过,hash索引在进行等值匹配时,一般性能是要高于B+树的,因为hash索引一般只需要一次IO即可,而B+树,可能需要几次匹配,所以hash索引的效率要高,但是hash索引又不适合做范围查询、模糊匹配等。
    • InnoDB存储引擎会监控对表上各索引页的查询,如果观察到在特定的条件下hash索引可以提升速度,则建立hash索引,称之为自适应hash索引。
    • 自适应哈希索引,无需人工干预,是系统根据情况自动完成。
    • 参数: adaptive_hash_index
  • Log Buffer

    • Log Buffer:日志缓冲区,用来保存要写入到磁盘中的log日志数据(redo log 、undo log),默认大小为 16MB,日志缓冲区的日志会定期刷新到磁盘中。如果需要更新、插入或删除许多行的事务,增加日志缓冲区的大小可以节省磁盘 I/O。
    • 参数:
      • innodb_log_buffer_size:缓冲区大小
      • innodb_flush_log_at_trx_commit:日志刷新到磁盘时机,取值主要包含以下三个:
      • 1: 日志在每次事务提交时写入并刷新到磁盘,默认值。
      • 0: 每秒将日志写入并刷新到磁盘一次。
      • 2: 日志在每次事务提交后写入,并每秒刷新到磁盘一次。

5.2.2 磁盘结构

在这里插入图片描述

  • System Tablespace

  • 系统表空间是更改缓冲区的存储区域。如果表是在系统表空间而不是每个表文件或通用表空间中创建的,它也可能包含表和索引数据。(在MySQL5.x版本中还包含InnoDB数据字典、undolog等)参数:innodb_data_file_path

  • File-Per-Table Tablespaces

    • 如果开启了innodb_file_per_table开关 ,则每个表的文件表空间包含单个InnoDB表的数据和索引 ,并存储在文件系统上的单个数据文件中。
    • 开关参数:innodb_file_per_table ,该参数默认开启。
  • General Tablespaces

    • 通用表空间,需要通过 CREATE TABLESPACE 语法创建通用表空间,在创建表时,可以指定该表空间
  • Undo Tablespaces

    • 撤销表空间,MySQL实例在初始化时会自动创建两个默认的undo表空间(初始大小16M),用于存储undo log日志。
  • Temporary Tablespaces

  • InnoDB 使用会话临时表空间和全局临时表空间。存储用户创建的临时表等数据。

  • Doublewrite Buffer Files

    • 1 CREATE TABLESPACE ts_name ADD DATAFILE ‘file_name’ ENGINE = engine_name;
    • 1 CREATE TABLE xxx … TABLESPACE ts_name;
    • 双写缓冲区,innoDB引擎将数据页从Buffer Pool刷新到磁盘前,先将数据页写入双写缓冲区文件中,便于系统异常时恢复数据。
  • Redo Log

    • 重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo logbuffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中, 用于在刷新脏页到磁盘时,发生错误时, 进行数据恢复使用。

5.2.3 守护线程

有多个线程池,线程池有多个线程。用来内存与磁盘的后台IO操作。

在这里插入图片描述

5.3 事务原理

在这里插入图片描述

  • 对于这四大特性,实际上分为两个部分。 其中的原子性、一致性、持久化,实际上是由InnoDB中的两份日志来保证的,一份是redo log日志,一份是undo log日志。 而持久性是通过数据库的锁,加上MVCC来保证的。

5.3.1 redo log

重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo logfile),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中, 用于在刷新脏页到磁盘,发生错误时, 进行数据恢复使用。

如果没有redolog,可能会存在什么问题的? 我们一起来分析一下。我们知道,在InnoDB引擎中的内存结构中,主要的内存区域就是缓冲池,在缓冲池中缓存了很多的数据页。 当我们在一个事务中,执行多个增删改的操作时,InnoDB引擎会先操作缓冲池中的数据,如果缓冲区没有对应的数据,会通过后台线程将磁盘中的数据加载出来,存放在缓冲区中,然后将缓冲池中的数据修改,修改后的数据页我们称为脏页。 而脏页则会在一定的时机,通过后台线程刷新到磁盘中,从而保证缓冲区与磁盘的数据一致。 而缓冲区的脏页数据并不是实时刷新的,而是一段时间之后将缓冲区的数据刷新到磁盘中,假如刷新到磁盘的过程出错了,而提示给用户事务提交成功,而数据却没有持久化下来,这就出现问题了,没有保证事务的持久性。

在这里插入图片描述

5.3.2 undo log

回滚日志,用于记录数据被修改前的信息 , 作用包含两个 : 提供回滚(保证事务的原子性) 和
MVCC(多版本并发控制) 。

undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undolog中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。

5.4 MVCC

5.4.1 基本概念

  • 当前读

    • 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select … lock in share mode(共享锁),select …for update、update、insert、delete(排他锁)都是一种当前读。
  • 快照读

    • 简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
      • • Read Committed:每次select,都生成一个快照读。
      • • Repeatable Read:开启事务后第一个select语句才是快照读的地方。
      • • Serializable:快照读会退化为当前读。
  • MVCC

    • 全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。
  • 隐藏字段

隐藏字段含义
DB_TRX_ID最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
DB_ROLL_PTR回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版 本。
DB_ROW_ID隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。

利用好undo log,旧数据会放到undo log日志中,并做好链表。

在这里插入图片描述

之后就可以根据日志与readview来保证可重复读。

6.MySQL管理

6.1 系统数据库

Mysql数据库安装完成后,自带了一下四个数据库,具体作用如下:

数据库含义
mysql存储MySQL服务器正常运行所需要的各种信息 (时区、主从、用 户、权限等)
information_schema提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类 型及访问权限等
performance_schema为MySQL服务器运行时状态提供了一个底层监控功能,主要用于收集 数据库服务器性能参数
sys包含了一系列方便 DBA 和开发人员利用 performance_schema 性能数据库进行性能调优和诊断的视图

6.2 常用工具

6.2.1 mysql

该mysql不是指mysql服务,而是指mysql的客户端工具。

  • 语法
    • mysql [options] [database]
  • 选项
    • -u, --user=name #指定用户名
    • -p, --password[=name] #指定密码
    • -h, --host=name #指定服务器IP或域名
    • -P, --port=port #指定连接端口
    • -e, --execute=name #执行SQL语句并退出

-e选项可以在Mysql客户端执行SQL语句,而不用连接到MySQL数据库再执行,对于一些批处理脚本,这种方式尤其方便。

6.2.2 mysqldump

用来备份数据库或者进行数据库的迁移,包含创建表、插入表、SQL语句等。

  • 语法:

    • mysqldump [options] db_name
  • 选项

    option含义
    -u用户名
    -p密码
    -h服务器ip, localhost默认
    -p连接端口, 3306默认
  • 输出选项

    option含义
    -n剔除数据创建语句
    -t剔除数据创建语句
    -d剔除数据
    -T生成两文件,一个.sql表结构,一个.txt数据文件非insert

常规使用:

  • 完全导出db_name数据库
mysqldump -uroot -p12345 db_name > d:/dn_name.sql
  • 剔除表结构
mysqldump -uroot -p12345 -t db_name > d:/dn_name.sql
  • 分开备份

    • 查看MySQL信任的导出路径

      show variables like 'secure_file_priv'
      # var/lib/mysql-file/
      
    • 导出

      mysqldump -uroot -p12345 -T /var/lib/mysql-files/ db_name file_name
      
    • 一个file_name.sql放表结构文件,file_name.txt放数据文件,注意这个数据文件不是insert,需要 mysqlimport/source 导入,或者load批量插入。

6.2.3 mysqlimport/source

  • mysqlimport

    用来导入mysqldump导出的file_name.txt数据文件。

    mysqlimport -uroot -p12345 db_name /var/file_name.txt
    
  • source

    如果需导入sql文件,可以使用mysql中的source指令来进行导入:

    source /root/file_name.sql
    
MySQL 进阶实战》是一本探讨MySQL数据库高级应用和实战技巧的书籍。MySQL是一种广泛使用的开源关系型数据库管理系统,在Web开发和数据分析等领域有着重要的应用。 《MySQL 进阶实战》将帮助读者理解MySQL的高级功能和最佳实践,进一步优化数据库的性能和稳定性。本书主要内容包括: 1. 高级查询优化:介绍如何使用索引、列存储和分区技术来提高查询性能,避免慢查询和死锁等问题。 2. 数据库设计与架构:详细讲解如何设计合理的数据库模型,包括表关系、数据类型和字段命名规范,以及应对高并发和大数据量的应用场景。 3. 高可用与备份恢复:介绍如何设置主从复制、集群和故障切换,确保数据库的高可用性和数据安全。 4. 性能调优与监控:介绍如何使用工具和技术调优MySQL数据库的性能,包括查询优化、缓存设置和资源管理等方面。 5. 安全与权限管理:详细讲解如何设置数据库的用户权限、访问控制和加密保护,防止非法访问和数据泄露。 6. MySQL新特性与实践:介绍新版本MySQL的特性和改进,以及如何应用到实际项目中。 《MySQL 进阶实战》不仅适合已经具有一定MySQL基础知识的读者,也可以作为MySQL数据库开发和运维人员的参考手册。通过读此书,读者将能够更深入地了解和应用MySQL数据库,在实际项目中提高开发和管理效率,提升数据库的性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值