mysql的事务简介、事务特性及隔离级别详解代码示例

1.什么是事务

  • 所谓的事务就是针对数据库的一组操作。他可以由一条或者多条sql语句组成,同一个事务的操作具备同步的特点,即事务中的语句要么都执行,要么 都不执行。

2.事务的使用

  • 开启事务-> start transaction
  • 执行sql
  • 提交事务 commit
  • 取消事务(回滚) rollback
  • 示例
--创建一个employee表
drop table employee;
create table employee(
	eid int primary key auto_increment,
	ename varchar(20) not null,
	balance double(9,2) check(balance>0)
);
--插入几条数据
insert into employee(ename,balance) values('小明',8000);
insert into employee(ename,balance) values('小红',9000);
insert into employee(ename,balance) values('李华',10000);

select * from employee;

--创建事务
start transaction;
insert into employee(ename,balance) values('a',6500);
insert into employee(ename,balance) values('b',7800);

##此时事务尚未被提交,但是employee表已经插入上去数据了,数据可以被回退或者提交
##提交
commit;

3.事务的特性

四个特性

  • 原子性

    • 原子性是指一个事务必须被视为一个不可分割的最小的工作单元,只有事务的所有的数据库操作都执行成功才算整个事务执行成功
  • 一致性

    • 一致性是指事务将数据库从一种状态转变为下一种一致的状态(例如转账前转账后总金额不发生改变)
  • 隔离性

    • 隔离性还可以被称为并发控制、可串行化、锁等,当多个数据库为每一个用户开启事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
  • 持久性

    • 持久性是指事务一旦被提交,其所作的修改就会永久的保存在数据库中,即使数据库发生故障也不应该对其有任何影响。
    • 事务的持久性不能做到100%的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所有提交的数据可能都会丢失。

4.事务的隔离级别

  • 读未提交(read-uncommitted)
  • 不可重复读(read-committed)
  • 可重复读(repeatable-read)
  • 串行化(serializable)

由于不同的隔离级别会引发不同的并发问题

  1. 脏读

    • 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取的数据 是脏数据。

    • 代码示例

##事务B
		start transaction;
		update employee set balance=10000 where eid=1;
		##事务A
		select * from employee where eid=1; ##脏数据
		##事务B
		rollback;
  1. 不可重复读

    • 事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一样

    • 代码示例

##事务A
		select * from employee where eid=1; ##第一次读取数据
		##事务B
		start transaction;
		update employee set balance=10000 where eid=1;
		commit;
		##事务A
		select * from employee where eid=1; ##第二次读取数据,发现与第一次读取的数据不同
  1. 幻读

    • 系统管理员将数据库中所有的学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体的分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

    • 代码示例

#准备工作
		create table score1(
			sid int primary key auto_increment,
			sname varchar(20) not null,
			point varchar(20) not null
		);
		insert into score1(sname,point) values('小明','80');
		insert into score1(sname,point) values('小红','90');
		insert into score1(sname,point) values('张三','100');
		insert into score1(sname,point) values('张三','70');
		insert into score1(sname,point) values('李四','60');
		#开始
		select * from score1;
		系统管理员A
		update score1 set point='A' where point='100';
		update score1 set point='B' where point='90';
		update score1 set point='C' where point='80';
		update score1 set point='D' where point='70';
		update score1 set point='E' where point='60';
		系统管理员B
		insert into score1(sname,point) values('小红','90');
		
		系统管理员A
		select * from score1;
  • 注意区分 不可重复读和幻读 不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需要锁住满足条件的行,解决幻读需要锁表。

  • mysql事务隔离级别的作用

事务隔离级别脏读不可重复读幻读
读未提交
不可重复读
可重复读
串行化
  • InnoDB默认级别 可重复读

读未提交 代码示例(根据序号执行)

  • 打开客户端A,并设置当前的事务隔离级别为读未提交,查询employee表的数据
##客户端A
	##设置事务级别为读未提交
	#①
	set session transaction isolation level read uncommitted;
	##开启事务
	#②
	start transaction;
	#③
	select * from employee;
	#数据如下
	1	小明	10000.00
	2	小红	9000.00
	3	李华	10000.00
	4		a		6500.00
	5		b		7800.00
	#⑥
	select * from employee;
	#数据如下
	1	小明	5000.00
	2	小红	9000.00
	3	李华	10000.00
	4		a		6500.00
	5		b		7800.00
	#⑧
	select * from employee;
	#数据如下
	1	小明	10000.00
	2	小红	9000.00
	3	李华	10000.00
	4		a		6500.00
	5		b		7800.00
	
	
##客户端B
	##设置事务的级别
	#④
		set session transaction isolation level read uncommitted;
		start TRANSACTION;
	##更新数据
	#⑤
	update employee set balance=5000 where eid=1;
	#⑦
	rollback;

读已提交 代码示例(根据序号执行)

##客户端A
		#①设置事务级别为读已提交
		set session transaction isolation level read committed;
		##开启事务
		start transaction;
		##③查询数据
		select * from employee;
		1		小明	10000.00
		2		小红	9000.00
		3		李华	10000.00
		4		a			6500.00
		5		b			7800.00
		##⑦查询数据(查询不到事务B更新的数据,解决了脏读的问题)
		select * from employee;
		1		小明	10000.00
		2		小红	9000.00
		3		李华	10000.00
		4		a			6500.00
		5		b			7800.00
		##⑨查询数据(上下结果不一致,产生了不可重复读的问题)
		select * from employee;
		1		小明	5000.00
		2		小红	9000.00
		3		李华	10000.00
		4		a			6500.00
		5		b			7800.00
		
		
##客户端B
	#④设置事务的级别
	set session transaction isolation level read committed;
	##⑤开启事务
	start transaction;
	#⑥更新employee表
		update employee set balance=5000 where eid=1;
		
	#⑧事务提交
		commit;

可重复读 代码示例(根据序号执行)

##客户端A
		#①设置事务级别为可重复读
		set session transaction isolation level repeatable read;
		##②开启事务
		start transaction;
		##③查询数据
		select * from employee;
		1		小明	10000.00
		2		小红	9000.00
		3		李华	10000.00
		4		a			6500.00
		5		b			7800.00
		##⑦查询数据(查询不到事务B更新的数据,解决了脏读的问题)
		select * from employee;
		1		小明	10000.00
		2		小红	9000.00
		3		李华	10000.00
		4		a			6500.00
		5		b			7800.00
		##⑨查询数据(上下结果一致,解决了不可重复读的问题)
		select * from employee;
		1		小明	10000.00
		2		小红	9000.00
		3		李华	10000.00
		4		a			6500.00
		5		b			7800.00
		##⑩更新数据(更新数据却是用的客户端B的 (9950-50) 	)
			update employee set balance=balance-50 where eid=1;
			select * from employee;
			1		小明		9900.00
			2		小红		9000.00
			3		李华		10000.00
			4		a			6500.00
			5		b			7800.00

数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,
select操作不会更新版本号,是快照读(历史版本);
insert、update和delete会更新版本号,是当前读(当前版本)。

##12查询(没有查到新插入的数据)
		
			select * from employee;
			1		小明		9900.00
			2		小红		9000.00
			3		李华		10000.00
			4		a			6500.00
			5		b			7800.00
##客户端B
	#④设置事务的级别
	set session transaction isolation level repeatable read;
	##⑤开启事务
	start transaction;
	#⑥更新employee表
		update employee set balance=balance-50 where eid=1;
		select * from employee;

	#⑧事务提交
		commit;
		
##客户端B
	# 11 设置事务的级别
	set session transaction isolation level repeatable read;
	##开启事务,插入数据
	start transaction;
	insert into employee(ename,balance) values('hhh',5000);

串行化 (代码示例)

##客户端A
		#①设置事务级别为串行化
		set session transaction isolation level SERIALIZABLE;
		##②开启事务
		start transaction;
		##③查询数据
		select * from employee;
##客户端B
		#①设置事务级别为串行化
		set session transaction isolation level SERIALIZABLE;
		##②开启事务
		start transaction;
		##③插入查询数据
		insert into employee(ename,balance) values('111',5000);
		select * from employee;

串行化由于并发性极低,开发中很少会用到。

posted @ 2021-05-01 17:08  小吕不秃顶也能变强  阅读(62)  评论(0)    收藏  举报