数据库事务

事务:数据库区别于文件系统的重要特性之一,当我们有了事务就会让数据库始终保持一致性,同事还能通过事务的机制恢复到某个时间点;一组逻辑操作单元,使数据从一种状态变换到另一种状态

事务处理的原则:保证所有的事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交,那些修改就永久被保存下来;要么数据库管理系统放弃所作的所有修改,整个事务回滚到最初状态

事务的特性(ACID)

原子性(atomicity):事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚

一致性(consitency):事务执行前后,事务从一个合法状态变换到另一个合法性的状态

隔离性(isolation):一个事务的执行不能被其他事务干扰,即一个事物内部操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰

持久性(durability):一个事务一旦被提交,对数据库的改变就是永久性的,通过事物的日志进行保证

总:原子性是基础,隔离性是手段,一致性是约束条件,持久性是目的

事务的状态:

1、活动的:事务对应的数据库操作正在执行过程中,我们就说该事务处在活动的状态

2、部分提交的:当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘,我们称为该事务处在部分提交的状态

3、失败的:当事务处在活动或者部分提交时,能遇到了某些错误而无法继续执行,我们就说该事务处在失败的状态

4、中止的:如果事务执行了一部分而变为失败的状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态

如何使用事务:

步骤:开启事务、一系列的DML操作、事物的结束操作

显式事务:



start transaction
或者 begin
 

隐式事务:



autocommit
 

MySql中completion_type参数的作用:

1、completion=0,这是默认情况。当我们执行COMMIT的时候会提交事务,再执行下一个事务时,还需要使用start transaction或者begin 来开启

2、completion=1,这种情况下,当我们提交事务后,相当于执行了commit and chain,也就是开启了一个链式事务,即当我们提交事务之后会开启一个相同隔离级别的事务

3、completion=2,当提交后,会自动与服务器断开连接

事务的隔离级别

数据并发问题:

1)脏写:对于两个事务SessionA、SessionB,如果事务SessionA修改了另一个未提交SessionB修改过的数据,就意味发生了脏写

2)脏读

3)不可重复读:事务SessonA、SessionB,SessionA读取了一个字段,然后SessionB更新了该字段,之后SessionA再次读取同一个字段,值就不同了,那就意味着发生了不可重复读

4)幻读:针对的是插入操作

四种隔离级别:脏写、脏读、不可重复读、幻读

解决:

数据库事务

读未提交:解决脏写问题但是另外三个问题解决不了

越往下说明其隔离级别越高 ,但是它的并发性越差

设置隔离级别:


Set [global|session] transaction_isolation='隔离级别'

使用global在全局范围内影响

使用session在会话范围内影响:对当前会话的所有后续的事务有效;如果在事务之间执行,则对后续的事务有效;该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务

commit之前属于内存级别还没有刷盘到磁盘级别;但是commit之后就是磁盘级别

MySQL事务日志:

事务的隔离性是由锁机制实现

而事务的原子性、一致性、持久性由事务的redo日志和undo日志来保证

REDO  LOG:重做日志,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性

UNDO LOG:回滚日志,回滚行记录到某个特定版本,用来保证事务的原子性、一致性

REDO和UNDO都是一种恢复操作

REDO:是存储引擎层生成的日志,记录的是"物理级别"上的页修改操作

UNDO:是存储引擎层生成的日志,记录的是逻辑操作日志

REDO日志:

数据库事务

好处:redo日志降低了刷盘频率;并且占用的空间非常小

特点:redo日志是顺序写入磁盘的;事务执行过程中,redo不断记录

redo的组成:Redo可以分成以下两个部分:

1)重做日志的缓冲(redo log buffer),保存在内存中,是易失的

2)重做日志文件(redo log file):保存在硬盘中,是持久的

步骤:数据库事务

redo log刷盘策略(也就是上面的第三步):redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入文件系统缓存中去

redo log buffer:InnoDB的更新操作采用的是Write Ahead Log(预先日志持久化)策略,即先写日志,再写入磁盘

UNDO日志:undo log是事务原子性的保证。在事务中更新数据的前置操作其实是要先写入一个undo log作用:

1、回滚数据:undo用于将数据库物理地恢复到执行语句或事务之前的样子是不可能的。Undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子

2、MVCC:InnoDB存储引擎中的MVCC实现是通过undo来完成的

UNDO存储结构:InnoDB对undo log的管理采用段的方式,也就是回滚段

回滚段与事务

1、每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务

2、当一个事务开始时,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段

回滚段中的数据分类:

1、未提交的回滚数据:该数据所关联的事务并未提交,用于实现读一致性,所以该数据不能被其他事务的数据覆盖

2、已经提交但未过期的回滚数据:该数据关联的事务已经提交,但是仍收到undo retention参数的保持时间的影响

3、事务已经提交并过期的数据:事务已经提交,而且数据保存时间已经超过undo retention参数指定的时间,属于已经过期的数据

事务提交之后并不能马上删除undolog以及undo log所在的页

:计算机协调多个进程或者线程并发访问某一资源的机制,需要保证该数据在任何时刻最多只有一个线程在访问,保证数据的完整性和一致性,为了保证数据的一致性,需要对并发操作进行控制,产生了锁,锁冲突也是影响数据库并发访问性能的一个重要因素

并发事务访问相同记录:

1、读-读情况:并发事务相机读取相同的记录,不会产生任何影响

2、写-写情况:在这种情况下会发生脏写的问题,任何一种隔离级别都不允许这种问题的发生.

3、读-写情况:这种情况下会发生脏读、不可重读、幻读

并发问题的解决:(主要解决读-写问题)

方案一:读操作利用MVCC(多版本并发控制),写操作进行加锁

MVCC,就是生成一个ReadView,通过ReadView找到符合条件的记录版本。查询语句只能读到在生成ReadView之前已提交事务所做的更改

数据库事务

方案二:读、写操作都加锁的方式

锁的分类:

数据库事务

在读锁和写锁中,只有共享锁和共享锁兼容,其余不兼容;

表级锁、页级锁、行锁:由于数据库系统需要在并发响应和系统性能两方面进行平衡,这就产生了——锁粒度;粒度越细说明并发性能越好

表锁:锁定整张表,开销最小,避免死锁的问题,但是并发性能比较差

表级别的共享锁和排他锁:

数据库事务

意向锁:InnoDB支持多粒度锁,它允许行级锁和表级锁共存,而意向锁就是其中的一种表锁

解决的问题:如果我们给某一行数据加上了排它锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了,它分为意向的共享锁和意向排它锁;为了协调行锁和表锁的关系

元数据锁(MDL锁):当对一个表增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁

行锁(InnoDB):

优点:锁定力度小,发生锁冲突概率低,可以实现的并发度高

缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁

记录锁:把锁加在一条条的数据上

间隙锁:解决幻读的问题,两种解决方案一个是MVCC一个是加锁的方式。它的提出仅仅是为了防止插入幻影记录而提出的

数据库事务

如果上图中id=8的记录加了锁,意味着不允许别的事务在id值为8的记录前边的间隙插入新记录,其实就是id列的值(3,8)这个区间的新纪录是不允许立即插入的

临键锁:本质是一个记录锁和一个临键锁的合体,它既能保护该记录,又能组织别的事务将新纪录插入被保护记录前边的间隙

意向锁:InnoDB规定事务在等待的时候也需要在内存中生成一个锁结构

页锁:对待锁的态度上分为悲观锁、乐观锁(在页的粒度上)

悲观锁:对数据被其他事务的修改持保守态度,会通过数据库自身的锁机制来实现,从而保证数据操作的排它性;每次去拿数据的时候都认为别人会去修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁

select…for update语句在执行过程中所有扫描的行都会被锁上,因此在Mysql中用悲观锁必须确定使用了索引,而不是全表扫描,否则将会把整个表锁住

乐观锁:不采用数据库自身的锁机制,而是通过程序来实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,通过两种方式进行实现(乐观锁的版本号机制和乐观锁的时间戳机制)

产生死锁的必要条件:
1、两个或者两个以上的事务

2、每个事务都已经持有锁并且申请新的锁

3、锁资源同时只能被同一个事务持有或者不兼容

4、事务之间因为持有锁和申请锁导致彼此循环等待

解决死锁

1、等待,直到超时;2、使用死锁检测进行死锁处理

锁结构

数据库事务

锁所在的事务信息:不论是表锁还是行锁,都是在事务执行过程中生成的,哪个事务生成了这个锁结构,这里就记录这个事务的信息

索引信息:对于行锁来说,需要记录一下加锁的记录属于哪个索引的

MVCC解决读写的问题(多版本并发控制):通过数据行的多个版本管理来实现数据库的并发控制

实现依赖于隐藏字段、Undolog、read view;解决的是读已提交和可重复读隔离级别的事务

readview:ReadView就是事务A在使用MVCC机制进行快照读操作时产生的读视图。当事务启动时,会生成数据库系统当前的一个快照,InnoDB为每个事务构造了一个数组,用来记录并维护系统当前活跃事务的ID

整体操作流程:

1、首先获取事务自己的版本号,也就是事务ID

2、获取ReadView

3、查询得到的数据,然后与ReadView中的事务版本号进行比较

4、如果不符合ReadView规则,就需要从UndoLog中获取历史快照

5、最后返回符合规则的数据

ReadCommited:每次读一次就会生成一个ReadView

使用Repeatable Read隔离级别的事务来说,只会在第一次执行查询语句时生成一个ReadView,之后的查询就不会重复生成了

© 版权声明

相关文章

暂无评论

none
暂无评论...