在学习事务之前先了解一下都有哪些存储引擎支持事务技术。
Mysql中默认的是MyISAM数据引擎(但也不是绝对的,有时我们可以修改默认存储引擎),可惜此引擎不支持事务处理,我们需要将默认的数据引擎改为InnoDB。其中InnoDB和BerkeleyDB支持事务处理,只是默认的情况下都是被disable的。所有的引擎里面,InnoDB性能最强大,算是商业级的。
所以我建议在使用事务时,一定要保证当前数据库的引擎支持事务技术。因此鉴于innodb功能强大且支持事务,我推荐在innodb存储引擎上建立事务。
事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
事务的开始与结束可以由用户显示控制。如果用户没用显示地定义事务,则由数据库管理系统按默认规定自动划分事务。(在mysql中,用来自动提交事务的变量是autocommit)。
事务的4大特性是指:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持续性(Durability)。
原子性:事务中的操作要么都做,要么都不做。
一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性可以理解即为正确性,不一致性可以理解为不正确性。也就是从正确状态变为正确状态。
隔离性:一个事务的执行不能被其他事务干扰,特别注意在并发执行的各个事务之间不能互相干扰,各个事务应保持隔离。
持续性:一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。之后的其他操作或故障不应该对事务的执行结果有影响。
事务是数据库恢复技术和并发控制的基本单元。因此就有必要保证事务的ACID特性,但是仍然会有一些因素导致ACID,如下:
1. 多个事务并发运行时,不同事务的操作交叉执行。
2. 事务在运行过程中被强行停止。
预防策略:
在第一种情况下,数据库管理系统必须保证多个事务的交叉运行不影响这些事务的原
子性:
在第二种情况下,数据库管理系统必须保证被强行终止的事务对数据库和其他事分
没有任何影响。
这些就是数据库管理系统中恢复机制和并发控制机制的责任。
以上知识现阶段只做了解即可,之后可以深入学习。
开启事务使用begin transaction或者start transaction,结束事务使用commit(表示提交)、rollback(回滚)。
set autocommit=0; #关闭事务的自动提交设置。
start transaction; #开启事务。start可以使用begin代替。
代码部分; #在代码部分可以结束事务
set autocommit=1; #还原事务的自动提交设置。
例如,向student表中插入一条数据:
SET autocommit=0;
START TRANSACTION;
INSERT INTO student VALUES('201215119','汪峰','男',50,'CS');
COMMIT;
SET autocommit=1;
-- 执行结果如下:注意一旦commit就表明已经写入数据库了,即使新建一个连接结果还是一样的
mysql> select * from student;
+-----------+--------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+-----------+--------+------+------+-------+
| 201215119 | 汪峰 | 男 | 50 | CS |
| 201215121 | 李勇 | 男 | 20 | CS |
| 201215122 | 刘晨 | 女 | 19 | CS |
| 201215123 | 王敏 | 女 | 18 | MA |
| 201215125 | 张立 | 男 | 19 | IS |
+-----------+--------+------+------+-------+
5 rows in set (0.00 sec)
如果把commit替换成rollback就不会插入了。
例如,再次向student表中插入一条数据:
SET autocommit=0;
START TRANSACTION;
INSERT INTO student VALUES('201215120','谢霆锋','男',40,'IS');
ROLLBACK; #这次使用的是rollback
SET autocommit=1;
-- 执行结果如下:
mysql> select * from student;
+-----------+--------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+-----------+--------+------+------+-------+
| 201215119 | 汪峰 | 男 | 50 | CS |
| 201215121 | 李勇 | 男 | 20 | CS |
| 201215122 | 刘晨 | 女 | 19 | CS |
| 201215123 | 王敏 | 女 | 18 | MA |
| 201215125 | 张立 | 男 | 19 | IS |
+-----------+--------+------+------+-------+
5 rows in set (0.00 sec)
可以看到使用了rollback后不会将事务的操作写入到数据库中。
使用rollback可以回滚到不同地方:
① 撤销全部事务处理。(rollback)
② 撤销事务到指定位置。(rollback to 还原点)
savepoint 还原点名称; -- 创建一个还原点,可以指定回滚到指定的还原点。
例如,向student表中插入数据:
START TRANSACTION;
INSERT INTO student VALUES('201215112','刘欢','男',60,'CS');
SAVEPOINT point1;
INSERT INTO student VALUES('201215112','吴奇隆','男',50,'MA');
ROLLBACK TO point1; #回滚到point1,也就是第二条数据没有被插入。
COMMIT;
-- 我们新建一个连接(主要是为了防止脏读)查看结果:
mysql> select * from student;
+-----------+--------+------+------+-------+
| Sno | Sname | Ssex | Sage | Sdept |
+-----------+--------+------+------+-------+
| 201215112 | 刘欢 | 男 | 60 | CS | #成功
| 201215119 | 汪峰 | 男 | 50 | CS |
| 201215121 | 李勇 | 男 | 20 | CS |
| 201215122 | 刘晨 | 女 | 19 | CS |
| 201215123 | 王敏 | 女 | 18 | MA |
| 201215125 | 张立 | 男 | 19 | IS |
+-----------+--------+------+------+-------+
6 rows in set (0.00 sec)
而第2条数据没有成功插入。
同时可以将事务创建在存储过程中,这样就会方便以后调用,并且提供了数据恢复的措施。例如在银行转账系统中我们可以创建一个事务,来保证转账的可靠性。这一点我在过程化sql学习之存储过程和函数(阶段2) 这篇文章提到过。但是那篇文章中的代码不是太完美(那篇文章中的代码无法在数据库中发生故障后,进行有效的数据库恢复)。在这里我补充一下:
DELIMITER $$
CREATE PROCEDURE banks.transfer(IN outCustomerId INT,IN inCustomerId INT,IN mount DECIMAL,OUT message VARCHAR(20))
BEGIN
DECLARE balance DECIMAL DEFAULT 0;
SET autocommit=0; #关闭事务自动提交设置
START TRANSACTION;
SELECT money INTO balance FROM account WHERE id=outCustomerId;
IF mount<0 THEN
SET message="转账钱数不能为负数";
ROLLBACK;
ELSEIF mount>balance THEN
SET message="账户余额不足";
ROLLBACK;
ELSE
UPDATE account SET money=money-mount WHERE id=outCustomerId;
UPDATE account SET money=money+mount WHERE id=inCustomerId;
COMMIT; #成功,可以提交事务
END IF;
SET autocommit=1; #不要忘记最后要修改为自动提交事务。
END$$
DELIMITER ;
补充后的代码,就可以避免在发生故障后转账人少了一笔钱,而收款人没有收到这笔钱的情况。我们可以利用数据库恢复技术,对该事务撤销、重做。