1、事务

1、什么是事务

事务(Transaction),顾名思义就是要做的或所做的事情,数据库事务指的则是作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。

 

2、为什么需要事务

 

一个经典的例子:A账户转给B账户10元,数据库操作需要两步,第一步A账户减10元,第二步B账户加10元,如果没有事务并且在两步中间发生异常,就会导致A的账户少了10元,但B的账户没有变化,如果不能保证这两步操作统一,银行的转账业务也没法进行展开了。

 

当一个事务被提交给了DBMS(数据库管理系统),则DBMS需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态(要么全执行,要么全都不执行);同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。

3、事务的4个特性


# 1、原子性(Atomicity)
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。

# 2、一致性(Consistency)
事务应确保数据库的状态从一个一致状态转变为另一个一致状态,例如转账行为中,一个人减了50元,另外一个人就应该加上这50元,而不能是40元。
其他一致状态的含义是数据库中的数据应满足完整性约束,例如字段约束不能为负数,事务执行完毕后的该字段也同样不是负数

# 3、隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。

# 4、持久性(Durability)
一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。

举例说明ACID

用一个常用的“A账户向B账号汇钱”的例子来说明如何通过数据库事务保证数据的准确性和完整性。熟悉关系型数据库事务的都知道从帐号A到帐号B需要6个操作:

 

1、从A账号中把余额读出来(500)。
2、对A账号做减法操作(500-100)。
3、把结果写回A账号中(400)。
4、从B账号中把余额读出来(500)。
5、对B账号做加法操作(500+100)。
6、把结果写回B账号中(600)。

 


# 1、原子性:
保证1-6所有过程要么都执行,要么都不执行。一旦在执行某一步骤的过程中发生问题,就需要执行回滚操作。 假如执行到第五步的时候,B账户突然不可用(比如被注销),那么之前的所有操作都应该回滚到执行事务之前的状态。

# 2、一致性
在转账之前,A和B的账户中共有500+500=1000元钱。在转账之后,A和B的账户中共有400+600=1000元。也就是说,数据的状态在执行该事务操作之后从一个状态改变到了另外一个状态。同时一致性还能保证账户余额不会变成负数等。

# 3、隔离性
在A向B转账的整个过程中,只要事务还没有提交(commit),查询A账户和B账户的时候,两个账户里面的钱的数量都不会有变化。
如果在A给B转账的同时,有另外一个事务执行了C给B转账的操作并提交了,虽然A给B转账的事务里看不到最新修改的数据,但是当两个事务都提交完的时候,B账户里面的钱应该是A转给B的钱加上C转给B的钱再加上自己原有的钱。

# 4、持久性
一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化(会把数据写入数据库做持久化保存)!

# 强调:原子性与隔离性
一致性与原子性是密切相关的,原子性的破坏可能导致数据库的不一致,数据的一致性问题并不都和原子性有关。
比如刚刚的例子,在第五步的时候,对B账户做加法时只加了50元。那么该过程可以符合原子性,但是数据的一致性就出现了问题。

因此,事务的原子性与一致性缺一不可。

4、MySQL事务的运行模式


手动开启的事务里默认不会自动提交
所以我们可以将要执行的sql语句放在我们自己手动开启的事务里,如此便是显式开启、显式提交
start transaction;

update test.t1 set id=33 where name = "jack";

commit;


这种方式在当你使用commit或者rollback后,事务就结束了
再次进入事务状态需要再次start transaction

5、事务保存点

savepoint和虚拟机中的快照类似,用于事务中,没设置一个savepoint就是一个保存点,当事务结束时会自动删除定义的所有保存点,在事务没有结束前可以回退到任意保存点。


1、设置保存点savepoint 保存点名字
2、回滚到某个保存点,该保存点之后的操作无效,rollback 某个保存点名
3、取消全部事务,删除所有保存点rollback

# 注意:rollback和commit都会结束掉事务,这之后无法再回退到某个保存点

案例


mysql> CREATE TABLE employee (
    -> id INT PRIMARY KEY AUTO_INCREMENT,
    -> name VARCHAR(20),
    -> age TINYINT(2)
    -> );
Query OK, 0 rows affected (0.05 sec)

mysql> INSERT INTO employee (name, age) VALUES ("ShanHe", 18),("ChenYang", 16);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM employee;
+----+----------+------+
| id | name     | age  |
+----+----------+------+
|  1 | ShanHe   |   18 |
|  2 | ChenYang |   16 |
+----+----------+------+
2 rows in set (0.00 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.01 sec)

mysql> UPDATE employee SET name="ShanHeChen" WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SAVEPOINT one;   -- 保存点one
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM employee;
+----+------------+------+
| id | name       | age  |
+----+------------+------+
|  1 | ShanHeChen |   18 |
|  2 | ChenYang   |   16 |
+----+------------+------+
2 rows in set (0.00 sec)