MySQL事务,事务隔离级别

一、数据库事务
1、事务是作为单个逻辑工作单元执行的一系列操作。可以是一条SQL语句也可以是多条SQL语句。

2、事务具有四个特性

原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。

3、启动事务:使用 API 函数和 Transact-SQL 语句,可以按显式、自动提交或隐式的方式来启动事务。

4、结束事务:您可以使用 COMMIT(成功) 或 ROLLBACK(失败) 语句,或者通过 API 函数来结束事务。

5、创建事务的原则

尽可能使事务保持简短很重要,当事务启动后,数据库管理系统 (DBMS) 必须在事务结束之前保留很多资源、以保证事务的正确安全执行。
特别是在大量并发的系统中, 保持事务简短以减少并发 资源锁定争夺,将显得更为重要。
a、事务处理,禁止与用户交互,在事务开始前完成用户输入。
b、在浏览数据时,尽量不要打开事务
c、尽可能使事务保持简短。
d、考虑为只读查询使用快照隔离,以减少阻塞。
e、灵活地使用更低的事务隔离级别。
f、灵活地使用更低的游标并发选项,例如开放式并发选项。
g、在事务中尽量使访问的数据量最小。

二、事务的隔离级别

1、事务常见的四种隔离级别


image.png

事务的隔离级别和数据库并发性是对立的,两者此增彼长。一般来说,使用READ UNCOMMITED隔离级别的数据库拥有最高的并发性和吞吐量,而使用SERIALIZABLE隔离级别的数据库并发性最低。

三 事务隔离性的实现——常见的并发控制技术
并发控制技术是实现事务隔离性以及不同隔离级别的关键,实现方式有很多,按照其对可能冲突的操作采取的不同策略可以分为乐观并发控制和悲观并发控制两大类。
乐观并发控制:对于并发执行可能冲突的操作,假定其不会真的冲突,允许并发执行,直到真正发生冲突时才去解决冲突,比如让事务回滚。
悲观并发控制:对于并发执行可能冲突的操作,假定其必定发生冲突,通过让事务等待(锁)或者中止(时间戳排序)的方式使并行的操作串行执行。

3.1 基于封锁的并发控制
核心思想:对于并发可能冲突的操作,比如读-写,写-读,写-写,通过锁使它们互斥执行。
锁通常分为共享锁和排他锁两种类型
1.共享锁(S):事务T对数据A加共享锁,其他事务只能对A加共享锁但不能加排他锁。
2.排他锁(X):事务T对数据A加排他锁,其他事务对A既不能加共享锁也不能加排他锁
基于锁的并发控制流程:
事务根据自己对数据项进行的操作类型申请相应的锁(读申请共享锁,写申请排他锁)
申请锁的请求被发送给锁管理器。锁管理器根据当前数据项是否已经有锁以及申请的和持有的锁是否冲突决定是否为该请求授予锁。
若锁被授予,则申请锁的事务可以继续执行;若被拒绝,则申请锁的事务将进行等待,直到锁被其他事务释放。
可能出现的问题:
死锁:多个事务持有锁并互相循环等待其他事务的锁导致所有事务都无法继续执行。
饥饿:数据项A一直被加共享锁,导致事务一直无法获取A的排他锁。
对于可能发生冲突的并发操作,锁使它们由并行变为串行执行,是一种悲观的并发控制。

3.2 基于时间戳的并发控制
核心思想:对于并发可能冲突的操作,基于时间戳排序规则选定某事务继续执行,其他事务回滚。
系统会在每个事务开始时赋予其一个时间戳,这个时间戳可以是系统时钟也可以是一个不断累加的计数器值,当事务回滚时会为其赋予一个新的时间戳,先开始的事务时间戳小于后开始事务的时间戳。
每一个数据项Q有两个时间戳相关的字段:
W-timestamp(Q):成功执行write(Q)的所有事务的最大时间戳
R-timestamp(Q):成功执行read(Q)的所有事务的最大时间戳
时间戳排序规则如下:
假设事务T发出read(Q),T的时间戳为TS
a.若TS(T) read操作将被拒绝,T回滚。
b.若TS(T)>=W-timestamp(Q),则执行read操作,同时把
R-timestamp(Q)设置为TS(T)与R-timestamp(Q)中的最大值
假设事务T发出write(Q)
a.若TS(T) b.若TS(T) c.其他情况:系统执行write操作,将W-timestamp(Q)设置
为TS(T)。
基于时间戳排序和基于锁实现的本质一样:对于可能冲突的并发操作,以串行的方式取代并发执行,因而它也是一种悲观并发控制。它们的区别主要有两点:
基于锁是让冲突的事务进行等待,而基于时间戳排序是让冲突的事务回滚。
基于锁冲突事务的执行次序是根据它们申请锁的顺序,先申请的先执行;而基于时间戳排序是根据特定的时间戳排序规则。

3.3 基于有效性检查的并发控制
核心思想:事务对数据的更新首先在自己的工作空间进行,等到要写回数据库时才进行有效性检查,对不符合要求的事务进行回滚。
基于有效性检查的事务执行过程会被分为三个阶段:
读阶段:数据项被读入并保存在事务的局部变量中。所有write操作都是对局部变量进行,并不对数据库进行真正的更新。
有效性检查阶段:对事务进行有效性检查,判断是否可以执行write操作而不违反可串行性。如果失败,则回滚该事务。
写阶段:事务已通过有效性检查,则将临时变量中的结果更新到数据库中。
有效性检查通常也是通过对事务的时间戳进行比较完成的,不过和基于时间戳排序的规则不一样。
该方法允许可能冲突的操作并发执行,因为每个事务操作的都是自己工作空间的局部变量,直到有效性检查阶段发现了冲突才回滚。因而这是一种乐观的并发策略。

3.4 基于快照隔离的并发控制
快照隔离是多版本并发控制(mvcc)的一种实现方式。
其核心思想是:数据库为每个数据项维护多个版本(快照),每个事务只对属于自己的私有快照进行更新,在事务真正提交前进行有效性检查,使得事务正常提交更新或者失败回滚。
由于快照隔离导致事务看不到其他事务对数据项的更新,为了避免出现丢失更新问题,可以采用以下两种方案避免:
先提交者获胜:对于执行该检查的事务T,判断是否有其他事务已经将更新写入数据库,是则T回滚否则T正常提交。
先更新者获胜:通过锁机制保证第一个获得锁的事务提交其更新,之后试图更新的事务中止。
事务间可能冲突的操作通过数据项的不同版本的快照相互隔离,到真正要写入数据库时才进行冲突检测。因而这也是一种乐观并发控制。

理论的东西就搬这么多,我不是dba,对MySQL源码也没什么研究,就结合我自己的实践来说一下吧。
MySQL默认的事务隔离级别是可重复读,应该是通过快照来实现的,该快照是连接数据库开启事务时整个database的快照,所以在一个事务内,如果只是查询,那么无论查询多少次,只要SQL语句一样,返回的结果都是一样的。而如果在自身事务内进行了insert,update,delete操作(其他事务没有获取相关锁),那么按照事务的要求,其他的事务不能读取到本事务内还未提交的修改,但是在自身事务内,可以查询到自身所作的修改,同时不能读取到其他事务已经提交的修改。但是MySQL同时使用到了锁,只读的SQL就不说了,每次都一样,接下来仔细说下insert,update,delete等操作时如何保证隔离性
insert:上面也说过了,MySQL开启事务的时候使用到了快照,在事务内如果进行插入操作,那么需要获取写锁,例如插入id=1的一条数据,如果需要获取id=1的写锁,如果其他事务同样插入id=1的数据,其他事务首先获取到了写锁,那么本身事务会进行等待,等待锁的释放,而写锁的释放需要等到事务的提交或者回滚,如果其他事务成功提交,本身事务返回失败,因为主键重复,如果其他事务因为异常回滚,本身事务获取写锁,可以进行插入操作
update,delete:操作相关数据时候,同样需要获取写锁,如果其他事务已经获取写锁,则等待。
说明一点:事务提交或者回滚,持有的锁释放。

你可能感兴趣的:(MySQL事务,事务隔离级别)