视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
MySQLInnodb事务编程问题和处理
2020-11-09 09:12:24 责编:小采
文档


1.在循环中提交的问题

很多开发人员非常喜欢在循环中进行事务提交,下面演示一个他们经常写的一个存储过程示例,如下所示:

DROP PROCEDURE IF EXISTS load1;CREATE PROCEDURE load1(count INT UNSIGNED)BEGIN
 DECLARE s INT UNSIGNED DEFAULT 1;
 DECLARE c CHAR(80) DEFAULT REPEAT('a',80); WHILE s <= count DO
 INSERT INTO t1 select NULL,c;
 COMMIT; SET s=s+1; END WHILE;END;

在上面的例子中,是否加上commit命令并不是关键。由于MySQL innodb的存储引擎默认为自动提交,因此去掉存储过程中的commit结果是一样的。如下所示,下面也是另一个容易被开发人员忽视的问题:

DROP PROCEDURE IF EXISTS load2; CREATE PROCEDURE load2(count INT UNSIGNED)BEGIN
 DECLARE s INT UNSIGNED DEFAULT 1;
 DECLARE c CHAR(80) DEFAULT REPEAT('a',80); WHILE s <= count DO
 INSERT INTO t1 select NULL,c; SET s=s+1; END WHILE;END;

不论上面哪个存储过程,当发生错误时,数据库会停留在一个未知的位置。例如我们要插入10000条数据,但是在插入5000条时发生了错误,然而这5000条已经存放在了数据库中,我们如何处理?另外一个是性能问题,上面的两个存储过程都不会比下面的这个存储过程快,因为下面这个是将insert放在了一个事务中:

DROP PROCEDURE IF EXISTS load3; CREATE PROCEDURE load3(count INT UNSIGNED)BEGIN
 DECLARE s INT UNSIGNED DEFAULT 1;
 DECLARE c CHAR(80) DEFAULT REPEAT('a',80);
 START TRANSACTION; WHILE s <= count DO
 INSERT INTO t1 select NULL,c; SET s=s+1; END WHILE;
 COMMIT;END;

对于上面三个存储过程,我们分别插入100万数据来比较执行时间,如下所示,显然可以看到第三种方法要快很多,这是因为每次提及都要写一次重做日志,所以load1和load2实际写了100万次重做日志。对于存储过程load3,我们只写了1次重做日志。

先准备一个测试表

CREATE TABLE `t1` (`id` int NOT NULL AUTO_INCREMENT ,`name` varchar(500) NULL ,
PRIMARY KEY (`id`)
) ;

执行测试

09:50:44 test> call load1(1000000);
Query OK, 0 rows affected (1 min 4.90 sec)09:54:23 test> truncate table t1;
Query OK, 0 rows affected (0.05 sec)09:54:25 test> call load2(1000000);
Query OK, 1 row affected (1 min 3.38 sec)09:55:32 test> truncate table t1;
Query OK, 0 rows affected (0.20 sec)09:55:58 test> call load3(1000000);
Query OK, 0 rows affected (33.90 sec)

对于第二个存储过程load2,我们也可以人为的开启下事务,同样可以达到存储过程load3的效果,执行时间如下所示:

09:57:42 test> begin;
Query OK, 0 rows affected (0.00 sec)09:57:46 test> call load2(1000000);
Query OK, 1 row affected (34.08 sec)09:58:26 test> commit;
Query OK, 0 rows affected (0.76 sec)

2.关于使用自动提交

在一些特殊场景下,有时候自动提交不一定是个好的事情, 如我们上面讲到的循环提交的问题,MySQL数据库默认是自动提交(autocommit)。可以通过如下方式来改变MySQL的提交方式:

10:35:34 test> SET AUTOCOMMIT=0;
Query OK, 0 rows affected (0.00 sec)

也可以使用START TRANSATION或者BEGIN显示的开启一个事务。MySQL会自动执行

SET AUTOCOMMIT=0,并在COMMIT或ROLLBACK结束一个事务后执行SET AUTOCOMMIT=1 。

3.使用自动回滚处理异常 当存储过程发生异常的时候怎么办,Innodb存储引擎支持通过一个HANDLER来进行事务的自动回滚操作。如在存储过程中发生错误会自动进行回滚操作。如下面一个示例:

CREATE PROCEDURE sp_auto_rollback_demo()BEGIN
 DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
 START TRANSACTION;
 INSERT INTO b select 1;
 INSERT INTO b select 2;
 INSERT INTO b select 1;
 INSERT INTO b select 3;
 COMMIT;END;

测试表如下

CREATE TABLE `b` ( `a` int(11) NOT NULL DEFAULT '0',
 PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

执行上面的存储过程,因此会在插入第二个记录1时发生错误,但是因为启用了自动回滚操作,这个存储过程执行结果如下:

10:09:46 test> call sp_auto_rollback_demo;
Query OK, 0 rows affected (0.01 sec)10:10:04 test> select * from b;Empty set (0.00 sec)

看起来没有问题,运行比较正常,但是在执行sp_auto_rollback_demo的时候是执行成功了还是失败了?对此,我们可以进行如下处理,示例如下:

DROP PROCEDURE IF EXISTS sp_auto_rollback_demo;CREATE PROCEDURE sp_auto_rollback_demo()BEGIN
 DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SELECT -1; END;
 START TRANSACTION;
 INSERT INTO b select 1;
 INSERT INTO b select 2;
 INSERT INTO b select 1;
 INSERT INTO b select 3;
 COMMIT;
 SELECT 1;END;

当发生错误时,先回滚然后返回-1,表示运行发生了错误。返回1表示运行正常。运行结果如下:

10:16:19 test> call sp_auto_rollback_demo\G*************************** 1. row ***************************-1: -1
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
10:16:35 test> select * from b;
Empty set (0.00 sec)

下载本文
显示全文
专题