Mysql 事务总结

事务

InnoDB引擎下才支持事务功能。

一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元,只有当事务中的所有操作都正常执行完了,整个事务才会被提交给数据库;如果有部分事务处理失败,那么事务就要回退到最初的状态,因此,事务要么全部执行成功,要么全部失败。

buffer pool机制

首先需要理解sql语句的执行过程:

学习这些原理之前我们要先知道MySQL的Buffer Pool机制。

InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证,所以我们需要使用mysql中的日志来保证数据持久性

无论sql操作的是缓存还是文件 最终都会对数据造成持久性的影响。不可逆的。

set autocommit = 0; 开启事务

事务错误:

使用事务可能发送的问题:

脏读(Dirty Read):一个事务读取了另一个事务未提交的数据。

例如当事务A和事务B并发执行时,当事务A更新后,事务B查询读取到A尚未提交的数据,
此时事务A回滚,则事务B读到的数据就是无效的脏数据。(事务B读取了事务A尚未提交的数据)

不可重复读(NonRepeatable Read): 一个事务的操作导致另一个事务前后两次读取到不同的数据。

例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A更新操作更改事务B查询到的数据,此时事务B再次去读该数据,发现前后两次读的数据不一样。(事务B读取了事务A已提交的数据)

虚读(Phantom Read)/幻读:一个事务的操作导致另一个事务前后两次查询的结果数据量不同。

例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。(事务B读取了事务A新增加的数据或者读不到事务A删除的数据)

锁和MVCC机制:

有这样几种锁我们需要了解,虽然这列出了很多,但大家要仔细看仔细理解这知识从不同维度对锁的分类,而起了一些不同的名字罢了所以不要被吓到!

Record Locks(记录锁): 在索引记录上加锁。

Gap Locks(间隙锁): 在索引记录之间加锁,或者在第一个索引记录之前加锁,或者在最后一个索引记录之后加锁。

Next-Key Locks: 在索引记录上加锁,并且在索引记录之前的间隙加锁。它相当于是Record Locks与Gap Locks的一个结合

共享读锁和排他写锁:

共享锁(S): 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。

排他锁(X): 允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的。

行锁和表锁:

表级锁: 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

行级锁: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高

MVCC机制:

所谓的mvcc机制就是尽量的不去使用这些锁,一旦这些锁加上之后事务与事务之间就变成了完全的串行执行,在隔离级别中串行化其实就是这样做的。这里又要涉及到我们之前所学的乐观锁和悲观锁。

InnoDB的一致性的非锁定读就是通过在MVCC实现的,Mysql的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。MVCC的实现,是通过保存数据在某一个时间点的快照来实现的,每一个事务只操作自己版本下的数据。因此每一个事务无论执行多长时间看到的数据,都是一样的。所以MVCC实现可重复读。

隔离级别

把这些机制了解清楚之后,我们来说一下隔离性实现的原理。首先隔离性一共分了4种隔离级别。分别是:未提交读,已提交读,可重复读,和序列化

未提交读(Read uncommitted):

这种级别,数据库一般都不会用,而且任何操作都不会加锁,这里就不讨论了。

已提交读(Read committed):

在RC级别中,数据的读取都是不加锁的,但是数据的写入、修改和删除是需要加锁的(Record Locks)。那么已提交读时如何解决脏读问题的?这个就利用了mvcc机制,

在这个级别下,mvcc没有commit提交修改的是主内存的数据

可重复读(Repeatable read):

数据的读取都是不加锁的,但是数据的写入、修改和删除是需要加锁(Record Locks)的+Mvcc机制

这个级别下,mvcc机制即使commit提交后修改的也是自己版本的数据

序列化(Serializable):

这个级别很简单,读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,(加的锁就是Next-Key Locks)实现简单,数据更加安全,但是并发能力非常差。如果你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可以使用这种模式。由于InnoDB引擎加的是行锁,并且是加在索引记录上,所以为了不产生幻读在还会加间隙锁。

隔离级别 脏读 不可重复读 幻读
未提交读 可以 可以 可以
已提交读 不可以 可以 可以
可重复读 不可以 不可以 可以
串行化 不可以 不可以 不可以

由于多个线程会请求相同的数据,事务之间通常都会用锁互相隔离,由于数据库支持不同类型的锁,因此Java JDBC支持不同级别的事务处理,它们由Connection对象指定。在JDBC中,定义了以下5种事务隔离级别:

  1. TRANSACTION_NONE。 表示不支持事务

  2. TRANSACTION_READ_UNCOMMITTED。未提交读。
    说明在提交前一个事务可以看到另一个事务的变化。这样读”脏”数据,不可重复读和虚读都是被允许的。

  3. TRANSACTION_READ_COMMITTED。已提交读。
    说明读取未提交的数据是不允许的。这个级别仍然允许不可重复读和虚读产生。

  4. TRANSACTION_REPEATABLE_READ。可重复读。
    说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。

  5. TRANSACTION_SERIALIZABLE。可序列化/串行化。
    是最高的事务级别,它防止读脏数据,不可重复读和虚读。

事务的特性

总结事务共有几大特性?

A C I D 特性

事务的原子性(Atomic): undo log 日志

事务是一个不可分割的整体,事务必须具有原子特性,及当数据修改时,要么全执行,要么全不执行,即不允许事务部分的完成。 rollback; commit;

事务的隔离性(Isolation): 锁 + MVCC 机制

当两个或者多个事务并发执行时,为了保证数据的安全性,将一个事物内部的操作与其它事务的操作隔离起来,不被其它正在执行的事务所看到。

例如对任何一对事务T1和T2,对T1而言,T2要么在T1开始之前已经结束,要么在T1完成之后再开始执行。隔离性使得每个事务的更新在它被提交之前,对其它事务都是不可见的。让我们看看事务处理之父Jim Gray对事务隔离性的定义:

Isolation: Concurrently executing transactions see the stored information as if they were running serially (one after another).

事务的持久性(Durability): 原理 redo log

commit之后 清空undo log 数据持久性修改 不可逆

事务完成以后,DBMS保证它对数据库中的数据的修改是永久性的,即使数据库因为故障出错,也应该能够恢复数据!

事务的一致性(Consistency):持久性+隔离性+原子性共同实现了数据的一致性。

一个事务执行之前和执行之后,数据库数据必须保持一致性状态。数据库的一致性状态必须由用户来负责,由并发控制机制实现。就拿网上购物来说,你只有让商品出库,又让商品进入顾客的购物车才能构成一个完整的事务!由于并发操作带来的数据不一致性包括读脏数据(脏读),不可重复读和虚读(幻读)。


详解:

持久性实现原理:redo log 日志

当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。

原子性实现原理:undo log 日志

实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。undo log属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态

发布了25 篇原创文章 · 获赞 12 · 访问量 2901

猜你喜欢

转载自blog.csdn.net/ALone_cat/article/details/104810154