MySQL的事务

MySQL的事务
提到事务我们首先想到的是事务的四个特性:ACID(原子性,一致性,隔离性,持久性)。事务的实现是由引擎层来实现的,因此不同的存储引擎可能对事务有不同的实现方案。比如MySQL的MyISAM搜索引擎就没有事务的概念,这也是被Innodb所替代的原因之一。
事务的四个特性
原子性:事务的所有操作在数据库中要么全部反映出来,要么全部不反 映出来
一致性:事务在之前后的数据是一致的
隔离性:多个事务在并发执行的时候,对于任何一个事务Ti和Tj,在Ti看来,Tj要么在执行之前已经完成执行,或者再Tj完成之后的开始执行。因此,每个事务都感觉不到系统中其他事务在并发的执行。
持久性:一个事务成功完成后,它对数据的改变必须是永久的,即使事务刚提交就出现了宕机了数据也不能丢失。
事务的原子性和持久性比较好理解,一致性 会更加抽象一些,对于一致性经常有个转账的例子,A给B转账,转账前后A和B的账户总和不变就是一致的。这个例子猛的一看很清楚使用原子性就能保证。答案是:不能的,原子性可以保证A的账户扣减和B账户的增加要么全部成功,要么全部失败,但是并不能保证A减少的数量等于B增加的数量,实际上为了达到一致性所以同时满足其他的三个条件。

事务的隔离性比较复杂,因为MySQL的事务可以有多种隔离级别,接下来看一下:
事务的隔离级别
当多个事务并发执行的时候,有可能出现脏读,不可重复读,幻读
脏读:事务A和事务B同时执行时,事务B可以读到事务A未提交的数据,就发生了脏读。脏读的本质其实就是事务B读到事务A未提交的数据,如果事务A发生了回滚那么事务B读到的数据就是无效的,如下面的案例所示:

事务A                                 事务B
begin                                begin
                                   
update t set value = 100
                                     select value from t
rollback
                                    commit
commit  

不可重复读:在一个事务中,多次查询会出现不同的结果,就叫不可重复读。如下面所示:事务B查到的value值不一样

事务A                               事务A
begin                              begin

update set t value = 100 
                                    select value from t
update set t value = 200 
                                   select value from t
commit                                   commit

幻读:在一个事务中就行范围查询,查询到一定范围的数据,但是这个时候又有新的数据插入,这就导致查询的数据又多了一行这就是幻读,如下所示:

事务A                          事务B
begin                         begin
							insert * from t
select value from t
							insert * from t
commit
						   commit

mysql的事务级别包括:读未提交,读已提交,可重复读,和串行化
读未提交:一个事务还没有提交就被其他事务看到,这就造成了脏读
读已提交:一个事务提交后,其更改才能被其他事务看到,解决了脏读的可能,但是出现了不可重复读。
可重复读:在一个事务中多次读取同一个数据得到的结果总是相同的,即使有其他事务更新了该数据并且事务提交成功了。可重复读解决了不可重复读的问题,但是仍有幻读的出现,INNODB搜索引擎通过多版本并发控制协议解决了幻读的出现
串行化:串行隔离级别是最严格的,事务中读操作加读锁,写操作加写锁,所以可能会出现大量的锁抢夺的场景

从上到下隔离级别越来越高效率越低,因此应根据不同的企业 业务场景选不同的事务隔离级别
查询和修改事务的隔离级别

mysql> select @@global.tx_isolation,@@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation  |
+-----------------------+-----------------+
| REPEATABLE-READ       | REPEATABLE-READ |
+-----------------------+-----------------+

设置INNODB的事务隔离级别的方法是:

set 作用域 transaction isolation level 事务隔离级别

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

mysql> set global transaction isolation level read committed; // 设定全局的隔离级别为读提交

mysql> set session transaction isolation level read committed; // 设定当前会话的隔离级别为读提交

事务的开启方法:
mysql里可以通过begin命令或start transaction来显示启动一个事务,显示启动一个事务,需要commit命令进行提交
mysql里如果没有显示执行命令开启一个事务,mysql也会再执行第一条命令的时候自动开启一个事务如果自动提交autocommit处于开启状态,那么自动开启的事务也会自动提交。那么执行一条select语句的时候,mysql首先会自动开启一个事务,并且在select语句执行后自动提交,因此在mysql里执行一条命令也是开启了一个事务
在mysql里如果执行set autocommit = 0,可以关闭事务的自动提交,如果autocommit 处于关闭状态,那么在执行select命令的时候也会开启一个事务,但是不会自动提交事务
begin命令或start transaction在执行后并不是立即开启一个事务,而是在执行第一条语句的时候才开启事务start transaction with consistent snapshot 这个执行后才是立即开启一个事务
举例说明不同隔离级别事务的影响:

create table t(k int) ENGINE = InnoDB;
insert into t values(1);
事务A					事务B
begin
1:select k from t
	                   begin;update t set k = k + 1				
2:selcet k from t
					   commit
3:selcet k from t

commit

4:selcet k from t

隔离级别是未提交读时:对于事务A,第一条查询的语句是1,第二条查询的语句是,2,第三条查询的语句也是2,第四条查询的语句是2;
隔离级别是读提交:对于事务A,第一条查询的语句是1,第二条查询的语句是,1,第三条查询的语句也是2,第四条查询的语句是2;
隔离级别是可重复读时:对于事务A第一条查询的语句是1,第二条查询的语句是1,第三条查询的语句是1,第四条查询的语句是2
隔离级别是串行化:对于事务A,第一条查询语句的结果是1,。这时事务B在执行更新操作的时候会被阻塞,这时因为事务A在执行读的时候加了读锁,事务B在执行更新操作的时候要加写锁,因为读锁和写锁冲突,因此会被阻塞,事务B只能等到事务A提交事务释放了读锁,才能进行更新。因此,事务A第1 2,3,执行查询的语句都是1,第四条执行的查询语句是2.
事务隔离性的实现:

事务的隔离级别是通过回滚日志undo log日志来实现的,对于同一条数据,innodb会存储多个版本,多个版本则是通过回滚日志(undo log)来哦实现的,将当前值回滚不同的次数就可以得到不同低版本的数据,这就是多版本并发控制协议(MVCC)。当然只有undo log 日志还不行,为了支持提交读和可重复读两种隔离级别,一个事务Ti如何知道自己应该使用哪个版本的数据呢?innodb的做法是维护有个一致性视图来实现

INNODB给每个事务维护一个唯一的事务ID,事务ID严格递增分配的,也就是后开启事务的ID 一定要比先开启事务的ID 要大。因为通过undo日志可以得到多版本的数据,可以假想在数据库中每个数据有多个版本。每个事务更新一个数据时,就会生成一个新版本的数据并且将自己的事务ID和这个版本的数据绑定在一起,用来标识这个数据的版本。

当开启一个新的事务时,INNODB会为每一个事务维护一个数组,这个数组保存了当前活跃事务的事务ID,所谓的活跃事务是指:事务已经开始,但是还未提交事务。在这个数组中最小事务的ID将其称为低水位,最大事务的ID+1称为高水位。当某个事务读取某条数据的时候,从该数据的最高版本开始,如果读得起那么就读取这个数据,如果读不起就取更低一个版本的数据,如此循环,直到读取有效的数据。

在判断读得起和读不起时有以下几种情况:
1.数据版本号大于等于事务的高水位,说明是后面的事务ID创建的,读不起;
2.数据版本号小于等于低水位,说明事务开启前就已经提交的,或者是本事务自己修改的,读不起;
3.数据的版本号介于高水位和低水位之间,如果版本号在数组里,说明是未提交的,读不起;
4.数据版本号介于高水位和低水位之间,如果该版本好不在数组里,说明是已经提交的,读得起
读提交和可重复读的区别在于。提交读每次执行语句前更新这个数组,这样已经提交的数据就不在数组里,就会被看到,可重复读就是始终使用事务开启时生成的数组。
快照读和当前读
Innodb给每一个事务生成一个唯一的事务ID的方法称为生成快照。因此这种场景下称为快照读 。但是对于更新数据不能使用快照读,因为更新数据时如果使用快照读会可能覆盖其它事务的更改,另外查询时如果加锁会采用当前读的方式。当前读就是读这个数据最新提交的数据。Innodb的多版本并发控制实现了在串行化的隔离级别下读不加锁,提高了并发性能。
下面举个例子解释快照读和当前读:
建一个表,插入一条数据

mysql-> create table t(k int) ENGINE = Innodb;
mysql-> insert into t(k) values (1);

让后将事务的隔离级别设置成可重复读(REPEATABLE-READ).接着开启三个事务,并按照下面的顺序执行

事务A				              事务B				              事务C
start  transaction with consistent 
snapshot  
							     start  transaction with consistent 
								 snapshot  

select k from t;

								select k from t;
															update t set k = k+1;
															
								update t set k = k+1;
								select k from t;
								commit;
select k from t;
commit;

结果是:A两次读取的结果是1,事务B第一次读取的结果是1,第三次读取的结果是3。事务A两次都是快照读,两次都是在可重复读的隔离级别下,因此两次读取的结果相同。事务B第一次是快照读,读取的是1,但是update进行了一次当前读,将k的值更新为事务c提交的数据k=2,并且在此基础上加1就是3.执行了一次update会创建一个新版本的数据,并将自己的事务ID作为新的数据版本号因此在自己的事务中可以读到自己更新的数据。将事务C提交的数据第二次读取的是3
注释:如果事务B在查的时候 加上select k from t for update; 那么事务C就会阻塞 原因是锁阻塞
而在题上由于select k from t 是并没有加锁的,因此是可以当前读的。而update在更新的时候先进行读,告诉innodb搜索引擎就行读,那么搜索引擎就会告诉返回服务端数据k和锁,因此update是当前读,并且涉及到并发控制协议,不加锁的不涉及并发控制协议。并发控制协议是当前读很底层的原理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL 事务是指一组数据库操作,这些操作要么全部执行,要么全部不执行,其目的是保证在并发环境下,数据的一致性和完整性。MySQL 事务具有 ACID 性质,即原子性、一致性、隔离性和持久性。 MySQL 中使用事务需要使用 BEGIN、COMMIT 和 ROLLBACK 语句,其中 BEGIN 表示开启一个事务,COMMIT 表示提交事务,ROLLBACK 表示回滚事务事务的基本语法如下: ``` BEGIN; -- 执行一组数据库操作 COMMIT; -- 提交事务 -- 或者 ROLLBACK; -- 回滚事务 ``` 在 MySQL 中,事务的隔离级别分为四个等级,分别是 Read Uncommitted、Read Committed、Repeatable Read 和 Serializable。隔离级别越高,数据的一致性和完整性越高,但同时也会影响数据库的性能。 MySQL 事务的 ACID 性质有以下含义: 1. 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部失败回滚,不会只执行其中的一部分操作。 2. 一致性(Consistency):事务执行前后,数据库中的数据必须保持一致性状态,即满足数据库的约束条件和完整性规则。 3. 隔离性(Isolation):事务之间应该是相互隔离的,一个事务的执行不应该被其他事务干扰,保证事务之间的数据相互独立。 4. 持久性(Durability):事务提交后,对数据库的修改应该是永久性的,即使出现系统故障或电源故障,也不应该对数据产生影响。 总之,MySQL 事务是一组数据库操作,具有 ACID 性质,可以通过 BEGIN、COMMIT 和 ROLLBACK 语句来实现,隔离级别越高,数据的一致性和完整性越高,但同时也会影响数据库的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值