02深入理解事务与锁机制

2020-10-27  本文已影响0人  owen_he

事务及其特性

什么是事务?事务具有哪些特性?事务是指作为单个逻辑工作单元执行的一系列操作,这些操作要么全做,要么全不做,是一个不可分割的工作单元。

一个逻辑工作单元要成为事务,在关系型数据库管理系统中,必须满足 4 个特性,即所谓的 ACID:原子性、一致性、隔离性和持久性。

一致性

一致性其实包括两部分内容,分别是约束一致性和数据一致性。

原子性

接“要么”,即要么改了,要么没改。也就是说用户感受不到一个正在改的状态。MySQL 是通过 WAL(Write Ahead Log)技术来实现这种效果的。

举例来讲,如果事务提交了,那改了的数据就生效了,如果此时 Buffer Pool 的脏页没有刷盘,如何来保证改了的数据生效呢?就需要使用 Redo 日志恢复出来的数据。而如果事务没有提交,且 Buffer Pool 的脏页被刷盘了,那这个本不应该存在的数据如何消失呢?就需要通过 Undo 来实现了,Undo 又是通过 Redo 来保证的,所以最终原子性的保证还是靠 Redo 的 WAL 机制实现的。

持久性

所谓持久性,就是指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,接下来的操作或故障不应该对其有任何影响。

持久性是如何保证的呢?一旦事务提交,通过原子性,即便是遇到宕机,也可以从逻辑上将数据找回来后再次写入物理存储空间,这样就从逻辑和物理两个方面保证了数据不会丢失,即保证了数据库的持久性。

隔离性

所谓隔离性,指的是一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他的并发事务是隔离的。锁和多版本控制就符合隔离性。

并发事务控制

单版本控制-锁

先来看锁,锁用独占的方式来保证在只有一个版本的情况下事务之间相互隔离,所以锁可以理解为单版本控制。

多版本控制-MVCC

多版本控制也叫作 MVCC,是指在数据库中,为了实现高并发的数据访问,对数据进行多版本处理,并通过事务的可见性来保证事务能看到自己应该看到的数据版本。多版本控制很巧妙地将稀缺资源的独占互斥转换为并发,大大提高了数据库的吞吐量及读写性能。

特性背后的技术原理

原子性背后的技术

先来看看原子性,每一个写事务,都会修改 Buffer Pool,从而产生相应的 Redo 日志,这些日志信息会被记录到 ib_logfiles 文件中。因为 Redo 日志是遵循 Write Ahead Log 的方式写的,所以事务是顺序被记录的。在 MySQL 中,任何 Buffer Pool 中的页被刷到磁盘之前,都会先写入到日志文件中,这样做有两方面的保证。

持久性背后的技术

再来看持久性,如下图所示,一个“提交”动作触发的操作有:
binlog 落地、发送 binlog、存储引擎提交、flush_logs, check_point、事务提交标记等。这些都是数据库保证其数据完整性、持久性的手段。通过原子性可以保证逻辑上的持久性,通过存储引擎的数据刷盘可以保证物理上的持久性。

隔离性背后的技术

接下来看隔离性,InnoDB 支持的隔离性有 4 种,隔离性从低到高分别为:读未提交、读提交、可重复读、可串行化

一致性背后的技术

一致性可以归纳为数据的完整性。根据前文可知,数据的完整性是通过其他三个特性来保证的,包括原子性、隔离性、持久性,而这三个特性,又是通过 Redo/Undo 来保证的。

如上图所示,逻辑上的一致性,包括唯一索引、外键约束、check 约束,这属于业务逻辑范畴

并发事务问题及解决方案

那么随着数据库并发事务处理能力的大大增强,数据库资源的利用率也会大大提高,从而提高了数据库系统的事务吞吐量,可以支持更多的用户并发访问。但并发事务处理也会带来一些问题,如:脏读、不可重复读、幻读

脏读

一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫作"脏读"(Dirty Reads)。

不可重复读

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫作“ 不可重复读”(Non-Repeatable Reads)。

幻读

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”(Phantom Reads)。

解决方案

产生的这些问题,MySQL 数据库是通过事务隔离级别来解决的,上文已经详细讲解过,这里再进行简单的说明。
在上文讲 MySQL 事务特性的隔离性的时候就已经详细地讲解了事务的四种隔离级别。这里要求大家能够记住这种关系的矩阵表;记住各种事务隔离级别及各自都解决了什么问题,如下图所示。

MySQL 锁分类

在 MySQL 中有三种级别的锁:页级锁、表级锁、行级锁。

三种级别的锁分别对应存储引擎关系如下图所示。

InnoDB 中的锁

InnoDB 中的锁

在 MySQL InnoDB 存储引擎中,锁分为行锁和表锁。其中行锁包括两种锁。

另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。表锁又分为三种。

InnoDB 死锁

在 MySQL 中死锁不会发生在 MyISAM 存储引擎中,但会发生在 InnoDB 存储引擎中,因为 InnoDB 是逐行加锁的,极容易产生死锁。那么死锁产生的四个条件是什么呢?

在开发或者使用数据库的过程中,如何避免死锁的产生呢?这里给出一些建议:
加锁顺序一致;

给大家一些开发建议来避免线上业务因死锁造成的不必要的影响。

上一篇 下一篇

猜你喜欢

热点阅读