MySQL Innodb事务编程问题和处理
2014-12-24 16:42
260 查看
http://www.ruzuojun.com/topic/857.html
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)
以上整理和摘录于 《MySQL技术内幕 SQL编程》 ,示例部分经过本人测试验证。
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)
以上整理和摘录于 《MySQL技术内幕 SQL编程》 ,示例部分经过本人测试验证。
相关文章推荐
- [MySQL生产环境] Innodb存储引擎内存报警问题处理过程
- PHP PDO事务处理及MYSQLengine=InnoDB
- EF 事务处理 (InnoDB Engine的MySQL表也可以)
- Mysql事务以及四中隔离级别实例2以及InnoDB如何解决当时读的幻读问题
- 资深架构师Sum的故事:(Mysql)InnoDB下,存储过程中事务的处理
- php+Mysqli利用事务处理转账问题实例
- 面试常考 MySQL优化 事务处理 MyISAM和InnoDB索引实现
- PHP 处理 MySQL INNODB 事务回滚(ThinkPHP、MySQL、PDO)
- MySQL事务处理失效问题
- 并发控制 mysql InnoDB锁表,事务及处理方法
- Innodb的事务处理autocommit参数问题
- MySQL表类型MyISAM/InnoDB的区别(解决事务不回滚的问题)(转)
- 分析Mysql事务和数据的一致性处理问题
- MySQL InnoDB :事务隔离隔离级别以及对应的问题
- [MySQL生产环境] Innodb存储引擎内存报警问题处理过程
- php+Mysqli利用事务处理转账问题实例
- mysql java 处理事务问题
- [MySQL]对于事务并发处理带来的问题,脏读、不可重复读、幻读的理解
- MYSQL集群与INNODB事务处理的对比总结
- 分布式事务处理的问题