0
点赞
收藏
分享

微信扫一扫

MySQL四大特性及其实现原理

妖妖妈 2022-05-04 阅读 116

MySQL四大特性及其实现原理

事务

数据库事务概述

存储引擎支持的情况

在这里插入图片描述

基本概念

事务的ACID特性

事务的状态

在这里插入图片描述

如何使用事务

使用事务有两种方式,分别为显示事务隐式事务

显示事务

隐式事务

隐式提交事务的情况

表中数据(只测试了第一个)
在这里插入图片描述

事务的常见分类

从事务理论的角度,可以把事务分为以下几种类型:
扁平事务
带有保存点的扁平事务
链事务
嵌套事务
分布式事务

事务的隔离级别

MySQL是一个客户端/服务端架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接上之后,就可以称为一个会话,每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说可能同时处理多个事务。事务有隔离性的特性,理论上某个事务对某个数据进行访问时,其他事务应该进行排队,当该事务提交后其他事务才可以继续访问。但是这样对性能影响太大

数据并发的问题

1、脏写
对于两个事务A、B,如果A修改了另一个未提交我事务B修改过的数据那就意味着发生了脏写。
2、脏读
对于两个事务A、B,A读取了已经被B修改但还没有提交的字段。
3、不可重复读
对于两个事务A、B,A读取了一个字段,B更新了该字段,之后A再次读取同一个字段,值就不同了
4、幻读
对于两个事务A、B,A从一个表中读取了一个字段,B在该表中插入了新的行,然后A再次读取同一个表,就会多出几行。

SQL中的四种隔离级别

MySQL支持的四种隔离级别

MySQL在REPEATABLE READ隔离级别下是可以禁止幻读发生。
查看事务隔离性
在这里插入图片描述
在这里插入图片描述

修改事务隔离级别
在这里插入图片描述

MySQL事务日志

事务有四种特性:原子性、一致性、隔离性、持久性
隔离性是由锁机制
原子性、一致性、持久性由事务的redo logundo log
redo log:重做日志,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性
undo log:回滚日志,回滚行记录到某个特定版本,用来保证事务的原子性和一致性
redo log记录的是" 物理级别 “的页修改操作,主要是为了保证数据的可靠性。
undo log记录的” 逻辑操作 "日志,比如对某一行进行了Insert语句那么undo log记录一条与之相反的delete操作。主要用于事务的回滚(undo log 记录的是每个修改操作的逆操作)和一致性非锁定读(undo log回滚行记录到某种特定的版本–mvcc,即多版本并发控制)

redo log

InnoDB引擎是以页为单位来管理存储空间的,在真正访问页面之前,需要把磁盘上的页缓存到内存的Buffer Pool之后才可以访问。所有的变更都必须更新缓冲池中的数据,然后缓冲池中的脏页会以一定的频率被刷入磁盘(checkPoint机制),通过缓冲来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。

为什么需要redo log

一方面,缓冲池可以帮助我们消除CPU和磁盘之间的鸿沟,checkPoint机制可以保证数据的最终到磁盘,然而由于并不是每次变更时候都触发checkpoint机制,而是master线程隔一段时间去处理,所以最坏的情况就是事务提交后,刚写完缓冲池,数据库宕机了,那么这段数据就丢失了无法恢复。
另一方面,事务包含持久性的特性,就算说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库所做的更改不能丢失。
为了保证这个持久性?一个简单的做法: 在事务提交完成之前把该事务所修改的所以页面都刷新到磁盘。但是存在以下问题:

另一个解决思路: 我们只是想让已经提交了的事务对数据库中数据所作的修改永久生效, 即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们没有必要再每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改了那些东西记录以下,比如:将第0号表空间的10号页面偏移量为100处的值更新为2.
InnoDB引擎的事务采用WAL技术(Write-Ahead Logging),这种技术的思想就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log,当发生宕机且数据未刷新到磁盘时,可以通过redo log来恢复,保证ACID的D

redo log的好处、特点

redo 组成

redo log可以简单分为以下两部分
重做日志的缓冲(redo log buffer) ,保存在内存中,易丢失
在服务启动时就像操作系统申请了一大片称之为redo log buffer的连续内存空间,翻译成中文就是redo日志缓冲区,这片内存被划分为若干个连续的redo log block,一个redo log block占用512字节大小

redo的整体流程

在这里插入图片描述

redo log的刷盘策略

redo log buffer 刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到文件系统缓存(page cache) 中去,真正的写入会交给系统自己来决定。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,如果系统宕机,那么数据也丢失了。针对这种情况InnoDB给出innodb_flush_log_at_trx_commit参数,该参数控制commit提交事务时们如何将redo log buffer 中的日志刷新到redo log file中
在这里插入图片描述

在这里插入图片描述
另外,InnoDB存储引擎有一个后台线程,每隔一秒就会把redo log buffer中的内容写到文件系统缓存(page cache),然后调用刷盘操作。
也就是说,一个没有提交事务的redo log记录,也可能会刷盘,因为在事务执行过程redo log记录会被写道redo log buffer ,这些redo log 记录会被后台线程刷盘。
在这里插入图片描述

除了后台线程每秒1次的轮询操作,还要一种情况,当redo log buffer占用的空间即将达到innodb_log_buffer_size的一半时后台线程也会主动刷盘

不同刷盘策略演示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

写入redo log buffer过程

补充概念:Mini-Transaction

MySQL把对底层页面中的一次原子访问的过程称之为一个Mini-Transaction,简称mtr,比如像某个索引对应的B+树插入一条记录的过程就是一个mtr,一个所谓的mtr可以包含一组redo日志,在进行崩溃恢复时这一组redo日志作为一个不可分割的整体

redo日志写入log buffer

向log buffer中写入redo log的过程是顺序的,也就是先往前边的block中写,当该block的空闲空间用完之后再往下一个block中写。当我们想往log buffer中写入redo log时,第一个遇到的问题就是应该写在那个block的那个偏移量处,所以InnoDB的设计着提供了一个称为buf_free的全局变量,该变量指明后续写入的redo日志应该写入到log buffer中的那个位置

redo log block结构

一个redo log block是由日志头、日志体、日志尾组成。日志头占用12字节,日志尾占用8字节,所以一个block真正能存储的数据就是512-12-8 = 492字节。

redo log file

相关参数设置

checkpoint

Undo日志

redo log是事务持久性的保证,undo log是事务原子性的保证。在保证事务中更新数据的前置操作其实要先写入一个undo log

如何理解Undo日志

事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。每当我们要对一条记录做改动时,都需要留一手

undo log日志作用

undo的存储结构

InnoDB对undo log的管理采用段的方式,也就是回滚段(rollback segment)。每个回滚段记录了1024个undo log segment,每个undo log segment段中进行undo页的申请

undo类型

undo log的生命周期

对于InnoDB引擎来说,每个行记录除了记录本身的数据之外,还有几个隐藏的列:

概述

MySQL并发事务访问相同记录情况

读-读情况

写-写情况

在这里插入图片描述

读-写或写-读情况

并发问题的解决方案

锁的不同角度分类

在这里插入图片描述

从数据操作的类型划分

从数据操作的粒度划分

为了经可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁很消耗资源,因此数据库系统需要在高并发响应和系统性能两方面进行平衡,这样就产生了锁粒度的概念

表锁

锁类型自己可读自己可写自己可以操作其他表他人可读他人可写
读锁
写锁

行锁

行锁也称为记录锁,顾名思义,就是锁住某一行,需要注意的是,MySQL服务器层并没有实现行锁机制,行级锁只在存储引擎层实现
优点:锁的粒度小,发生锁冲突概率低,可以实现的并发度高
缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁情况

页锁

对待锁的态度划分

从对待锁的态度来看锁的话,可以分为乐观锁和悲观锁,它们并不是锁,而是锁的设计思想

悲观锁

乐观锁

按加锁的方式划分:显示锁、隐式锁

隐式锁

一个事务在执行INSERT操作时,如果即将插入的间隙已经被其他事务加了gap锁,那么本次insert操作就会阻塞,并且当前事务会在该间隙上加一个插入意向锁,否则一般情况下insert是不加锁的。那如果一个事务首先插入了一条记录,然后另一个事务对这条记录查或改就会发生脏读,脏写

即一个事务对新插入的记录可以不显示加锁,但是由于事务id的存在,相当于加了一个隐式锁。别的事务在对这条记录加s锁或x锁时由于隐式锁的存在,会先帮助当前事务生成一个锁结构,然后自己再生成一个锁结构后进入等待模式。隐式锁是一种延迟加载的机制,从而来减少加锁的数量。

全局锁

全局锁就是对整个数据库加锁,当你需要让整个库处于只读状态时候,可以使用这个命令,之后其他线程的增删改、数据定义语言等都会阻塞。

死锁

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环。

锁内存结构

在这里插入图片描述

锁监控

在这里插入图片描述

多版本并发控制

什么是MVCC

快照读与当前读

MVCC在MySQL InnoDB中的实现主要是为了提高数据库的并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读,而这个读指的是快照读而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现而MVCC本质是乐观锁思想

MVCC实现原理

MVCC依赖于:隐藏字段、undo log 、Read view

什么是ReadView

在MVCC机制中,多个事务对同一个行记录进行更新会产生多个历史快照,这些历史快照保存在Undo log里。如果一个事务想要查询这个行记录,需要读取那个版本的行记录呢,这时就需要ReadView了
ReadView就是事务在使用MVCC机制进行快照读操作时产生的读视图。当事务启动时,会生成数据库系统当前的一个快照,InnoDB为每个事务构造了一个数组,用来记录并维护当前系统活跃事务的ID

设计思路

使用READ UNCOMMITED隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本
使用SERIALIZABLE 隔离级别的事务,InnoDB规定使用加锁的方式来访问记录
使用READ COMMITTED和REPEABLE READ隔离级别的事务,都必须保证读到已经提交了的事务修改过的记录

ReadView中主要包含4个比较重要的内容
1、creator_trx_id,创建这个ReadView事务的ID
2、trx_ids:表锁在生成ReadView时当前系统中活跃的读写事务的事务id列表
3、up_limit_id:活跃的事务中最小事务id
4、low_limit_id:表示生成ReadView时系统中应该分配给下一个事务的id。low_limit_id是系统中最大的事务id值

在这里插入图片描述

举报

相关推荐

0 条评论