本文主要讲解:Mysql的分布式事务原理及中间会遇见的问题
目录
一、事务的特性与类型
事务的特性(ACID):
1)原子性
构成事务的操作要么同时成功,要么同时失败
2)一致性
事务执行之前和事务执行之后,数据的一致性(如:卖票系统,卖完一张后,费用扣除、余票减一。“一致”是指数据库中的数据是正确的,不存在矛盾)
3)隔离性
两个事务之间互不干扰。一个事务在执行过程中,不能看见其他事务运行的中间过程。
4)持久性
事务提交后,此事务对数据的更改操作会被持久化到数据库中,并且不会被回滚
事务的类型:
1)扁平事务
最常见的,在这期间的所有操作要么全部提交成功,要么全部提交失败(回滚)
2)带有保存点的扁平事务
在扁平事务的内部设置了保存点,回滚时,可以回滚到该位置
3)链式事务
链式事务是在带有保存点的事务的基础上,自动将当前事务的上下文隐式的传递给下一个事务。上一个事务的提交操作和下一个事务的开始操作具备原子性。
4)嵌套事务
多个事务嵌套,共同完成一个任务,最外层的顶层事务控制着所有的内部事务,内部事务提交后,整体事务并不会提交,只有当最外层的顶层事务提交后,整个事务才算提交完成。
5)分布式事务
事务的参与者、事务所在的服务器、涉及的资源服务器以及事务管理器等分别位于不同的分布式系统的不同服务或数据库的节点
二、本地事务
基于关系型数据库的事务也可以称为本地事务或者传统事务。
本地事务的特征:
1)一次事务过程只能连接一个支持事务的数据库
2)执行结果满足ACID
3)使用数据库本身的锁执行事务
本地事务的优点:
1)支持严格的ACID属性
2)事务可靠
3)执行效率高
4)事务的状态可以只在数据库中维护
5)编程模型简单
本地事务的缺点:
1)不具备分布式事务的处理能力
2)不能用于多个事务性数据库
三、并发事务带来的问题
更新丢失(或脏写)
脏读
不可重复度
幻读
1)更新丢失(或脏写):
对于同一行数据来说,一个事务对该行的数据的操作会覆盖其他事务对该行数据的更新操作。本质上是写操作的冲突,解决办法是让每个事务按照串行的方式执行,按照一定顺序依次进行写操作。
例子:张三账户余额100元,事务A对张三账户增加300元,事务B对张三账户增加400元。此时事务A与事务B同时读取到张三的余额为100,那么最终的余额取决于最后提交的那个事务+100元。
2)脏读:
一个事务读取了另外一个事务未提交的数据。
本质上是读写冲突,解决脏读的方法就是先写后读,也就是写完后读。
例子: 事务A对张三账户执行转账100元,还未提交,事务B读取到了结果,此时由于网络等原因,事务A执行了回滚,事务B读到的就是脏读。
3)不可重复读:
同一个事务,使用相同的查询语句,在不同时刻读取的结果数据不一致。
本质上是读写操作的冲突,解决办法是先读后写。
例子:事务A对张三的账户准备扣款,执行事务时,发现还有100元。但此时事务B对张三账户扣款了100。事务A对张三账户再读时,就没有100了,两次读的结果不一样。
4)幻读:
一个事务两次读取一个范围的数据记录,两次读取的范围结果不同。
本质上是读写操作的冲突,解决办法就是先读后写。
5)不可重复读与幻读的区别:
不可重复读重点在于更新与删除,而幻读重点在于插入操作。
使用锁机制实现事务隔离级别时,不可重复读与幻读不同。(在可重复读隔离级别中,对读的数据会加锁,其他事务无法修改这些数据,而这种方法无法对新加入的数据加锁)
幻读无法通过行级锁来避免。(需要使用串行化的事务隔离级别,但这么做会大大的降低数据库并发能力)
不可重复读和幻读最大的区别,就在于如何通过锁机制来解决它们产生的问题。(除了使用悲观锁来解决这两个问题,还会使用乐观锁来解决。Mysql为了提高整体性能,使用了MVCC机制:多版本并发控制,来解决不可重复读与幻读的问题。MVCC机制分为快照读与幻读,其中快照读就可以解决幻读)
四、事务隔离级别
读未提交
读已提交
可重复读
串行化
事务隔离级别的区别(Mysql的InnoDB存储引擎默认是可重复读,Oracle默认是读已提交):
修改事务隔离级别的设置点击我
五、各种类型的锁
本质上,锁是计算机中对多个进程或者多个线程访问某一个资源进行协调的一种机制。在Mysql中使用锁实现了事务隔离级别
悲观锁与乐观锁:
对数据库中数据的读写持悲观态度,在整个数据处理中,将相应的数据锁定。在数据库中,悲观锁的实现需要依赖数据库提供的锁机制实现,性能不友好,特别是在长事务时。
对于数据库中的数据的读写持乐观态度。在整个数据处理中,大多数情况下是通过数据版本(Version)记录机制来实现的。
读锁与写锁:
读锁,又称为共享锁或S锁(Share锁),针对同一份数据,可以加多个读锁而互不影响。但不能增加写锁了。
写锁,又称为排它锁或X锁(Exclusive锁),如果当前写锁未释放前,它会阻塞其他的写锁和读锁。
读锁共享,写锁排他。
表锁、行锁、页面锁:
表锁就是在整个数据表上加锁。在Mysql中有:1)表共享锁、2)表独占写锁
行锁是在数据行上进行的加锁和释放锁,InnoDB有两种类型的行锁:1)共享锁(可以加多个共享锁)、2)排他锁(不允许再加锁了),锁只要加到索引上,如果对非索引字段设置条件更新,行锁可能会变成表锁。InnoDB的行锁是针对索引加锁,不是针对记录,并且加锁的索引不能失效,否则行锁可能会变成表锁。
在页面级别对数据进行加锁和释放锁。锁粒度介于表锁与行锁之间,其并发度一般。
间隙锁与临键锁:
间隙锁是对两个值之间的空隙加锁。Mysql的默认隔离级别是可重复读,在该级别下可能会出现幻读的问题,而间隙锁在某种程度上可以解决幻读的问题。
临键锁又称为Next-Key锁,是行锁与间隙锁的组合。
六、死锁问题
死锁的必要条件(都存在才发生死锁):
1)互斥条件
2)不可剥夺条件
3)请求与保持
4)循环等待条件
处理死锁的方法
1)预防死锁:破坏4个条件之一
2)避免死锁:使用某种策略放在该事件发生
3)检测死锁:允许死锁发生,但发生后
4)解除死锁
七、MVCC机制
MVCC(多版本并发控制)主要解决多事务并发控制问题,也就是保证事务的隔离性。
MVCC工作原理
- 读已提交MVCC的工作原理 1)当前事务自身产生的数据。 2)当前事务开启之前,其他已经提交的事务。 在t1时刻事务E能读取到事务A、B、C已经提交的事务及事务D上一个提交的版本。
- 可重复读MVCC的工作原理 1)在系统中记录下t1时刻启动事务E时的活动事务列表,在事务E执行的过程中,一直使用在t1时刻记录的活动事务列表即可,这个一直使用的活动事务列表被称为快照。 也就是说事务E,在t1与t2时刻读取到的事务是一样的。
读已提交隔离级别下每个SQL语句都有一个自己的快照,它们看到的数据库中的数据是不同的。而在可重复读隔离级别下,所有的SQL语句使用同一个快照,能够看到数据库中同样的数据。
八、Redo Log基本原理
事务的隔离性是由锁实现的,Mysql中原子性和持久性是由redolog实现的,一致性是由undolog实现的。
redo log基本概念:
- 往往用来恢复提交后的物理数据页,且只能恢复到最后一次提交的位置
- redo log 也被称为重做日志,它是在InnoDB存储引擎层产生的,用来保证事务的原子性和持久性。
- redo log 通常包含两部分:一部分是内存中的日志缓冲(redolog buffer)。另一部分是存放在磁盘上的重做日志文件(redolog file)。
redo log 基本原理
- 当创建一个订单时,会写入到redo log buffer中,redo log buffer会根据一定的规则刷新到redo log文件中去。当mysql发生故障重启时,会根据redo log中的数据对订单表中的数据恢复(先恢复到order.idb文件中,再加载到order中)
九、Redo Log刷盘规则
1、Redo Log Buffer写日志到Redo Log File
在mysql innodb存储引擎中,通过提交事务时强制执行写日志操作,这样一个机制来实现事务的持久化。innodb存储引擎为了保证在事务提交时,将日志提交到事务日志文件中,默认在每次将redo log buffer中的日志写入日志文件的过程中,就会调用一次操作系统的fsync()的操作
写入时经过了用户空间与内核空间(如果使用O_DIRECT标志位,可以不经过内核空间)。
2、Redo Log刷盘规则
- 开启事务,发出Commit(提交事务)指令后是否刷日志由变量innodb_flush_log_at_trx_commit决定。
- 每秒刷新一次。刷日志的频率由变量innodb_flush_log_at_timeout参数的值决定,默认是1秒。需要注意的是,刷日志的频率和是否执行了commit操作无关。
- 当Log Buffer中已经使用的内存超过一半时,也会触发刷盘操作。
- 当事务中存在checkpoint(检查点)时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN的位置。其中,LSN的全称为Log Sequence Number,表示日志的逻辑序列号。
3、innodb_flush_log_at_trx_commit 变量的刷盘规则
innodb_flush_log_at_trx_commit 不同的值(默认为1),对应不同的规则
十、Redo Log写入机制与LSN机制
1、Redo log 写入机制
- redo log主要是物理日志,也就是物理上的修改操作能够恢复提交事务修改的页操作,redo log的文件是以顺序循环的方式写入的,一个文件写满会写入另外一个文件,最后一个文件写满后,会覆盖写入到第一个文件。
- 当Write Pos追上Check Point时,表示Redo log已满,需要擦除掉前面的数据
2、Redo log LSN机制
一般可以从LSN(Log Sequence Number 表示日志的逻辑序号列)中获取到如下信息:
- Redo Log写入的总量
- checkpoint位置,也就是检查点的具体位置
- 数据页版本相关的信息 如果页中的LSN值小于Redo log中的LSN值,则表示丢失了一部分数据,此时,可以通过Redo Log的记录来恢复到数据。否则,不需要恢复数据。 可以在通过如下命令查看LSN值:
showengineinnodbstatus
重点观察如下参数:
Log sequence number:当前内存缓冲区的redo log 的LSN值。
Log flushed up to:表示的是刷新到磁盘上的redo log文件的LSN。
Pages flushed up to:已经刷新到磁盘数据页上的LSN。
Lastcheckpoint at:上一次检查点所在位置的LSN。
3、Redo log 相关参数
查看Redo log相关的参数
show variables like'%innodb_log%'
其中重要的参数有:
log buffer 的大小:innodb_log_buffer-size
事务日志的大小:innodb_log_file_size
事务日志组中的事务日志文件的个数(默认为2):innodb_log_files_group=2
事务日志组所在的目录:innodb_log_group_home_dir
十一、Undo Log基本概念与存储方式
Mysql的一致性是由undolog实现的。
Undo Log基本概念:
- Undo Log在MySQL事务的实现中,主要起到两方面的作用:回滚事务和多版本并发事务,也就是常说的MVCC机制。
- Undo Log与Redo Log不同,Undo Log记录的是逻辑日志。可以这样理解:当数据库执行一条insert语句时,Undo Log会记录一条对应的delete语句;当数据库执行一条delete语句时,Undo Log会记录一条对应的insert语句;当数据库执行一条update语句时,Undo Log会记录一条相反的update语句。
- Undo Log的完整性和可靠性需要 Redo log来保证,所以数据库崩溃需要先做Redo log 数据恢复,然后做undo log回滚,
Undo Log存储方式:
- 在MySQL中,InnoDB存储引擎对于Undo Log的存储采用段的方式进行管理
- 在MySQL中,InnoDB存储引擎对于Undo Log的存储采用段的方式进行管理 Undo Log默认存放在共享数据表空间中,默认就是ibdata1文件中。如果开启了innodb_file _per_table参数,就会将Undo Log存放在每张数据表的.ibd文件中。
十二、Undo Log 基本原理
1、Undo Log 持久化原理
- 写入时经过了用户空间与内核空间(如果使用O_DIRECT标志位,可以不经过内核空间)。
2、案例
- 当mysql提交事务之前,innodb会将数据表中修改前的数据保存到undo log buffer中,undo log buffer中的数据会持久化到磁盘上的undo log file中
- 当数据库发生故障、重启或者事务回滚时,innodb存储引擎就会读取undo log中的数据,将事务还未提交的数据回滚到最初的状态,同时系统根据需要查询加载订单表中的数据(也就是加载order.idb文件中的数据),也可以向订单表中写入数据,也就是将数据持久化到order.ibd文件中。
十三、Undo Log 实现MVCC机制
Undo log的字面意思是撤销操作的日志,指的是使mysql中的数据回到某个状态。在事务开启中(Undo Log是InnerDB独有的),mysql会将待修改的记录保存到Undo Log中。如果数据库崩溃或者事务需要回滚时,mysql可以通过利用Undo log日志,将数据库中的事务回滚到之前的状态。mysql新增、修改、删除数据时,在事务开启前,就会将信息写入Undo Log中,事务提交时,并不会立刻删除Undo Log,InnoDB存储引擎会将事务对应的Undo Log放入待删除列表中,之后会通过后台的purge thread对待删除的列表进行删除操作处理。
注意的是Undo log是一种逻辑日志,记录的是一种逻辑过程。比如mysql执行delete操作,Undo log就会记录一个insert操作;mysql执行一个insert操作,Undo Log就会记录一个delete操作;mysqlk执行update操作,Undo Log记录一个相反的update操作。
Undo Log以段的方式来管理记录日志信息,在InnoDB存储引擎的数据文件中,包含了一种rollback segment的回滚段,内部包含了1024个undo log segment 。
Undo Log实现了事务的原子性和多版本并发控制(MVCC)。原子性——Mysql出现了错误、或者手动执行了事务的回滚,Undo Log会将数据库中的数据恢复到之前的状态。下图是Undo log实现多版本并发控制,事务A未提交之前,undo log保存了未提交之前的版本,事务B读取的是之前的版本信息。
版权归原作者 希境 所有, 如有侵权,请联系我们删除。