MySQL的基本使用——事务

回顾

关于触发器的复习题

create table goods(   #商品表
gid char(10),  #商品编号
number int   #库存数目
);

create table order_detail(   #订单表
gid char(10),
number int,  #下单数目
ordertime timestamp
);

1.建立一个触发器,当订货时,会判断库存是否够,如果够,则订货信息会顺利的写入order_detail表,
库存信息更新,如果该商品库存不够,报错"商品库存不足",完成后测试该触发器

create trigger t_order before insert on order_detail for each row
begin
	declare num int;
	select number into num from goods where gid=new.gid;
	if num<new.number then signal SQLSTATE '45000' set MESSAGE_TEXT = "商品库存不足",MYSQL_ERRNO = 1333;
	else update goods set number = number - new.number where gid = new.gid;
	end if;
end;

drop trigger t_order;


insert into goods(gid,number) values("001",2);
select * from goods;
insert into order_detail(gid,number) values("001",10);
select * from order_detail;


一、事务概念

事务处理在数据库开发过程中有着非常重要的作用,它可以保证在同一个事务中的操作具有同步性。

1.1 事务的概念

现实生活中,人们经常会进行转账操作,转账可以分为转人和转出两部分,只有这两个部分都完成才认为转账成功。在数据库中,这个过程是使用两条SQL语句来实现的,如果执行其中任意一条语句出现异常没有执行,则会导致两个账户的金额不同步,造成错误。为了防止上述情况的发生,就需要使用MySQL中的事务(Transaction)。
在MySQL中,事务就是针对数据库的一组操作,它可以由一条或多 条SQL语句组成,且每个SQL语句是相互依赖的。只要在程序执行过程中有一-条SQL语句执行失败或发生错误,则其他语句都不会执行。也就是说,事务的执行要么成功,要么就返回到事务开始前的状态,这就保证了同一事务操作的同步性和数据的完整性。

MySQL中的事务必须满足A、C、I、D这4个基本特性,具体如下。
在这里插入图片描述

(1)原子性(Atomicity)。原子性是指一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。事务中如果有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。
(2)一致性(Consistency)。一致性是指在事务处理时,无论执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统不会返回到一个未处理的事务中。MySQL中的一致性主要由日志机制实现,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。
(3)隔离性(Isolation)。隔离性是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。隔离性相关的技术有并发控制、可串行化、锁等。当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
(4)持久性(Durability)。持久性是指事务一旦提交,其对数据库的修改就是永久性的。需要注意的是,事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性。而一些外部原因导致数据库发生故障,那么所有提交的数据可能都会丢失。

1.2 事务的基本操作

在默认情况下,用户执行的每一条SQL语句都会被当成单独的事务自动提交。如果要将一组SQL语句作为一个事务,则需要执行以下语句显示开启一个事务。

start transaction;

该语句的作用是让SQL语句不再自动提交,需要用户使用以下语句来进行手动提交,当事务提交后,操作的数据才会更改。

commit;

如果对提交的事务不满意,需要回滚,那么可以使用如下语句取消事务

rollback;

注意:rollback只能针对对未提交的事务进行回滚,已提交的事务无法回滚。当执行commit或者rollback后,当前事务就会自动结束!

使用步骤:

  1. 开始一个事务,标记事务的起始点
start transaction;
  1. 提交一个事务给数据库
commit;
  1. 将事务回滚,数据回到本次事务的初始状态
rollback;

1.3 注意事项

  • MySQL中的事务不允许嵌套,若在执行start transaction语句前上一个事务还未提交,会隐示地执行提交操作。
  • 事务处理主要是针对数据表中的数据的处理,不包括创建或删除数据库、数据表,修改表结构等操作,而且执行这类操作时会隐式地提交事务。
  • MySQL 5.7默认的存储引擎为InnoDB,该存储引擎支持事务,而另一个常见的数据存储引擎MyISAM不支持事务。对于MyISAM存储引擎的数据表,无论事务是否提交,对数据的操作都会立即生效,不能回滚。
  • 在MySQL中,还可以使用start transaction的别名begin或begin work来显示地开启一个事务。但由于begin与MySQL编程中的begin ...end冲突,因此不推荐使用。

1.4 拓展技能

MySQL默认是自动提交模式,如果没有显示开启事务(start transaction),每一条SQL语句都会自动提交(commit)。如果想要控制自动提交,可以通过更改autocommit变量来实现,将其值设为1表示开启自动提交,设为0表示关闭自动提交。

select @@autocommit;	# 查看当前autocommit的值
set autocommit=0;		# 关闭自动提交模式
set autocommit=1;		# 开启自动提交模式

二、事务处理

2.1 开始事务

可以使用一条START TRANSACTION或begin语句来显式地启动一个事务。
语法格式:

START TRANSACTION 

2.2 结束事务

COMMIT语句是提交语句,它使得自从事务开始以来所执行的所有数据修改成为数据库的永久部分,也标志一个事务的结束,其语法格式为:

COMMIT;

2.3 撤销事务

ROLLBACK语句是撤销语句,它撤销事务所做的修改,并结束当前这个事务。
语法格式:

ROLLBACK;

2.4 回滚事务

除了撤销整个事务,用户还可以使用ROLLBACK TO语句使事务回滚到某个点,在这之前需要使用SAVEPOINT语句来设置一个保存点。
SAVEPOINT语法格式为:

SAVEPOINT identifier;

其中,identifier为保存点的名称。
ROLLBACK TO SAVEPOINT语句会向已命名的保存点回滚一个事务。如果在保存点被设置后,当前事务对数据进行了更改,则这些更改会在回滚中被撤销,语法格式为:

ROLLBACK TO SAVEPOINT identifier;

当事务回滚到某个保存点后,在该保存点之后设置的保存点将被删除。

删除保存点:
RELEASE SAVEPOINT语句会从当前事务的一组保存点中删除已命名的保存点。不出现提交或回滚。如果保存点不存在,会出现错误。语法格式为:

RELEASE SAVEPOINT identifier;

2.5 控制事务结束后的行为

事务的提交(COMMIT)和回滚(ROLLBACK)还有一些可选的字句,如:

COMMIT ADN CHAIN NO RELEASE;
ROLLBACK ADN CHAIN NO RELEASE;

在上述的选择中,AND CHAIN用于在当前事务结束时,立即启动一个新事务,并且新事务与刚结束的事务有相同的隔离级别;RELEASE用于在终止当前事务后,让服务器断开与客户端的连接。若添加NO,则表示控制CHAINRELEASE完成。

三、事务隔离级别

由于数据库是一个多用户的共享资源,允许多线程并发访问,因此用户可以通过不同的线程执行不同的事务。为了保证这些事务直接不受影响,对事务设置隔离级是十分重要的。

3.1 查看隔离级别

对于隔离级别的查看,有几种不同的方式。如下:

select @@global.transaction_ioslation;		# 查看全局隔离级
select @@session.transaction_ioslation;		# 查看当前会话的隔离级
select @@transaction_ioslation;				# 查看下一个事务的隔离级

3.2 修改隔离级别

基于ANSI/ISO SQL规范,MySQL提供了下面4种隔离级:序列化SERIALIZABLE)、可重复读REPEATABLE READ)、提交读READ COMMITTED)、未提交读READ UNCOMMITTED)。
系统变量TX_ISOLATION中存储了事务的隔离级,可以使用SELECT获得当前会话隔离级的值

SELECT @@TX_ISOLATION;

只有支持事务的存储引擎才可以定义一个隔离级。定义隔离级可以使用SET TRANSACTION语句。
语法格式:

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL 隔离级别
										          | SERIALIZABLE 
										          | REPEATABLE READ
										          | READ COMMITTED
										          | READ UNCOMMITTED 

3.2.1 READ UNCOMMITED(读取未提交)

READ UNCOMMITED是事务中最低的级别,在该级别下的事务可以读取到其他事务中未提交的数据,这种读取方式也被称为脏读。简而言之,脏读是指一个事务读取了另外一个事务未提交的数据。

3.2.2 READ COMMITED(读取提交)

READ COMMITED是大多是数据库管理系统的默认隔离级,在该隔离级下只能读取其他事务已经提交的数据,避免了脏读数据的现象。但在该隔离级别下,会出现不可重复读(NON-REPEATABLE READ)的问题。

3.2.3 REPEATABLE READ(可重复读)

REPEATABLE READ是MySQL的默认事务隔离级,它解决了脏读和不可重复读的问题,确保了同一事务的多个实例在并发读取数据时,会看到同样的结果。
但在理论上,该隔离级会出现幻读(PHANTOM READ)的现象。幻读又被称为虚读,是指在一个事务内两次查询中数据条数不一致。

3.2.4 SERIALIZABLE(可串行化)

SERIALIZABLE是最高级别的隔离级,它在每个读的数据行上加锁,使之不会发生冲突,从而解决了脏读、不可重复读和幻读的问题。但是由于加锁可能导致超时和锁竞争现象,因此SERIALIZABLE也是性能最低的一种隔离级,除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。

四、实践

在这里插入图片描述
题目
模拟网上支付

顾客A在线购买一款商品,价格为500.00元,采用网上银行转账的方式支付
假如顾客A银行卡的余额为2000.00元,且向卖家B支付购买商品费用500.00元,起始卖家B的账号金额10000.00元
创建数据库shop和创建表account并插入2条数据,如下图所示:

在这里插入图片描述
(1)A账户给B账户转账,如果正常,A账户的钱会减少,B账户钱会增加,但A账户钱不够或者B账户不可用,这时则需返回A账户的500元,达到账户总额的平衡;
(2)A给B转账成功后,B给C转账2倍金额(比如A->B 500元,B->C 1000元),如果B账户钱不够或者C账户不可用,这时候回滚到A->B刚转完的阶段。
要求:使用事务结合存储过程模拟以上过程

create procedure p_money(in outid int,in inid int,in money int)
# outid 转出账户
# inid  转入账户
# money 转出金额
begin
	declare ye,res int;# ye 为余额
	start transaction;
	update account set cash = cash - money where id = outid;
	savepoint s1;
	select cash into ye from account where id = outid;
	if ye<0 then rollback;
	else update account set cash = cash + money where id = inid;
		select vaild into res from account where id = inid;
			if res=0 then rollback;
			end if;
	end if;
	commit;
end;

select * from account;
call p_money(2,1,1000);


猜你喜欢

转载自blog.csdn.net/qq_44723773/article/details/110387517