MySQL(进阶篇3.0)
锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算机资源(CPU、RAM、I/O)的争用之外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤为重要,也更加复杂
MySQL中的锁,按照锁的粒度分为以下三类:
- 全部锁:锁定数据库中的所有表
- 表级锁:每次操作锁住整张表
- 行级锁:每次操作锁住对应的行数据
全局锁
介绍
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性
为什么全局逻辑备份,就需要加全局锁呢?
1、如果不加全局锁,可能存在的问题
假设数据库中存在这样的三张表:tb_stock库存表,tb_order订单表,tb_orderlog订单日志表

- 在进行数据备份时,先备份了tb_stock库存表
- 然后接下来,在业务系统中,执行了下单操作,扣减库存,生成订单(更新tb_stock表,插入tb_order表)
- 然后再执行备份tb_order表的逻辑
- 业务中执行插入订单日志操作
- 最后,有备份了tb_orderlog表
此时备份出来的数据是存在问题的。因为备份出来的数据,tb_stock表与tb_order表的数据不一致(有最新操作的订单信息,但是库存数没减)
此时就可以借助于MySQL的全局锁来解决
2、再来分析一下加了全局锁后的情况

对数据库进行逻辑备份之前,先对整个数据库加上全局锁,一旦加了全局锁之后,其他的ddl、dml全部都处于阻塞状态,但是可以执行DQL语句,也就是只读状态,而数据备份就是查询操作。那么数据在进行逻辑备份的过程中,数据库中的数据就是不会发生变化的,这样就保证了数据的一致性和完整性。
语法
1、加全局锁
flush tables with read lock;
2、数据备份
mysqldump -u用户名 -p密码 demo > demo.sql
3、释放锁
unlock tables;
特点
数据库中加全局锁,是一个比较重的操作,存在以下问题:
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来得二进制日志(binlog),会导致主从延迟
在innoDB引擎中,可以在备份时加上参数–single-transaction 参数来完成不加锁得一致性数据备份
mysqldump --single-transaction -u用户名 -p密码 demo > demo.sql
表级锁
介绍
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中
对于表级锁,主要分为以下三类:
- 表锁
- 元数据锁(meta data lock, MDL)
- 意向锁
表锁
对于表锁,分为两类:
- 表共享读锁(read lock)
- 表独占写锁(write lock)
语法:
- 加锁:lock tables 表名… read/write.
- 释放锁:unlock tables / 客户端断开连接
特点:
1、读锁

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

左侧为客户端一,对指定表加了写锁,会阻塞右侧客户端的读和写
注意:读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户的读,又会阻塞其他客户端的写
元数据锁
元数据锁(meta data lock),简写MDL
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性
也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表的结构的。
常见的SQL操作时,所添加的元数据锁:
| 对应SQL | 锁类型 | 说明 |
|---|---|---|
| lock tables xxx read/write | shared_read_only / shared_no_read_write | |
| select、select… lock in share mode | shared_read | 与shared_read、shared_write兼容,与exclusive互斥 |
| insert、update、delete、select … for update | shared_write | 与shared_read、shared_write兼容,与exclusive互斥 |
| alter table … | exclusive | 与其他的mdl都互斥 |
意向锁
介绍
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查
加入没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢?
首先客户端一,开启了一个事务,然后执行DML操作,在执行DML语句时,会对涉及到的行加行锁

当客户端二,想对这张表加表锁时,会检查当前表是否有对应的行锁,如果没有,则添加表锁,此时就会从第一行数据,检查到最后一行数据,效率较低

有了意向锁之后:
客户端一,在执行DML操作时,会对涉及的行加行锁,同时也会对该表加上意向锁

而其他客户端,在对这张表加表锁的时候,会根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况

分类
- 意向共享锁(IS):有语句select … lock in share mode添加。与表锁共享锁(read)兼容,与表锁排他锁(write)互斥
- 意向排他锁(IX):由insert、update、delete、select…for update 添加。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放
可以通过以下SQL,查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
演示:
1、意向共享锁与表读锁是兼容的
2、意向排他锁与表读、写锁都是互斥的
行级锁
介绍
行级锁,每次操作锁对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
- 行锁(Record Lock):锁定单个记录的锁,防止其他事务对此进行update和delete。在RC、RR隔离级别下都支持

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

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

行锁
介绍
InnoDB实现了以下两种类型的行锁:
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁
- 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁
两种锁的兼容情况如下:

常见的SQL语句,在执行时,所加的行锁如下:
| SQL | 行锁类型 | 说明 |
|---|---|---|
| insert … | 排他锁 | 自动加锁 |
| update … | 排他锁 | 自动加锁 |
| delete … | 排他锁 | 自动加锁 |
| select(正常) | 不加任何锁 | |
| select … lock in share mode | 共享锁 | 需要手动在select之后加lock in share mode |
| select … for update | 排他锁 | 需要手动在select之后加for update |
演示
默认情况下,InnoDB在REPEATABLE 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);
1、普通的select语句,执行时,不会加锁
select * from stu where id = 1;
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;


2、select… lock in share mode,加共享锁,共享锁与共享锁之间兼容


客户端一获取的是id为1这行的共享锁,客户端二是可以获取id为3这行的排他锁的,因为不是同一行数据,而如果客户端二想要获取id为1这行的排他锁,会处于阻塞状态,因为共享锁和排他锁之间互斥
3、排他锁与排他锁之间互斥
begin;
update stu set name = 'lei' where name = 'lily';
commit;
begin;
update stu set name = 'php' where id = 19;
commit;

当客户端一执行update语句,会为id为1的记录加排他锁;客户端二如果也执行update语句更新id为1的数据,也要为id为1的数据加排他锁,但是客户端二会处于阻塞状态,因为排他锁之间是互斥的。直到客户端一,把事务提交了,才会把这一行的行锁释放,此时客户端二,解除阻塞。
间隙锁&临键锁
默认情况下,InnoDB在REPETABLE READ事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读
- 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁
- 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁
- 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一个间隙上采用间隙锁
InnoDB引擎
逻辑存储结构
innoDB的逻辑存储结构如下图所示:

1、表空间
表空间是InnoDB存储引擎逻辑结构的最高层,如果用户启用了参数innoDB_file_per_table,则每张表都会有一个表空间(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日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息
架构
概述
MySQL5.5版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。
下面是InnoDB架构图,左侧为内存结构,右侧为磁盘结构

内存结构

在左侧的内存结构中,主要分为四大块:Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer。接下来介绍一下这四个部分
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’;
Change Buffer
Change Buffer,更改缓冲区(针对于非唯一二级索引页),在执行DML语句时,如果这些数据page没有在Buffer Pool中,不会直接操作磁盘,而会将数据变更存在更改缓冲区Change Buffer中,在未来数据被读取时,再将数据合并恢复到Buffer Pool中,在将合并后的数据刷新到磁盘中,Change Buffer的意义是什么呢?

与聚集索引不同,二级索引通常是非唯一的,并且以相对随机的顺序插入二级索引。同样,删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作磁盘,会造成大量的磁盘IO。有了ChangeBuffer之后,可以在缓冲池中进行合并处理,减少磁盘IO
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:日志在每次事务提交后写入,并每秒刷新到磁盘一次
磁盘结构

System Tablespace
系统表空间是更改缓冲区的存储区域。如果表是在系统表空间而不是每个表文件或通用表空间中创建的,它也可能包含表和索引数据(在MySQL5.x版本中还包含InnoDB数据字典、undolog等)
参数:innodb_date_file_path
系统表空间,默认的文件名叫ibdatal
File-Per-Tablespaces
如果开启了innodb_file_per_table开关,则每个表的文件表空间包含单个InnoDB表的数据和索引,并存储在文件系统上的单个数据文件中
开关参数:innodb_file_per_table,该参数默认开启
也就是说,每创建一个表,都会产生一个表空间文件。
General Tablespaces
通用表空间,需要通过CREATE TABLESPACE语法创建通用表空间,在创建表时,可以指定该表空间
1、创建表空间
create tablespace ts_name add datafile 'file_name' engine = engine_name;
2、创建表时指定表空间
create table xxx ... tablespace ts_name;
Undo Tablespaces
撤销表空间,MySQL实例在初始化时会自动创建两个默认的undo表空间(初始化为16M),用于存储undo log日志
Temporary Tablespaces
InnoDB使用会话临时表空间和全局临时表空间。存储用户创建的临时表等数据
Doublewrite Buffer Files
双写缓冲区,innodb引擎将数据页从buffer pool刷新到磁盘前,先将数据页写入双写缓冲区文件中,便于系统异常时恢复数据
Redo Log
重做日志,用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者是在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中,用于刷新脏页到磁盘时,发生错误时,进行数据恢复使用
后台线程

在innodb的后台线程中,分为4类,分别为:Master Thread、IO Thread、Purge Thread、Page Cleaner Thread
Master Thread
核心后台线程,负责调度其他线程,还负责将缓冲池中的数据异步刷新到磁盘中,保持数据的一致性,还包括脏页的刷新、合并插入缓存、undo页的回收
IO Thread
在Innodb存储引擎中大量使用了AIO来处理IO请求,这样可以极大的提高数据库的性能,而IO Thread主要负责这些IO请求得回调
| 线程类型 | 默认个数 | 职责 |
|---|---|---|
| Read thread | 4 | 负责读操作 |
| Write thread | 4 | 负责写操作 |
| Log thread | 1 | 负责将日志缓冲区刷新到磁盘 |
| Insert buffer thread | 1 | 负责将写缓冲区内容刷新到磁盘 |
可以通过以下的这条指令,查看到innodb的状态信息,其中就包含IO Thread信息
show engine innodb status \G;

Purge Thread
主要用于回收事务已经提交了的undo log,在事务提交之后,undo log可能不用了,就用它来回收
Page Cleaner Thread
协助Master Thread刷新脏页到磁盘的线程,它可以减轻Master Thread的工作压力,减少阻塞
事务原理
事务基础
事务
事务是一组操作的集合,他是不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败
特性
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性(Durability):事务一旦提交或回滚,他对数据库中的数据的改变就是永久的
对于这四大特性,实际上分为两个部分。其中原子性、一致性、持久化,实际上是由InnoDB中的两份日志来保证的,一份是redo log日志、一份是undo log日志;而隔离性是通过数据库的锁,加上mvcc来保证的

redo log
重做日志,记录的是事务提交时数据页的物理修改,用来是实现事务的持久性
该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是内存中,后者是磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于刷新脏页到磁盘,发生错误时,进行数据恢复使用
如果没有redo log,可能会出现哪些问题?
在InnoDB引擎中的内存结构中,主要的内存区域就是缓冲池,在缓冲池中缓存了很多的数据页。当在一个事务中,执行多个增删改的操作时,InnoDB引擎会先操作缓冲池中的数据,如果缓冲区没有对应的数据,会通过后台线程将磁盘中的数据加载出来,存放在缓冲区中,然后将缓冲池中的数据修改,修改后的数据页为脏页。而脏页则会在一定的时机,通过后台线程刷新到磁盘中,从而保证缓冲区与磁盘的数据一致。而缓冲区的脏页数据并不是实时刷新的,而是一段时间之后将缓冲区的数据刷新到磁盘中,假如刷新到磁盘的过程出错了,而提示给用户事务提交成功,而数据却没有持久化下来,这就出现问题了,没有保证事务的持久性。

那么redo log是如何解决这个问题的?

有了redo log之后,当对缓冲区的数据进行增删改之后,会首先将操作的数据页的变化,记录在redo log buffer中。在事务提交时,会将redo log buffer中的数据刷新到redo log磁盘文件中。过一段时间之后,如果刷新缓冲区的脏页到磁盘时,发生错误,此时就可以借助于redo log进行数据恢复,这样就保证了事务的持久性。而如果脏页成功刷新到磁盘或者涉及到的数据已经落盘,此时redo log就没有作用了,就可以删除了。所以存在的两个redo log文件是循环写的
那为什么每一次提交事务,要刷新redo log到磁盘中呢,而不是直接将buffer pool中的脏页刷新到磁盘中呢?
因为在业务操作中,操作数据一般都是随机读写磁盘的,而不是顺序读写磁盘。而redo log在往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的。顺序写的效率,要远大于随机写。这种先写日志的方式,称之为WAL(Write-Ahead Logging)
undo log
回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚(保证事务的原子性)和MVCC(多版本并发控制)
undo log和redo 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
MVCC
基本概念
当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于日常的操作,如: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
隐藏字段
介绍

当创建了上面这张表之后,在查看表结构的时候,就可以显式的看到这三个字段。实际上除了这三个字段以外,InnoDB还会自动的添加三个隐藏字段及其含义分别是:
| 隐藏字段 | 含义 |
|---|---|
| DB_TRX_ID | 最近修改事务id,记录插入这条记录或最后一次修改该记录的事务id |
| DB_ROLL_PTR | 回滚指针,指向这条记录上一版本,用于配合undo log,指向上一个版本 |
| DB_ROW_ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段 |
而上述的前两个字段是肯定会添加,是否添加最后一个字段DB_ROW_ID,要看当前表有没有主键,如果有主键,则不会添加隐藏字段
undo log
介绍
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除
而update、delete的时候,产生的undo log日志不仅在回滚的时候需要,在快照读的时候也需要,不会立即删除
版本链
有一张表原始数据为:

DB_TRX_ID:代表最近下修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是自增的
DB_ROLL_PTR:由于这条数据是才插入的,没有被更新过,所以该字段值为null
然后,有四个并发事务同时在访问这张表
1、第一步

当事务2执行第一条修改语句时,会记录undo log日志,记录数据变更之前得样子;然后更新记录,并且记录本次操作得事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本

2、第二步

当事务3执行第一条修改语句时,也会记录undo log日志,记录数据变更之前得样子;然后更新记录,并且记录本次操作得事务id,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本

3、第三步

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

最终发现,不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最近的旧纪录,链表尾部是最早的旧纪录
readview
Readview(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(为提交的)id
ReadView中包含了四个核心字段:
| 字段 | 含义 |
|---|---|
| m_ids | 当前活跃的事务id集合 |
| min_trx_id | 最小活跃事务ID |
| max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) |
| creator_trx_id | ReadView创建者的事务ID |
而在ReadView中就规定了版本链数据的访问规则:
tx_id代表当前undo log版本链对应事务ID
| 条件 | 是否可以访问 | 说明 |
|---|---|---|
| trx_id == creator_trx_id | 可以访问该版本 | 成立,说明数据是当前这个事务更改的 |
| trx_id < min_trx_id | 可以访问该版本 | 成立,说明数据已经提交了 |
| trx_id > max_trx_id | 不可以访问该版本 | 成立,说明该事务是在ReadVIew生成后才开启的 |
| min_trx_id <= trx_id <= max_trx_id | 如果trx_ix不在m_ids中,是可以访问该版本的 | 成立,说明数据已经提交了 |
不同隔离级别,生成ReadView的时机不同:
- READ COMMITTED:在事务中每一次执行快照读时生成ReadView
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView
原理分析
RC隔离级别
RC隔离级别下,在事务中每一次执行快照读时生成ReadView
两次快照读读取数据,是如何获取数据的?
在事务5中,查询了两次id为30的记录,由于隔离级别为Read Committed,所以每一次进行快照读都会生成一个ReadView,那么两次生成的ReadView如下:

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


在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配
-
先匹配
这条记录,这条记录对应的trx_id为4,也就是将4带入右侧的匹配规则中。①不满足 ②不满足 ③不满足 ④也是不满足,都不满足,则继续匹配undo log版本链的下一条 -
再匹配第二条
这条记录对应的trx_id为3,也就是将3带入右侧的匹配规则中。①不满足 ②不满足 ③不满足 ④也是不满足,都不满足,则继续匹配undo log版本链的下一条 -
再匹配第三条
,这条记录对应的trx_id为2,也就是将2带入右侧的匹配规则中。①不满足 ②满足 终止匹配,此次快照读,返回的数据就是版本链中记录的这条数据
2、第二次快照读具体二点读取过程


再进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:
-
先匹配
这条记录,这条记录对应的trx_id为4,也就是将4带入右侧的匹配规则中。①不满足 ②不满足 ③不满足 ④也是不满足,都不满足,则继续匹配undo log版本链的下一条 -
再匹配第二条
这条记录对应的trx_id为3,也就是将3带入右侧的匹配规则中。①不满足 ②满足 终止匹配,此次快照读,返回的数据就是版本链中记录的这条数据
RR隔离级别
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。而RR是可重复读,在下一个事务中,执行两次相同的select语句,查询到的结果是一样的
MySQL是如何做到可重复读的呢?

在RR隔离级别下,只是在事务中第一次快照读时生成ReadView,后续都是复用该ReadView,那么既然ReadView都一样,ReadView的版本链匹配规则也一样,那么最终快照读返回的结果也是一样的
所以,MVCC的实现原理就是通过InnoDB表的隐藏字段、undo log版本链、ReadView来实现的。而MVCC+锁,则实现了事务的隔离性。而一致性则是由redolog和undolog保证

MySQL管理
系统数据库
MySQL数据库安装完成后,自带了以下四个数据库,具体作用如下:
| 数据库 | 含义 |
|---|---|
| mysql | 存储在MySQL服务器正常运行所需要的各种信息(时区、主从、用户、权限等) |
| information_schema | 提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类型及访问权限 |
| performance_schema | 为MySQL服务器运行时状态提供了一个底层监控功能,主要用于收集数据库服务器性能参数 |
| sys | 包含了一系列方便DBA和开发人员利用performance_schema性能数据库进行性能调优和诊断的视图 |
常用工具
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 -uroot –p123456 db01 -e "select * from stu";
mysqladmin
mysqladmin是一个执行管理操作的客户端程序,可以用它来检查服务器的配置和当前状态、创建并删除数据库等
通过帮助文档查看选项:mysqladmin --help
语法:mysqladmin [options] command ...
选项:-u, --user=name #指定用户名-p, --password[=name] #指定密码-h, --host=name #指定服务器IP或域名-P, --port=port #指定连接端口
示例:
mysqladmin -uroot –p1234 drop 'test01';
mysqladmin -uroot –p1234 version;
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这个二进制文件中的数据信息
mysqlbinlog binlog.000008
简单格式为:
mysqlbinlog -s binlog.000008
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
示例:
1、查询每个数据库的表的数量及表中记录的数量
mysqlshow -uroot -p密码 --count
2、查看数据库db01的统计信息
mysqlshow -uroot -p1234 db01 --count
3、查看数据库db01中的course
mysqlshow -uroot -p1234 db01 course --count
4、查看数据库db01中的course表的id字段的信息
mysqlshow -uroot -p1234 db01 course id --count
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文件,数据文件
示例:
1、备份db01数据库
mysqldump -uroot -p1234 db01 > db01.sql
备份出来的数据包括:
- 删除表的语句
- 创建表的语句
- 数据插入语句
2、备份db01数据库中的表的数据,不备份表结构
mysqldump -uroot -p1234 -t db01 > db01.sql
3、将db01数据库的表结构与数据分开备份
mysqldump -uroot -p1234 -T /root db01 score
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(进阶篇3.0)
锁 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算机资源(CPU、RAM、I/O)的争用之外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&…...
2023.6.8小记——嵌入式系统初识、什么是ARM架构?
今天还挺充实的,早上在图书馆本来想学一下notion,结果看李沐老师的动手深度学习看到十点半,在电脑上配置了李沐老师的d2l和jupyter,等后续有时间的时候再继续学。 下午看了一下notion的使用方法,这玩意初学者用起来是…...
分布式运用之ELK企业级日志分析系统
1.ELK的相关知识 1.1 ELK的概念与组件 ELK平台是一套完整的日志集中处理解决方案,将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用, 完成更强大的用户对日志的查询、排序、统计需求。 ElasticSearch: 是基于Lucene(…...
【华为OD机试真题 C语言】8、停车场车辆统计 | 机试真题+思路参考+代码解析
文章目录 一、题目🎃题目描述🎃输入输出🎃样例1🎃样例2 二、思路参考三、代码参考🏆C语言 作者:KJ.JK 🍂个人博客首页: KJ.JK 🍂专栏介绍: 华为OD机试真题汇…...
c++ MES 对接(XML、JSON、SOAP)
🗑️ 清空 //MES系统对接可以使用多种协议,包括XML、JSON和SOAP等。 //以下是使用C语言进行MES系统对接的示例代码: //1. XML协议对接: //c #include <iostream> #include <string> #include <vector> #incl…...
idea导入java web项目带jar
可参考:idea导入Javaweb项目_小黑cc的博客-CSDN博客 配置tomcat 加载项目jar依赖 最后点ok,tomcat启动 jsp页面的项目,必须要加载这两个jar包...
【第55天|● 392.判断子序列 ● 115.不同的子序列 】
392.判断子序列 class Solution { public:bool isSubsequence(string s, string t) {if(s.size()0)return true;if(t.size()0) return false;vector<bool> dp(t.size()1, true);for(int i0; i<s.size(); i){for(int jt.size(); j>0; j--){if(s[i]t[j-1]&&…...
Dockerfile创建镜像
一、Docker镜像的创建 创建镜像有三种方法,分别为【基于已有镜像创建】、【基于本地模板创建】以及【基于Dockerfile创建】。 1.1 基于现有镜像创建 (1)首先启动一个镜像,在容器里做修改docker run -it centos:7 /bin/bash …...
基于 opencv 的人脸识别上课考勤系统,附源码,可作为毕业设计
一、简介 这个人脸识别考勤签到系统是基于大佬的人脸识别陌生人报警系统二次开发的。 项目使用Python实现,基于OpenCV框架进行人脸识别和摄像头硬件调用,同时也用OpenCV工具包处理图片。交互界面使用pyqt5实现。 该系统实现了从学生信息输入、人脸数据…...
.editorconfig 配置
有人会问:既然项目已经使用了 eslint 和 prettier,为什么还需要 EditorConfig? 为什么需要 EditorConfig? .editorconfig 是一个用于定义和维护跨不同编辑器和开发环境的一致编码样式的文件。它可以确保整个团队在使用不同编辑器…...
Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider
介绍 首先明确一下什么是延迟查找,一般来说通过Autowired注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到…...
VSCode--Config
1. basic 1.1 调整字体 1.2 调整 remote login 输入框都在 TERMINAL 中实现 1.3 界面设置成中文 安装插件: 然后配置即可。 2.Linux 2.1 Install 2.1.1 offline Install vscode server 问题描述 内网开发,vscode 自身通过代理安装完 remote 插件后…...
代码随想录刷题第48天|LeetCode198打家劫舍、LeetCode213打家劫舍II、LeetCode337打家劫舍III
1、LeetCode198打家劫舍 题目链接:198、打家劫舍 1、dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。 2、递推公式: 如果偷第i房间,那么dp[i] dp[i - 2] nums[i] …...
C# NTS 获取MuliiLineString中的所有线
/// <summary>/// 获取多段线的所有线/// </summary>/// <param name"ml"></param>/// <returns></returns>public static List<LineString> GetLineStrings(this MultiLineString ml){List<LineString> lineString…...
CodeWhisperer插件使用体验
官方教程点击跳转 使用工具 1.vscode 2.插件(AWS Toolkit),免费使用 安装以后如何使用 1.首先要有一个aws账号 2.插件下载好以后登录aws账号,我们主要用这款插件的CodeWhisperer这个功能,其它的自行看官方教程了解。 注意事项:我们在从vs…...
机器学习笔记 - 多实例学习(MIL)弱监督学习
一、多实例学习概述 多实例学习(MIL)是一种弱监督学习形式,其中训练实例被排列在称为袋的集合中,并为整个袋提供标签。这种方式越来越受到人们的关注,因为它自然适合各种问题,并允许利用弱标记数据。因此,它被应用于计算机视觉和文档分类等不同的应用领域。 多实例学习(…...
SQL Server 2008 定时自动备份和自动删除方法
SQL Server 2008 数据定时自动备份和自动删除方法,同一个计划兼备数据备份数数据删除的操作方法 工具/原料 SQL Server 2008 方法/步骤 1、 点击实例名下的【管理】-【维护计划】-点击鼠标右键,点击【维护计划向导】,填写计划名称&…...
代码生成器实现
代码生成器实现 实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的 构造数据模型 需求分析 借助Freemarker机制可以方便的根据模板生成文件,同时也是组成代码生成器的核心部分。对于Freemarker而 言,其强调 数据模型 模板 文件 的思…...
【Python基础】Python函数(基本函数)
文章目录 Python函数1、函数多返回值2、函数多种传参方式(1)位置参数(2)关键字参数(3)缺省参数(4)不定长参数位置传递关键字传递 3、小结 Python函数 1、函数多返回值 Q:如果一个函数要有多个返回值,该如何书写代码? # 使用多个变量&#…...
Vue3 + TS + Vite —— 大屏可视化 项目实战
前期回顾 Vue3 Ts Vite pnpm 项目中集成 —— eslint 、prettier、stylelint、husky、commitizen_彩色之外的博客-CSDN博客搭建VIte Ts Vue3项目并集成eslint 、prettier、stylelint、huskyhttps://blog.csdn.net/m0_57904695/article/details/129950163?spm1001.2014…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
