MySQL进阶 —— 超详细操作演示!!!(下)

数据库系列文章:

关系型数据库 — MySQL:


非关系型数据库 — Redis:

五、锁

5.1 概述

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

MySQL中的锁,按照锁的粒度分,分为以下三类:

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

5.2 全局锁

⭐️ 1). 介绍

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

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

为什么全库逻辑备份,就需要加全局锁呢?

A. 我们一起先来分析一下不加全局锁,可能存在的问题。

  • 假设在数据库中存在这样三张表: tb_stock 库存表tb_order 订单表tb_orderlog 订单日志表

在这里插入图片描述

  • 在进行数据备份时,先备份了tb_stock 库存表
  • 然后接下来,在业务系统中,执行了 下单操作,扣减库存,生成订单(更新 tb_stock表,插入 tb_order表)。
  • 然后再执行备份 tb_order 表的逻辑。
  • 业务中执行 插入订单日志 操作。
  • 最后,又备份了 tb_orderlog 表。

此时备份出来的数据,是存在问题的。因为备份出来的数据,tb_stock 表与 tb_order 表的数据不一致(有最新操作的订单信息,但是库存数没减)。

那如何来规避这种问题呢?

  • 此时就可以借助于MySQL的 全局锁 来解决。

B. 再来分析一下加了全局锁后的情况
在这里插入图片描述
对数据库进行进行 逻辑备份 之前,先对整个数据库加上 全局锁 ,一旦加了全局锁之后,其他的 DDL
DML 全部都处于 阻塞状态,但是可以执行 DQL语句,也就是处于 只读状态,而数据备份 就是 查询操作

那么数据在进行逻辑备份的过程中,数据库中的数据就是不会发生变化的,这样就保证了数据的 一致性完整性

⭐️ 2). 语法

a). 加全局锁

flush tables with read lock ;

b). 数据备份

## 不是SQL语句,要在win的命令行中执行!
mysqldump -h 192.168.200.2 -uroot –p1234 rmzh > D:/rmzh.sql
  • 数据备份的相关指令, 在后面MySQL管理章节, 还会详细讲解.

c). 释放锁

unlock tables ;

⭐️ 3). 特点

数据库中加全局锁,是一个比较重的操作,存在以下问题:

  • 如果在 主库 上备份,那么在备份期间都不能执行更新,业务基本上就得停摆
  • 如果在 从库 上备份,那么在备份期间从库不能执行主库同步过来的二进制日志binlog ),会导致主从延迟

InnoDB引擎 中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁一致性 数据备份。

## 不是SQL语句,要在win的命令行中执行!
mysqldump --single-transaction -uroot –p1234 rmzh > rmzh.sql

5.3 表级锁

⭐️ 1). 介绍

表级锁,每次操作 锁住整张表

  • 锁定粒度大发生锁冲突的概率最高并发度最低
  • 应用在 MyISAMInnoDBBDB 等存储引擎中。

对于表级锁,主要分为以下三类:

  • 表锁
  • 元数据锁meta data lockMDL
  • 意向锁

⭐️ 2). 表锁

对于表锁,分为两类:

  • 共享读锁read lock
  • 独占写锁write lock

语法:

  • 加锁lock tables 表名... read/write
  • 释放锁unlock tables / (客户端断开连接)

特点:

A. 读锁
在这里插入图片描述

  • 左侧为客户端一,对指定表加了读锁不会影响右侧客户端二的,但是会 阻塞右侧客户端的

测试:
在这里插入图片描述

B. 写锁
在这里插入图片描述

  • 左侧为客户端一,对指定表加了写锁,会 阻塞右侧客户端的读和写

测试:
在这里插入图片描述

结论:

  • 读锁 不会阻塞其他客户端的,但是会阻塞写
  • 写锁 既会阻塞其他客户端的,又会阻塞其他客户端的

⭐️ 3). 元数据锁

meta data lock , 元数据锁,简写 MDL

  • MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。
  • MDL锁主要作用是维护表元数据的数据 一致性,在表上有活动事务的时候,不可以对元数据进行写入操作
  • 为了避免 DML与DDL冲突,保证读写的正确性

这里的 元数据,大家可以简单理解为就是 一张表的表结构。 也就是说,某一张表涉及到 未提交的事务 时,是不能够修改这张表的表结构的。

在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加 MDL读锁(共享);当对表结构进行变更操作的时候,加 MDL写锁 (排他)。

常见的SQL操作时,所添加的元数据锁:
在这里插入图片描述
演示:

  • 当执行 SELECTINSERTUPDATEDELETE 等语句时,添加的是元数据共享锁(SHARED_READ / SHARED_WRITE),之间是 兼容 的。

在这里插入图片描述

  • 当执行 SELECT 语句时,添加的是 元数据共享锁SHARED_READ),会阻塞元数据排他锁EXCLUSIVE),之间是 互斥 的。

在这里插入图片描述

我们可以通过下面的SQL,来查看数据库中的 元数据锁 的情况:

select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks ;

我们在操作过程中,可以通过上述的SQL语句,来查看元数据锁的加锁情况。
在这里插入图片描述

⭐️ 4). 意向锁

a). 介绍

  • 为了避免DML在执行时,加的 行锁表锁 的冲突,在InnoDB中引入了 意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查

假如没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢,来通过示意图简单分析一下:

  • 首先客户端一,开启一个事务,然后执行DML操作,在执行DML语句时,会对涉及到的行加行锁
    在这里插入图片描述
  • 当客户端二,想对这张表加表锁时,会检查当前表是否有对应的行锁,如果没有,则添加表锁,此时就会从第一行数据,检查到最后一行数据,效率较低。
    在这里插入图片描述

有了意向锁之后 :

  • 客户端一,在执行DML操作时,会对涉及的行加行锁,同时也会对该表加上意向锁
    在这里插入图片描述
  • 而其他客户端,在对这张表加表锁的时候,会根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况了。
    在这里插入图片描述

b). 分类

  • 意向共享锁(IS): 由语句 select ... lock in share mode 添加 。 与 表锁共享锁( read )兼容,与表锁排他锁( write )互斥
  • 意向排他锁(IX): 由 insertupdatedeleteselect...for update 添加 。与表锁共享锁( read )及 排他锁( write )都互斥意向锁之间不会互斥

一旦事务提交了,意向共享锁、意向排他锁,都会自动释放

可以通过以下SQL,查看意向锁行锁加锁情况

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

演示:

  • A. 意向共享锁表读锁兼容
    在这里插入图片描述

  • B. 意向排他锁表读锁写锁 都是 互斥
    在这里插入图片描述

5.4 行级锁

⭐️ 1). 介绍

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

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

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

⭐️ 2). 行锁

a). 介绍

InnoDB实现了以下两种类型的行锁:

  • 共享锁S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁
  • 排他锁X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁排他锁

两种行锁的兼容情况如下:
在这里插入图片描述
常见的SQL语句,在执行时,所加的行锁如下:

在这里插入图片描述
b). 演示

默认情况下,InnoDBREPEATABLE READ 事务隔离级别运行,InnoDB 使用 next-key 锁进行搜索和索引扫描,以防止幻读。

  • 针对 唯一索引 进行检索时,对已存在的记录进行等值匹配时,将会自动优化为 行锁
  • InnoDB行锁 是针对于 索引加的锁,不通过索引条件检索数据,那么 InnoDB 将对表中的所有记录加锁,此时 就会升级为 表锁

可以通过以下SQL,查看意向锁及行锁的加锁情况:

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

示例演示

数据准备:

CREATE TABLE `stu` (
`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int NOT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4;
INSERT INTO `stu` VALUES (1, 'tom', 1);
INSERT INTO `stu` VALUES (3, 'cat', 3);
INSERT INTO `stu` VALUES (8, 'rose', 8);
INSERT INTO `stu` VALUES (11, 'jetty', 11);
INSERT INTO `stu` VALUES (19, 'lily', 19);
INSERT INTO `stu` VALUES (25, 'luci', 25);

演示行锁的时候,我们就通过上面这张表来演示一下。

  • A. 普通的 select 语句,执行时,不会加锁

在这里插入图片描述

  • B. select...lock in share mode加共享锁共享锁共享锁之间兼容

在这里插入图片描述

  • 共享锁排他锁 之间互斥

在这里插入图片描述

客户端一获取的是 id1 这行的共享锁,客户端二是可以获取 id3 这行的 排它锁 的,因为不是同一行数据。 而如果客户端二想获取 id1 这行的 排他锁,会处于==阻塞状态==,因为为共享锁排他锁之间互斥

  • C. 排它锁排他锁 之间 互斥

在这里插入图片描述

当客户端一,执行 update 语句,会为 id1 的记录加 排他锁; 客户端二,如果也执行 update 语句更新 id1 的数据,也要为 id1 的数据加排他锁,但是客户端二会处于 阻塞状态,因为排他锁之间是互斥的。 直到客户端一,把事务提交了,才会把这一行的行锁释放,此时客户端二,解除阻塞。

  • D. 无索引行锁 升级为表锁
    • stu表中数据如下:

在这里插入图片描述

  • 我们在两个客户端中执行如下操作:

在这里插入图片描述

在客户端一中,开启事务,并执行 update 语句,更新 nameLily 的数据,也就是 id19 的记录 。然后在客户端二中更新 id3 的记录,却不能直接执行,会处于阻塞状态,为什么呢?

  • 原因就是因为此时,客户端一,根据 name 字段进行更新时,name 字段是没有索引的如果没有索引,此时行锁会升级为表锁(因为行锁是对索引项加的锁,而 name 没有索引)。
  • 接下来,我们再针对name字段建立索引,索引建立之后,再次做一个测试:

在这里插入图片描述

此时我们可以看到,客户端一,开启事务,然后依然是根据 name 进行更新。而客户端二,在更新 id3 的数据时,更新成功,并 未进入 阻塞状态。 这样就说明,我们根据索引字段进行更新操作,就可以 避免 行锁 升级为 表锁 的情况。

⭐️ 3). 间歇锁 & 临键锁

默认情况下,InnoDBREPEATABLE READ 事务隔离级别运行,InnoDB 使用 next-key 锁进行搜索和索引扫描,以防止幻读

  • 索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁
  • 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为 间隙锁
  • 索引上的范围查询(唯一索引) – 会访问到不满足条件的第一个值为止。

注意:间隙锁 唯一目的是防止其他事务插入间隙。间隙锁 可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。

示例演示

  • A. 索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁

在这里插入图片描述

  • B. 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁

介绍分析一下:
我们知道InnoDB的B+树索引,叶子节点是有序的双向链表。 假如,我们要根据这个二级索引查询值
为18的数据,并加上共享锁,我们是只锁定18这一行就可以了吗? 并不是,因为是非唯一索引,这个
结构中可能有多个18的存在,所以,在加锁时会继续往后找,找到一个不满足条件的值(当前案例中也
就是29)。此时会对18加临键锁,并对29之前的间隙加锁。

在这里插入图片描述

在这里插入图片描述

  • C. 索引上的范围查询(唯一索引) – 会访问到不满足条件的第一个值为止。

在这里插入图片描述

查询的条件为 id>=19,并添加共享锁。 此时我们可以根据数据库表中现有的数据,将数据分为三个部
分:
[19]
(19,25]
(25,+∞]
所以数据库数据在加锁是,就是将19加了行锁,25的临键锁(包含25及25之前的间隙),正无穷的临键锁(正无穷及之前的间隙)。

🚀🚀🚀 锁 快速食用:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

## 1、概述
-- 在并发访问时,解决数据访问的一致性、有效性问题
-- 全局锁、表级锁、行级锁

## 2、全局锁
-- 对整个数据库实例加锁,加锁后整个实例就处于只读状态
-- 性能较差,数据逻辑备份时使用

## 3、表级锁
-- 操作锁住整张表,锁定粒度大,发生锁冲突的概率高
-- 表锁、元数据锁、意向锁

## 4、行级锁
-- 操作锁住对应的行数据,锁定粒度最小,发生锁冲突的概率最低
-- 行锁、间隙锁、临键锁

六、InnoDB 引擎

6.1 逻辑存储结构

InnoDB的 逻辑存储 结构如下图所示:

在这里插入图片描述
1). 表空间

  • 表空间InnoDB 存储引擎 逻辑结构的最高层;
  • 如果用户启用了参数 innodb_file_per_table (在8.0版本中默认开启) ,则每张表都会有一个 表空间xxx.ibd),一个 mysql 实例可以 对应 多个表空间,用于存储记录索引等数据。
    在这里插入图片描述

2). 段

  • ,分为数据段Leaf node segment )、索引段Non-leaf node segment)、回滚段Rollback segment);
  • InnoDB索引组织表数据段就是 B+树叶子节点索引段即为B+树的 非叶子节点
  • 段用来管理多个 Extent(区)。

3). 区

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

4). 页

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

5). 行

  • InnoDB 存储引擎 数据是按行进行存放的
  • 在行中,默认有两个隐藏字段
    • Trx_id:每次对某条记录进行改动时,都会把对应的事务 id 赋值给 trx_id 隐藏列
    • Roll_pointer:每次对某条引记录进行改动时,都会把旧的版本写入到 undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

6.2 架构

⭐️ 1). 概述

  • MySQL5.5 版本开始,默认使用 InnoDB 存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。

下面是 InnoDB 架构图,左侧为内存结构,右侧为磁盘结构

在这里插入图片描述

⭐️ 2). 内存结构

  • 在左侧的内存结构中,主要分为这么四大块儿: Buffer Pool(缓冲池)、Change BufferAdaptive Hash IndexLog Buffer。 接下来介绍一下这四个部分。
    在这里插入图片描述

a). 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,数据被修改过,也中数据磁盘的数据产生了不一致。

在专用服务器上,通常将多达80%的物理内存分配给缓冲池 。参数设置: show variables like 'innodb_buffer_pool_size';

b). Change Buffer

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

Change Buffer 的意义是什么呢?

  • 先来看一幅图,这个是二级索引的结构图:

在这里插入图片描述

聚集索引不同,二级索引通常是非唯一的,并且以相对随机的顺序插入二级索引。
同样,删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作磁盘,会造成大量的磁盘IO。有了Change Buffer 之后,我们可以在缓冲池中进行合并处理,减少磁盘IO。

c). Adaptive Hash Index

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

自适应哈希索引,无需人工干预,是系统根据情况自动完成

  • 参数: adaptive_hash_index
    在这里插入图片描述

d). Log Buffer

  • Log Buffer日志缓冲区,用来保存要写入到磁盘中的 log日志数据(redo logundo log),默认大小为 16MB,日志缓冲区的日志会定期刷新到磁盘中。
  • 如果需要更新插入删除许多行的事务,增加日志缓冲区的大小可以节省磁盘 I/O。

参数:

  • innodb_log_buffer_size:缓冲区大小
  • innodb_flush_log_at_trx_commit:日志刷新到磁盘时机,取值主要包含以下三个:
    • 1: 日志在每次事务提交时写入并刷新到磁盘,默认值
    • 0: 每秒将日志写入并刷新到磁盘一次。
    • 2: 日志在每次事务提交后写入,并每秒刷新到磁盘一次。

在这里插入图片描述

⭐️ 3). 磁盘结构

  • 接下来,再来看看InnoDB体系结构的右边部分,也就是磁盘结构

在这里插入图片描述
a). System Tablespace

  • 系统表空间更改缓冲区的存储区域。

  • 如果表是在 系统表空间 而不是每个表文件或通用表空间中创建的,它也可能包含索引数据。(在MySQL5.x版本中还包含 InnoDB数据字典undolog 等)

  • 参数innodb_data_file_path

  • 系统表空间,默认的文件名叫 ibdata1

b). File-Per-Table Tablespaces

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

  • 开关参数innodb_file_per_table ,该参数默认开启

  • 那也就是说,我们每创建一个表,都会产生一个表空间文件,如图:

c). General Tablespaces

  • 通用表空间,需要通过 CREATE TABLESPACE 语法创建通用表空间,在创建表时,可以指定该表空间
  • A. 创建表空间
CREATE TABLESPACE ts_name ADD DATAFILE 'file_name' ENGINE = engine_name;
  • B. 创建表时指定表空间
CREATE TABLE xxx ... TABLESPACE ts_name;

d). Undo Tablespaces

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

e). Temporary Tablespaces

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

f). Doublewrite Buffer Files

  • 双写缓冲区innoDB 引擎将数据页从 Buffer Poo l刷新到磁盘前,先将数据页写入双写缓冲区文件中,便于系统异常时恢复数据

g). Redo Log

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

前面我们介绍了InnoDB的内存结构,以及磁盘结构,那么内存中我们所更新的数据,又是如何到磁盘中的呢?

  • 此时,就涉及到一组 后台线程,接下来,就来介绍一些InnoDB中涉及到的 后台线程

在这里插入图片描述

⭐️ 4). 后台线程

在这里插入图片描述
InnoDB后台线程中,分为4类,分别是:Master ThreadIO ThreadPurge ThreadPage Cleaner Thread

a). Master Thread

  • 核心后台线程,负责调度其他线程,还负责将缓冲池中的数据异步刷新到磁盘中, 保持数据的一致性,还包括脏页的刷新合并插入缓存undo页的回收

b). IO Thread

  • InnoDB 存储引擎中大量使用了 AIO(异步非阻塞IO) 来处理IO请求, 这样可以极大地提高数据库的性能,而 IOThread主要负责这些 IO请求的回调
    在这里插入图片描述
  • 我们可以通过以下的这条指令,查看到 InnoDB 的状态信息,其中就包含 IO Thread 信息。
show engine innodb status \G;

在这里插入图片描述

c). Purge Thread

  • 主要用于回收 事务已经提交了的 undo log,在事务提交之后,undo log 可能不用了,就用它来回收。

d). Page Cleaner Thread

  • 协助 Master Thread 刷新脏页到磁盘的线程,它可以减轻 Master Thread 的工作压力,减少阻塞

6.3 事务原理

⭐️ 1). 事务基础
a). 事务

  • 事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统 提交撤销操作请求,即这些操作 要么同时成功,要么同时失败

b). 特性

  • 原子性Atomicity ):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性Consistency ):事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性Isolation ):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性Durability ):事务一旦提交回滚,它对数据库中的数据的改变就是永久的

那实际上,我们研究事务的原理,就是研究MySQL的InnoDB引擎是如何保证事务的这四大特性的。
在这里插入图片描述
而对于这四大特性,实际上分为两个部分。

  • 其中的 原子性一致性持久化,实际上是由InnoDB中的两份日志来保证的,一份是 redo log日志,一份是undo log 日志。
  • 隔离性 是通过数据库的锁,加上 MVCC 来保证的。

在这里插入图片描述
我们在讲解事务原理的时候,主要就是来研究一下 redologundolog 以及 MVCC

⭐️ 2). redo log

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

如果没有 redo log,可能会存在什么问题的? 我们一起来分析一下。

我们知道,在InnoDB引擎中的内存结构中,主要的内存区域 就是 缓冲池,在缓冲池中缓存了很多的数据页。

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

在这里插入图片描述
那么,如何解决上述的问题呢? 在InnoDB中提供了一份日志 redo log,接下来我们再来分析一下,通过 redolog 如何解决这个问题。

在这里插入图片描述

有了redolog 之后,当对缓冲区的数据进行增删改之后,会首先将操作的 数据页的变化,记录在 redo log buffer 中。在事务提交时,会将 redo log buffer 中的数据刷新到 redo log 磁盘文件中。
过一段时间之后,如果 刷新缓冲区脏页 到磁盘时,发生错误,此时就可以借助于redo log 进行数据恢复,这样就 保证了事务的持久性
而如果脏页成功刷新到磁盘涉及到的数据已经落盘,此时 redo log 就没有作用了,就可以删除了,所以存在的两个redo log 文件是循环写的

那为什么每一次提交事务,要刷新 redo log 到磁盘中呢,而不是直接将 buffer pool 中的脏页刷新到磁盘呢 ?

  • 因为在业务操作中,我们操作数据一般都是随机读写磁盘的,而不是顺序读写磁盘。 而redo log在往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的顺序写的效率,要远大于随机写
  • 这种 先写日志 的方式,称之为 WALWrite-Ahead Logging)。

⭐️ 3). undo log

  • 回滚日志,用于记录数据被 修改前的信息 , 作用包含两个 : 提供回滚 (保证事务的 原子性) 和MVCC(多版本并发控制) 。
  • undo logredo log 记录 物理日志 不一样,它是 逻辑日志。可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,当 update 一条记录时,它记录一条对应相反的 update 记录。当执行 rollback 时,就可以从 undo log 中的逻辑记录读取到相应的内容并进行回滚。
  • Undo log 销毁undo log事务执行时产生,事务提交时,并不会立即删除 undo log,因为这些日志可能还用于 MVCC
  • Undo log 存储undo log 采用 段的方式 进行管理和记录,存放在前面介绍的 rollback segment 回滚段 中,内部包含1024个 undo log segment

6.4 MVCC —— 多版本并发控制

⭐️ 1). 基本概念

a). 当前读

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

测试:

在这里插入图片描述

在测试中我们可以看到,即使是在默认的 RR隔离级别 下,事务A中依然可以读取到事务B最新提交的内容,因为在查询语句后面加上了 lock in share mode 共享锁,此时是当前读操作。当然,当我们加排他锁的时候,也是当前读操作。

b). 快照读

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

测试:

在这里插入图片描述

在测试中,我们看到即使 事务B提交了数据,事务A中也查询不到

  • 原因就是因为普通的 select快照读,而在当前默认的 RR隔离级别下,开启事务后第一个 select 语句才是快照读的地方,后面执行相同的 select 语句都是 从快照中获取数据,可能不是当前的最新数据,这样也就 保证了可重复读

c). MVCC

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

接下来,我们再来介绍一下InnoDB引擎的表中涉及到的 隐藏字段undolog 以及 readview,从而来介绍一下MVCC的原理。

⭐️ 2). 隐藏字段

a). 介绍

在这里插入图片描述

  • 当我们创建了上面的这张表,我们在查看表结构的时候,就可以显式的看到这三个字段。 实际上除了这三个字段以外,InnoDB还会自动的给我们添加 三个隐藏字段 及其含义分别是:

在这里插入图片描述
而上述的前两个字段是肯定会添加的, 是否添加最后一个字段 DB_ROW_ID,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。

b). 测试:

查看有主键的表 stu

  • 进入服务器中的 /var/lib/mysql/rmzh/ , 查看stu的表结构信息, 通过如下指令:
ibd2sdi stu.ibd

在这里插入图片描述

  • 查看到的表结构信息中,有一栏 columns,在其中我们会看到处理我们建表时指定的字段以外,还有额外的两个字段 分别是:DB_TRX_IDDB_ROLL_PTR ,因为该表有主键,所以没有 DB_ROW_ID 隐藏字段。

在这里插入图片描述

c). 查看没有主键的表 employee

  • 建表语句:
create table employee (id int , name varchar(10));
  • 此时,我们再通过以下指令来查看表结构及其其中的字段信息:
ibd2sdi employee.ibd

在这里插入图片描述

  • 查看到的表结构信息中,有一栏 columns,在其中我们会看到处理我们建表时指定的字段以外,还有额外的三个字段 分别是:DB_TRX_ID DB_ROLL_PTRDB_ROW_ID,因为 employee 表是没有指定主键的。

⭐️ 3). undolog

a). 介绍

  • 回滚日志,在 insertupdatedelete 的时候产生的便于数据回滚的日志。
  • insert 的时候,产生的 undo log 日志只在回滚时需要,在事务提交后,可被 立即删除
  • updatedelete 的时候,产生的 undo log 日志不仅在回滚时需要,在快照读时也需要,不会立即被删除

b). 版本链

有一张表原始数据为:
在这里插入图片描述

DB_TRX_ID : 代表最近 修改事务ID,记录插入这条记录最后一次修改该记录的事务ID,是自增的。
DB_ROLL_PTR : 由于这条数据是才插入的,没有被更新过,所以该字段值为 null

然后,有四个并发事务同时在访问这张表。

  • A. 第一步

在这里插入图片描述
事务2 执行第一条修改语句时,会记录 undo log 日志,记录数据变更之前的样子; 然后更新记录,且记录本次操作的 事务ID回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。

在这里插入图片描述
B.第二步

在这里插入图片描述
事务3 执行第一条修改语句时,也会记录 undo log 日志,记录数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。

在这里插入图片描述
C. 第三步

在这里插入图片描述
当事务4执行第一条修改语句时,也会记录 undo log 日志,记录数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。

在这里插入图片描述

最终我们发现,不同事务相同事务 对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。

⭐️ 4). readview

  • ReadView读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统 当前活跃的事务未提交的id

  • ReadView中包含了四个核心字段:

在这里插入图片描述
而在 readview 中就规定了 版本链 数据的访问规则:

  • trx_id 代表当前 undolog 版本链 对应 事务ID
    在这里插入图片描述

不同的隔离级别,生成 ReadView 的时机不同:

  • READ COMMITTED :在事务中 每一次 执行 快照读 时生成 ReadView
  • REPEATABLE READ:仅在事务中第一次执行 快照读 时生成 ReadView后续复用ReadView

⭐️ 5). 原理分析

a). RC隔离级别

  • RC隔离级别下,在事务中每一次执行 快照读 时生成 ReadView

我们就来分析事务5中,两次快照读读取数据,是如何获取数据的?

  • 在事务5中,查询了两次 id 为30的记录,由于隔离级别为 Read Committed,所以每一次进行 快照读 都会生成一个 ReadView,那么两次生成的 ReadView 如下。

在这里插入图片描述

  • 那么这两次 快照读 在获取数据时,就需要根据所生成的 ReadView 以及 ReadView版本链访问规则,到 undo log版本链中匹配数据,最终决定此次快照读返回的数据。

A. 先来看第一次快照读具体的读取过程:
在这里插入图片描述

  • 在进行匹配时,会从 undo log版本链,从上到下进行挨个匹配:

在这里插入图片描述

在这里插入图片描述

B. 再来看第二次快照读具体的读取过程:

在这里插入图片描述

在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:

在这里插入图片描述
b). RR隔离级别

  • RR隔离级别下,仅在事务中第一次执行快照读时生成 ReadView,后续复用该ReadView。 而 RR可重复读,在一个事务中,执行两次相同的 select 语句,查询到的结果是一样的。

那MySQL是如何做到可重复读的呢? 我们简单分析一下就知道了

在这里插入图片描述

  • 我们看到,在RR隔离级别下,只是在事务中第一次快照读时生成 ReadView,后续都是复用该ReadView,那么既然 ReadView 都一样, ReadView版本链匹配规则也一样, 那么最终快照读返回的结果也是一样的。

所以呢,MVCC的实现原理就是通过 InnoDB 表的隐藏字段UndoLog 版本链ReadView来实现的。
MVCC + 锁,则实现了事务的隔离性。 而一致性则是由 redologundolog 保证。

在这里插入图片描述

🚀🚀🚀 InnoDB 引擎 快速食用:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

## 1. 逻辑存储结构
表空间、段、区(1M)、页(16K)、行

## 2、架构
内存结构
磁盘结构

## 3、事务原理
原子性 - undo log
持久性 - redo log
一致性 - undo log + redo log
隔离性 -+ MVCC

## 4、MVCC (多版本并发控制)
记录隐藏字段、undo log版本链、readView (读视图)

七、MySQL 管理

7.1 系统数据库

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

在这里插入图片描述
在这里插入图片描述

7.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数据库再执行,对于一些批处理脚本,这种方式尤其方便。

示例:

## 不用登录MySQL
mysql -h192.168.200.202 -P3306 -uroot -p1234 rmzh -e "select * from stu";
mysql -uroot -p1234 rmzh -e "select * from stu";

在这里插入图片描述

⭐️ 2). mysqladmin

  • mysqladmin 是一个执行管理操作的客户端程序。可以用它来检查服务器配置当前状态创建并删除 数据库等。
通过帮助文档查看选项:
	mysqladmin --help

在这里插入图片描述
在这里插入图片描述

语法:
	mysqladmin [options] command ...
选项:
	-u, --user=name #指定用户名
	-p, --password[=name] #指定密码
	-h, --host=name #指定服务器IP或域名
	-P, --port=port #指定连接端口

示例:

## 如果不指定主机名及主机端口号,代表查看本机3306  MySQL
mysqladmin -uroot -p1234 drop 'test01';
mysqladmin -uroot -p1234 version;
mysqladmin -uroot -p1234 create db02;

在这里插入图片描述
在这里插入图片描述

⭐️ 3). mysqlbinlog

  • 由于服务器生成的 二进制日志文件二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到 mysqlbinlog 日志管理工具。
语法 :
	mysqlbinlog [options] log-files1 log-files2 ...
选项 :
	-d, --database=name 指定数据库名称,只列出指定的数据库相关操作。
	-o, --offset=# 忽略掉日志中的前n行命令。
	-r,--result-file=name 将输出的文本格式日志输出到指定文件。
	-s, --short-form 显示简单格式, 省略掉一些信息。
	--start-datatime=date1 --stop-datetime=date2 指定日期间隔内的所有日志。
	--start-position=pos1 --stop-position=pos2 指定位置间隔内的所有日志。

示例:

  • A. 查看 binlog.000008 这个二进制文件中的数据信息

在这里插入图片描述
在这里插入图片描述

⭐️ 4). mysqlshow

  • mysqlshow 客户端对象查找工具,用来很快地 查找 存在哪些 数据库数据库中的表表中的列 或者 索引
语法 :
	mysqlshow [options] [db_name [table_name [col_name]]]
选项 :
	--count 显示数据库及表的统计信息(数据库,表 均可以不指定)
	-i 显示指定数据库或者指定表的状态信息
示例:
	#查询test库中每个表中的字段书,及行数
	mysqlshow -uroot -p2143 test --count
	#查询test库中book表的详细情况
	mysqlshow -uroot -p2143 test book --count

示例:

  • A. 查询每个数据库的 表的数量表中记录的数量
mysqlshow -uroot -p1234 --count

在这里插入图片描述

  • B. 查看数据库 rmzh统计信息
mysqlshow -uroot -p1234 rmzh --count

在这里插入图片描述

  • C. 查看数据库 rmzh 中的 course 表的信息
mysqlshow -uroot -p1234 rmzh course --count

在这里插入图片描述

  • D. 查看数据库 rmzh 中的 course 表的 id 字段的信息
mysqlshow -uroot -p1234 rmzh course id --count

在这里插入图片描述

⭐️ 5). mysqldump

  • mysqldump 客户端工具用来 备份数据库 或在 不同数据库之间进行数据迁移。备份内容包含创建表,及插入表的SQL语句。
语法 :
	mysqldump [options] db_name [tables]
	mysqldump [options] --database/-B db1 [db2 db3...]
	mysqldump [options] --all-databases/-A
连接选项 :
	-u, --user=name 指定用户名
	-p, --password[=name] 指定密码
	-h, --host=name 指定服务器ip或域名
	-P, --port=# 指定连接端口
输出选项:
	--add-drop-database 在每个数据库创建语句前加上 drop database 语句
	--add-drop-table 在每个表创建语句前加上 drop table 语句 , 默认开启 ; 不
开启 (--skip-add-drop-table)
	-n, --no-create-db 不包含数据库的创建语句
	-t, --no-create-info 不包含数据表的创建语句
	-d --no-data 不包含数据
	-T, --tab=name 自动生成两个文件:一个.sql文件,创建表结构的语句;一个.txt文件,数据文件

示例:

  • A. 备份 rmzh 数据库
mysqldump -uroot -p1234 rmzh > rmzh.sql

在这里插入图片描述

  • 可以直接打开 rmzh.sql,来查看备份出来的数据到底什么样。
cat rmzh.sql

备份出来的数据包含:

  • 删除表的语句
  • 创建表的语句
  • 数据插入语句

如果我们在数据备份时,不需要创建表,或者不需要备份数据,只需要备份表结构,都可以通过对应的参数来实现。

  • B. 备份 rmzh 数据库中的表数据,不备份表结构( -t )
mysqldump -uroot -p1234 -t rmzh > rmzh01.sql

打开 rmzh01.sql ,来查看备份的数据,只有 insert 语句,没有备份表结构。

  • C. 将 rmzh 数据库的表的 表结构数据 分开备份( -T )
mysqldump -uroot -p1234 -T /root rmzh score

在这里插入图片描述

执行上述指令,会出错,数据不能完成备份,原因是因为我们所指定的数据存放目录 /root,MySQL认为是不安全的,需要存储在MySQL信任的目录下。那么,哪个目录才是MySQL信任的目录呢,可以查看一下系统变量 secure_file_priv 。执行结果如下:

在这里插入图片描述
在这里插入图片描述

上述的两个文件 score.sql 中记录的就是表结构文件,而 score.txt 就是表数据文件,但是需要注意表数据文件,并不是记录一条条的 insert 语句,而是按照一定的格式记录表结构中的数据。如下:

在这里插入图片描述

⭐️ 6). mysqlimport/source

  • 1). mysqlimport
    • mysqlimport 是客户端数据导入工具,用来导入 mysqldump-T 参数后导出的 文本文件
语法 :
	mysqlimport [options] db_name textfile1 [textfile2...]
示例 :
	mysqlimport -uroot -p2143 test /tmp/city.txt

在这里插入图片描述

  • 2). source
    • 如果需要导入 sql 文件,可以使用 mysql 中的 source 指令 :
语法 :
	source /root/xxxxx.sql # 在mysql 的命令行中执行

🚀🚀🚀 MySQL 管理 快速食用:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------->

## 1、mysql
Mysql 客户端工具,-e 执行SQL并退出 		# 通常在一些脚本编写的时候会用到这个工具

## 2、mysqladmin
Mysql 管理工具

## 3、mysqlbinlog
二进制日志查看工具

## 4、mysqlshow
查看数据库、表、字段的统计信息

## 5、mysqldump
数据备份工具

## 6、mysqlimport/source
数据导入

注:仅供学习参考,如有不足,欢迎指正!!!

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的懒虫

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值