1、base:ACID属性,并发控制
2、MySql事务的隔离级别有哪些,含义是什么?
3、锁知多少,读锁,写锁,排他锁,共享锁,间隙锁,乐观锁,悲观锁。
4、Mysql的事务与锁有什么关联?MySq中的事务实例。
1.1 ACID属性,多版本并发控制
在数据库汇总,事务可以看作是一组SQL语句组成的逻辑处理单元,事务主要具有以下4个属性,简称ACID属性:
原子性(Atomicity):事务是一个原子操作单元,其内部的SQL语句,要么全部成功,要么全部失败。
一致性(Consistent):在事务开始和完成后,数据都必须保持一致状态。
隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的独立环境下执行。这要求事务处理过程的中间状态对外部是不可见的。
持久性(Durable):事务完成后,它对数据库的修改是永久性的,即使出现系统故障也能够保持。
1.2 多版本并发控制
MySQL的大多数的事务型存储引擎实现的都不是简单的行级锁,基于提升并发性能的考虑,都引入了多版本并发控制技术(MVCC),可以认为MVCC是行级锁的一个变种,在很多情况下可以避免加锁的操作,让开销更低。InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。一个保存的行的创建时间,一个保存行过期时间(或删除时间)。存储的不是实际的时间值,是系统的版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本的版本号,用来和查询列的每行记录的版本号进行比较。
MVCC只在已提交读(Read Committed)和可重复读(Repeatable Read)两个隔离级别下工作,其他两个隔离级别和MVCC是不兼容的。因为未提交读,总数读取最新的数据行,而不是读取符合当前事务版本的数据行。而串行化(Serializable)则会对读的所有数据多加锁,下面以可重复读(Repeatable Read)为例子,说明下以select、delete、 insert、 update语句MVCC具体是如何操作的。
SELECT
Innodb检查每行数据,确保他们符合两个标准:
1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行
2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除符合了以上两点则返回查询结果。
INSERT
InnoDB为每个新增行记录当前系统版本号作为创建ID。
DELETE
InnoDB为每个删除行的记录当前系统版本号作为行的删除ID。
UPDATE
InnoDB复制了一行。这个新行的版本号使用了系统版本号。它也把系统版本号作为了删除行的版本。
1.3 事务的一致性和数据库的一致性读
一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见。强调的是当前事务在未提交之前的任何修改对其他事务都是不可见的。
读一致性是数据库一致性的一个重要方面,跟事务的一致性是2个不同的概念。数据库的读一致,因为innoDB采用了多版本控制技术(MVCC),为查询提供了一个该时间点的数据快照。从时间的角度来看,查询可以看到该时间点之前已经提交的更新,但是不能看到该时间点之后的更新以及尚未提交的事务所做的更新。
在事务的隔离级别为已提交读(Read Committed),读一致性是语句级别的,意味着同一个事务执行2次相同的查询,返回的结果存在不一致的问题,因为只要别的事务修改后提交了,在该隔离级别下再次查询都是最新的所有数据。
在事务的隔离级别为可重复读(Repeatable Read)时。读一致性是事务级别的,同一个事务无论执行多少次查询,无论别的事务在其过程中怎么修改数据,读到的永远是第一次读取到的快照。
2.1 事务并发时有可能产生的问题
由于数据库在处理过程中是并发进行处理的,因此在事务并发执行过程中,可能会带来如下一些问题:
更新丢失(Lost Update)
当多个事务对同一条数据库记录进行操作时,由于每个事务不知道其他事务的存在,如果其中一个事务读取了数据库记录后,该记录又被另外一个事务进行了更新,那么该事务基于最初读取到的值进行一些运算并将运算结果写回数据库时就可能会出现问题:中间事务的更新被覆盖,从而导致更新丢失的问题。
解决方法:使用InnoDB存储引擎时,主要通过排他锁避免更新丢失的问题,这就要求所有更新都遵循一个基本原则:在更新数据库记录前获得该记录的排他锁。对于某些特殊的应用,在业务允许的情况下也可以使用增量更新的方式进行优化:即对某些字段进行A+=X之类的操作,利用Mysql更新语句会默认获得行锁的特性去避免更新丢失。
脏读(Dirty Reads)
事务A在某个步骤中修改了一条记录,在A完成并提交前,该记录处于一个中间状态,此时,另外一个事务如果读取同一条记录,如果该事务读到这个中间状态,那么就有可能会出现数据不一致状态,这种现象叫做脏读。
解决方法:使用InnoDB存储引擎时,可以通过共享锁或者排他锁避免脏读,这就要求所有更新操作都遵循一个基本原则:在读取数据库记录前获得该记录的排他锁获取共享锁。此外,还可以通过设置InnoDB的事务隔离级别来避免脏读的问题。
不可重复读(Non-Repeatable Reads)
事务A在读取了某条记录后,该记录又被另外一个事务进行了修改,此时,如果事务A再次读取该记录时,如果该记录的数据和它首次读取的记录不一致了,那么这种现象就叫做不可重复读,它与脏读的区别是:脏读是读到事务中间过程的临时数据,数据处于一个临时的非稳定状态,可能被该事务再次修改或者回滚;而不可重复读是由于两次读取时间点的间隙中,数据被其他事务修改而导致的数据不一致,这种修改对数据库的影响是永久性的。
解决方法:使用InnoDB存储引擎时,可以通过设置InnoDB的事务隔离级别来控制重复读的特性,关于事务隔离级别,后续将进行详细描述。
幻读(Phantom Reads)
幻读出现在多条记录的查询中。当事务A用同样的查询条件读取之前某个点检索过的数据时,检索到了被其他事务在其间插入的新记录(或者少了被其他事务删除的记录),这种现象就称为幻读。
解决方法:幻读和不可重复读的本质相同,都是由于两次读取间隙中由于其他事务的操作导致的记录数据变更,其区别是:不可重复读一般是针对单条记录的,幻读是针对一个结果集而言。
事务并发问题不能单靠数据库事务控制来解决,通常需要结合InnoDB的锁机制来解决,因此,防止出现事务并发问题是应用层面需要解决的问题,不能单纯依靠事务机制。但是脏读、不可重复读、幻读是数据库读一致性的问题,需要由数据库提供一定的事务隔离机制进行解决。
2.2 事务的隔离级别
事务就是一组原子性的SQL查询,或者说一个独立的工作单元,如果数据库引擎能成功对数据库应用该组查询的全部语句,那么事务内的语句,要么全部执行成功,要么全部执行失败,如果其中有任何一条语句因为崩溃或者其他原因无法执行或者执行失败,那么其他的语句就都不会执行。在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。
下面用表格简单表述下:
隔离级别
读一致性
脏读
不可重复读
幻读
未提交读(Read Uncommitted)
最低级别
是
是
是
已提交读(Read Committed)
语句级别
否
是
是
可重复读(Repeatable Read)
事务级别
否
否
是
串行化(Serializable)
最高级别,事务级
否
否
否
未提交读(READ UNCOMMITTED)
在未提交读模式下,SELECT语句以不加锁方式提交执行,而且有可能使用之前较早版本的数据。通俗的可以理解为可能读到其他事务未提交的中间状态数据,这个中间数据有可能被重新修改或者被回滚。所以在该模式下,数据库查询是不符合一致性读的原则。
已提交读(READ COMMITTED)
在事务的读取操作点上可以看到该事务开始后到读取操作点之间被其他事务提交的修改。只要其他事务提交后的数据,当前事务在任一点都能看见。
可重复读(REPETABLE READ)
在一个事务内部,对记录的读取有以下特性,所有一致性读是读取由第一次读所确定的同一快照。也就是意味着,在同一个事务内,以同样的查询条件执行查询均以第一次查询的结果为准,哪怕后续有其他事务对记录进行过更改。
可序列化(SERIALIZABLE)
可序列化是读一致性的最高级别, 该级别与可重复读类似,但是在该级别下,即使是普通的不加锁查询,In