MySQL数据库,详解异常捕获及处理(二)

2021-12-02 09:00:52 浏览数 (1)

外部异常

外部异常不是由mysql内部抛出的错误,⽽是由于sql的执⾏结果和我们期望的结果不⼀致

的时候,我们需要对这种情况做⼀些处理,如回滚操作。

示例1

我们来模拟电商中下单操作,按照上⾯的步骤来更新账户余额。

电商中有个账户表和订单表,如下:

DROP TABLE IF EXISTS t_funds;

CREATE TABLE t_funds(

user_id INT PRIMARY KEY COMMENT '⽤户id',

available DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '账户余额'

) COMMENT '⽤户账户表';

DROP TABLE IF EXISTS t_order;

CREATE TABLE t_order(

id int PRIMARY KEY AUTO_INCREMENT COMMENT '订单id',

price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '订单⾦额') COMMENT '订单表';

delete from t_funds;

/*插⼊⼀条数据,⽤户id为1001,余额为1000*/

INSERT INTO t_funds (user_id,available) VALUES (1001,1000);

下单操作涉及到操作上⾯的账户表,我们⽤存储过程来模拟实现:

/*删除存储过程*/

DROP PROCEDURE IF EXISTS proc3;

/*声明结束符为$*/

DELIMITER $

/*创建存储过程*/

CREATE PROCEDURE proc3(v_user_id int,v_price decimal(10,2),OUT v_msg

varchar(64))

a:BEGIN

DECLARE v_available DECIMAL(10,2);

/*1.查询余额,判断余额是否够*/

select a.available into v_available from t_funds a where a.user_id

= v_user_id;

if v_available<=v_price THEN

SET v_msg='账户余额不⾜!';

/*退出*/

LEAVE a;

END IF;

/*模拟耗时5秒*/

SELECT sleep(5);

/*2.余额减去price*/

SET v_available = v_available - v_price;

/*3.更新余额*/

START TRANSACTION;

UPDATE t_funds SET available = v_available WHERE user_id =

v_user_id;

/*插⼊订单明细*/

INSERT INTO t_order (price) VALUES (v_price); /*提交事务*/

COMMIT;

SET v_msg='下单成功!';

END $

/*结束符置为;*/

DELIMITER ;

上⾯过程主要分为3步骤:验证余额、修改余额变量、更新余额。

开启2个cmd窗⼝,连接mysql,同时执⾏下⾯操作:

USE javacode2018;

CALL proc3(1001,100,@v_msg);

select @v_msg;

然后执⾏:

mysql> SELECT * FROM t_funds;

--------- -----------

| user_id | available |

--------- -----------

| 1001 | 900.00 |

--------- -----------

1 row in set (0.00 sec)

mysql> SELECT * FROM t_order;

---- --------

| id | price |

---- --------

| 1 | 100.00 |

| 2 | 100.00 |

---- --------

2 rows in set (0.00 sec)

上⾯出现了⾮常严重的错误:下单成功了2次,但是账户只扣了100。

上⾯过程是由于2个操作并发导致的,2个窗⼜同时执⾏第⼀步的时候看到了⼀样的数据

(看到的余额都是1000),然后继续向下执⾏,最终导致结果出问题了。

上⾯操作我们可以使⽤乐观锁来优化。乐观锁的过程:⽤期望的值和⽬标值进⾏⽐较,如果相同,则更新⽬标值,否则

什么也不做。

乐观锁类似于java中的cas操作,这块需要了解的可以点击:详解CAS

我们可以在资⾦表t_funds添加⼀个version字段,表⽰版本号,每次更新数据的时候

1,更新数据的时候将version作为条件去执⾏update,根据update影响⾏数来判断执⾏

是否成功,优化上⾯的代码,见⽰例2。

0 人点赞