简单谈谈MySQL的事务
2021/11/26 2:10:02
本文主要是介绍简单谈谈MySQL的事务,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简单谈谈MySQL的事务
文章目录
- 简单谈谈MySQL的事务
- 事务的基本特性和隔离级别
- 基本特性 ACID
- 4个隔离级别
- 隔离级别中可能出现的问题
- ACID是靠什么保证的
- 什么是MVCC
- readview是什么
- MVCC是如何实现不可重复读和可重复读的
- mysql的原子性和持久性是如何实现的
- 当前读、快照读,到底读的是什么
- 共享锁、排他锁
事务的基本特性和隔离级别
基本特性 ACID
事务的基本特性就是ACID
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:
- 原子性(atomicity,[ætəˈmɪsəti],或称不可分割性)
- 一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节
- 一致性(consistency,[kənˈsɪstənsi])
- 在事务开始之前和事务结束以后,数据库的完整性没有被破坏
- 隔离性(isolation,[aɪsəˈleɪʃn],又称独立性)
- 一个事务的修改在最终提交前,对其他事务是不可见的
- 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以**防止多个事务并发执行时由于交叉执行而导致数据的不一致 **
- 事务隔离分为4个不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
- 持久性(durability,[djʊərəˈbɪlɪti])
- 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
我们需要根据事务的原子性、隔离性和持久性来保证数据的一致性
那么多个事务访问同一个资源时,如何保证资源的可见性呢,这里需要谈到隔离级别了
4个隔离级别
read uncommit
:读未提交也叫做脏读,可能会读到其他事务未提交的数据- A向B转账,B本来有300,如果收了200就为500了,但是有可能事务会出现异常则修改失败应该回滚为300,可是此时另一个事务读取B的余额为500的脏数据,这就是脏读
read commit
:读已提交也叫做不可重复读,两次读取结果不一致,oracle的默认级别- 不可重复读解决了脏读的问题,他只会读取已经提交的事务
- 用户开启事务读取id=1的用户,查询到age=10,其他事务修改并提交了之后,再次读取发现结果=20
- 在同一个事务里同一个查询读取到不同的结果叫做不可重复读
repeatable read
:可重复读,这是mysql的默认级别,就是每次读取结果都一样- 但是有可能产生幻读,使用间隙锁解决
serializable
:串行,一般不使用。给每一行读取的数据加锁, 会导致大量超时和锁竞争的问题
隔离级别中可能出现的问题
- 脏读(Drity Read):可以给数据上写锁,让其他事务不能并发读和并发写,解决了脏数据的问题
- 某个事务已更新一份数据, 另一个事务在此时读取了同一份数据,由于某些原因,前一个回滚了操作,则后一个事务所读取的数据就会是不正确的。
- 不可重复读(Non-repeatable read):可以给数据上读锁,让其他事务可以并发读,但是不能写,解决了读取结果不一致的问题
- 在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
- 幻读(Phantom Read):使用间隙锁解决幻读问题(for update)
- 在一次事务里面,多次查询之后,结果集的个数不一致的情况叫做幻读。而多或者少的那一行被叫做幻行
- 例如事务1查询到3条记录但是不commit提交,事务2insert后就commit提交了,事务1再查询还是3条,但是执行全表update时却更新了4条,在更新完后再查询select,发现查询出来了4条数据,那么这就是产生了幻读,多出来的那条数据就是幻行
- 因为select是快照读,而update是当前读
- 如果加上锁之后,别的事务无法操作数据,也就不会出现幻读问题了
- 间隙锁本质上是用于阻止其他事务在该间隙内插入新记录,而自身事务是允许在该间隙内插入数据的
ACID是靠什么保证的
-
原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
-
一致性由其他三大特性保证,程序代码要保证业务层面上的数据一致性
-
隔离性由MVCC来保证
-
持久性由内存、redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可以从redo log恢复
-
redo log的刷盘会在系统空闲时进行
-
将新数据写入内存,然后对InnoDB的redo log写盘,redolog中事务进入prepare状态
如果前面prepare成功,bin log写盘,将事务日志持久化到bin log
如果持久化成功,那么在redo log里面写一个commit 记录,表示InnoDB事务进入commit状态
-
如果redolog写入完,变为prepare状态,可是断电了,没来得及写入bin log的话,在重新开启后会抛弃已经prepare的事务(因为它没有完成提交,所以不作数)
-
在innodb中有两个日志,undo log和redo log
在mysql server中有一个叫bin log的日志,也和事务有关
事务的成功不仅要在bin log中留下记录,而且也在redo log中写入commit提交状态的记录,表示事务已经完成提交
什么是MVCC
MVCC,全称 Multi-Version Concurrency Control
,即多版本并发控制
读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据——版本链
MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存
MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
MVCC只在RC和RR两钟隔离级别下工作,即read commit不可重复读和repeatable read可重复读
脏读每次读的都是最新的数据,不符合
串行会给所有读取的行都加锁,也不符合
一般我们认为MVCC有下面几个特点:
- 每行数据都存在一个版本,每次事务更新数据时都更新该版本号
- 修改时Copy出当前版本随意修改,和事务之间无干扰
- 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)
在InnoDB中,会在每行数据后添加两个额外的隐藏的字段来实现MVCC ,一条记录除了包括各个字段值,还包括了当前事务id(trx_id)和一个回滚指针(roll_pointer)。
- trx_id:生成这条记录(update/delete)的事务id
- roll_pointer:指向了undo_log中保存着的原来的记录,根据一条条的记录中的回滚指针从而构成版本链
注:一个事务的事务id在第一次insert/delete/update时生成
readview是什么
其实Read View的最大作用是用来做可见性判断的
也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据
有可能读取到的是最新的数据,也有可能读取的是当前行记录的undolog中某个版本的数据
事务开启后会创建readview,用于维护当前活动的事务的id(活动=未提交的事务),并排序生成一个数组
readview中存储了当前Read View中最大的ID及最小的ID
之前提到了在数据的隐藏列中trx_id用于储存了事务id(获取的是最大的那个id,即最新的id),在读取数据时,会拿到这个事务id去和readview中的比对
-
up_limit_id是当时活跃事务列表的最小事务id
- 如果row的
db_trx_id<up_limit_id
,说明这些数据在事务创建id的时都已经提交,如注释中的描述,这些数据均可见 - 小于 可见
- 如果row的
-
ow_limit_id是当时活跃事务的最大事务id
- 如果读到row的
db_trx_id>=low_limit_id
,说明这些id在此之前的数据都没有提交,如注释中的描述,这些数据都不可见 - 大于等于 不可见
- 如果读到row的
MVCC是如何实现不可重复读和可重复读的
读已提交(不可重复读)隔离级别下的事务在查询的开始会创建一个独立的readview
可重复读隔离级别下的事务在第一次读的时候生成一个readview,之后的读都是复用的这个readview
即:通过不同的readview生成策略来实现不同的隔离级别
mysql的原子性和持久性是如何实现的
原子性:
- 根据回滚指针roll_pointer指向的undolog中的上一条记录进行回滚
持久性:
- 将新数据写入内存,然后对InnoDB的redo log写盘,redolog中事务进入prepare状态
- 如果前面prepare成功,bin log写盘,将事务日志持久化到bin log
- 如果持久化成功,那么在redo log里面写一个commit 记录,表示InnoDB事务进入commit状态
- 如果持久化失败(断电了),如果redolog写入完,变为prepare状态,可是断电了,没来得及写入bin log的话,在重新开启后会抛弃已经prepare的事务(因为它没有完成提交,所以不作数)
当前读、快照读,到底读的是什么
当前读:读取的是数据的最新版本,总是能读取到最新的数据
快照读:读取的是历史版本的快照记录
为什么mysql中会有历史版本的记录呢?
原子性由undo log日志保证,它记录了需要回滚的日志信息,有时候可能会需要回滚,所以才将其记录了下来
在mysql默认的RR不可重复读隔离级别中,select读是快照读,读取的是历史版本的快照记录
而上锁或对表的操作是当前读
lock in share mode
for update
update
insert
delete
共享锁、排他锁
-
共享锁:
- 简称S锁,其他线程可以继续获取共享锁
- 用
IN SHARE MODE
获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作
-
排他锁:
- 简称X锁,只允许获取一次,不容许其他线程获取独占锁。
- 所以像select for update就会独占线程锁死这行
- 但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用
FOR UPDATE
方式获得排他锁 - 使用排它锁的事务不进行commit的话,别的事务是无法对被锁住的事务进行操作的
-
delete:
- X锁
-
insert:
- 一般不加锁,隐式锁
-
update:
- 不更新主键的情况下:
- X锁:就地更新
- 更新主键的情况下:
- X锁:更新主键的情况下,先执行delete操作,(隐式锁)再执行insert操作
- 不更新主键的情况下:
# 共享锁(S): SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE # 排他锁(X): SELECT * FROM table_name WHERE ... FOR UPDATE
关于mysql锁其实还有很多,可以查看一下这个博客
https://www.cnblogs.com/zhuwenjoyce/p/15060318.html
这篇关于简单谈谈MySQL的事务的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-16MySQL资料:新手入门教程
- 2024-11-16MySQL资料:新手入门教程
- 2024-11-15MySQL教程:初学者必备的MySQL数据库入门指南
- 2024-11-15MySQL教程:初学者必看的MySQL入门指南
- 2024-11-04部署MySQL集群项目实战:新手入门教程
- 2024-11-04如何部署MySQL集群资料:新手入门指南
- 2024-11-02MySQL集群项目实战:新手入门指南
- 2024-11-02初学者指南:部署MySQL集群资料
- 2024-11-01部署MySQL集群教程:新手入门指南
- 2024-11-01如何部署MySQL集群:新手入门教程