一、在循环中提交
- 开发人员喜欢在循环中进行事务提交,这种方法有两个缺点:出现错误难处理以及效率低
演示案例
- 创建一个表
create table t1( a int not null, b varchar(80) )engine=innodb;
- 下面一个存储过程
delimiter // 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; // delimiter ;
- 默认情况下,SQL语句都是自动提交的,也就是说在存储过程中,insert语句之后都会有一个隐式的commit操作,因此上面的存储过程也等价于:
delimiter // 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; // delimiter ;
- 这两个存储过程有两个问题:
- ①如果循环执行时发生错误,数据库会停留在一个未知的位置,因此很难进行处理
- ②性能问题:因为事务每提交一次就需要写一次重做日志,因此效率比较低
- 现在我们运行上面的两个存储过程,观察它们的运行的时间,可以看到差不多都为两秒多:
call load1(10000); truncate table t1; call load2(10000);
二、使用自动提交
- 自动提交不是一个好的习惯,因为这会是初级DBA容易犯错,另外还可能使一些开发人员产生错误的理解,如我们在上面介绍到的循环提交问题
- MySQL数据库默认设置使用自动提交(autocommit),可以使用下面的语句来关闭自动提交功能
set autocommit=0;
- 当然也可以使用start transaction或begin来显式开启一个事务。在显式开启事务之后,在默认设置下(即参数completion_type=0),MySQL会自动执行SET AUTOCOMMIT=0的命,在使用commit或rollback结束一个事务之后自动执行SET AUTOCOMMIT=1
不同编程语言API的自动提交
- 对于不同语言的API,自动提交也是不同的
- MySQL C API默认的提交方式是自动提交
- MySQL Python API则会自动执行SET AUTOCOMMIT=0,禁用自动提交
三、使用自动回滚
- InnoDB支持通过定义一个HANDLER来进行自动事务的回滚操作,如在一个存储过程中发生了错误会自动对其进行回滚操作
演示案例
- 因此我发现很多开发人员喜欢在应用程序中使用自动回滚,例如:
- 创建一个表
create table b( a int not null default 0, primary key(a) )engine=innodb default charset=latin1;
- 存储过程定义了一个exit类型的HANDLER,当捕获到错误时进行回滚:
delimiter // 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; // delimiter ;
- 因为表中a字段为主键,因此第三条insert语句会抛出错误,运行如下,表格没有插入任何数据: