一. 事务
数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
一般来说数据库事务由以下四个特点:
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
二. 事务控制语句
- BEGIN或START TRANSACTION,显式地开启一个事务。
- COMMIT;也可以使用COMMIT WORK,不过二者是等价的。COMMIT会提交事务,并使已对数据库进行的所有修改称为永久性的。
- ROLLBACK;有可以使用ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
- SAVEPOINT identifier;SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT。
- RELEASE SAVEPOINT identifier;删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常。
- ROLLBACK TO identifier;把事务回滚到标记点。
- SET TRANSACTION;用来设置事务的隔离级别。InnoDB存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。
三. 显式事务
用 BEGIN, ROLLBACK, COMMIT来实现:
- BEGIN 开始一个事务
- 执行对应的SQL语句
- COMMIT 事务确认
下面以银行转账为例来演示显示事务的使用
# 新建account表,并添加数据
mysql> CREATE TABLE `account` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`bankNo` varchar(20),
`username` varchar(40),
`money` float,
PRIMARY KEY (`id`));
mysql> INSERT INTO `account` VALUES(1, '110', '张三', 10000);
mysql> INSERT INTO `account` VALUES(2, '120', '李四', 1000);
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
# 张三向李四转账200
mysql> UPDATE account SET money = money - 200 WHERE bankNo = 110;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> UPDATE account SET money = money + 200 WHERE bankNo = 120;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#提交事务
mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)
#查看转账结果,转账成功。
mysql> SELECT * FROM account;
+----+--------+----------+-------+
| id | bankNo | username | money |
+----+--------+----------+-------+
| 1 | 110 | 张三 | 9800 |
| 2 | 120 | 李四 | 1200 |
+----+--------+----------+-------+
2 rows in set (0.00 sec)
# 张三再向李四转账200
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE account SET money = money - 200 WHERE bankNo = 110;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#假定更新李四余额的时候出错。该命令执行失败
mysql> UPDATE account SET money = money + 200 WHERE bankNo = 120;
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
# 查看张三和李四的账户余额,转账不成功。
mysql> SELECT * FROM account;
+----+--------+----------+-------+
| id | bankNo | username | money |
+----+--------+----------+-------+
| 1 | 110 | 张三 | 9800 |
| 2 | 120 | 李四 | 1200 |
+----+--------+----------+-------+
2 rows in set (0.00 sec)
四. 隐式事务
开启MySQL 的手动提交模式:
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。
下面同样以银行转账为例演示隐式事务的使用
# 新建account表,并添加数据
mysql> CREATE TABLE `account` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`bankNo` varchar(20),
`username` varchar(40) ,
`money` float,
PRIMARY KEY (`id`));
mysql> INSERT INTO `account` VALUES(1, '110', '张三', 10000);
mysql> INSERT INTO `account` VALUES(2, '120', '李四', 1000);
#禁止自动提交
mysql> SET AUTOCOMMIT= 0;
Query OK, 0 rows affected (0.00 sec)
# 张三向李四转账200
mysql> UPDATE account SET money = money - 200 WHERE bankNo = 110;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> UPDATE account SET money = money + 200 WHERE bankNo = 120;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#提交事务
mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)
#查看转账结果,转账成功。
mysql> SELECT * FROM account;
+----+--------+----------+-------+
| id | bankNo | username | money |
+----+--------+----------+-------+
| 1 | 110 | 张三 | 9800 |
| 2 | 120 | 李四 | 1200 |
+----+--------+----------+-------+
2 rows in set (0.00 sec)
# 当转账过程出现错误时,仍然使用ROLLBACK回滚。
五. 注意事项
- 事务提交之后,进行回滚事务是不起作用的,因为事务日志在提交事务的同时已经被清除。
- mysql支持的主流存储引擎中Innodb是支持事务的,Myisam不支持事务。
- 事务只支持DML语句,也即insert,delete,update三种操作。
# 创建表t1
mysql> create table t1 (id bigint(20), name varchar(20));
Query OK, 0 rows affected (0.06 sec)
mysql> show tables;
+---------------+
| Tables_in_tmp |
+---------------+
| t1 |
+---------------+
1 row in set (0.00 sec)
# 开始事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
# 删除表t1,drop操作属于DDL语句
mysql> drop table t1;
Query OK, 0 rows affected (0.04 sec)
#回滚
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> show tables;
Empty set (0.00 sec)
# 尽管rollback,但是表t1还是删除掉了。