事务的起源

原子性(Atomicity)


要么全做,要么全不做的规则称之为原子性

隔离性(Isolation)

其它的状态转换不会影响到本次状态转换,这个规则被称之为隔离性

一致性(Consistency)

如果数据库中的数据全部符合现实世界中的约束(all defined rules),我们说这些数据就是一致的,或者说符合一致性的。

如何保证数据库中数据的一致性(就是符合所有现实世界的约束)呢?这其实靠两方面的努力:

  • 数据库本身能为我们保证一部分一致性需求(就是数据库自身可以保证一部分现实世界的约束永远有效)。
  • 更多的一致性需求需要靠写业务代码的程序员自己保证。

原子性和隔离性都会对一致性产生影响

持久性(Durability)

当现实世界的一个状态转换完成后,这个转换的结果将永久的保留,这个规则被设计数据库的大叔们称为持久性。

事务的概念

把需要保证原子性、隔离性、一致性和持久性的一个或多个数据库操作称之为一个事务(英文名是:transaction)。
根据这些操作所执行的不同阶段把事务大致上划分成了这么几个状态:
image.png

MySQL中事务的语法

开启事务

  • BEGIN [WORK];
  • START TRANSACTION;

不过比BEGIN语句牛逼一点儿的是,可以在START TRANSACTION语句后边跟随几个修饰符,就是它们几个:

  • READ ONLY:标识当前事务是一个只读事务,也就是属于该事务的数据库操作只能读取数据,而不能修改数据。
  • READ WRITE:标识当前事务是一个读写事务,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据。
  • WITH CONSISTENT SNAPSHOT:启动一致性读(先不用关心啥是个一致性读,后边的章节才会唠叨)。

    START TRANSACTION READ WRITE, WITH CONSISTENT SNAPSHOT 开启一个读写事务和一致性读

提交事务

COMMIT [WORK]

手动中止事务

ROLLBACK [WORK]

支持事务的存储引擎

MySQL中并不是所有存储引擎都支持事务的功能,目前只有InnoDB和NDB存储引擎支持(NDB存储引擎不是我们的重点)

自动提交

  1. mysql> SHOW VARIABLES LIKE 'autocommit';
  2. +---------------+-------+
  3. | Variable_name | Value |
  4. +---------------+-------+
  5. | autocommit | ON |
  6. +---------------+-------+
  7. 1 row in set (0.01 sec)

可以看到它的默认值为ON,也就是说默认情况下,如果我们不显式的使用START TRANSACTION或者BEGIN语句开启一个事务,那么每一条语句都算是一个独立的事务,这种特性称之为事务的自动提交

隐式提交

如果我们输入了某些语句之后就会悄悄的提交掉,就像我们输入了COMMIT语句了一样,这种因为某些特殊的语句而导致事务提交的情况称为隐式提交,这些会导致事务隐式提交的语句包括:

  • 定义或修改数据库对象的数据定义语言(Data definition language,缩写为:DDL)。

所谓的数据库对象,指的就是数据库、表、视图、存储过程等等这些东西。当我们使用CREATE、ALTER、DROP等语句去修改这些所谓的数据库对象时,就会隐式的提交前边语句所属于的事务,就像这样:

  1. BEGIN;
  2. SELECT ... # 事务中的一条语句
  3. UPDATE ... # 事务中的一条语句
  4. ... # 事务中的其它语句
  5. CREATE TABLE ... # 此语句会隐式的提交前边语句所属于的事务
  • 隐式使用或修改mysql数据库中的表

当我们使用ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD等语句时也会隐式的提交前边语句所属于的事务。

  • 事务控制或关于锁定的语句

当我们在一个事务还没提交或者回滚时就又使用START TRANSACTION或者BEGIN语句开启了另一个事务时,会隐式的提交上一个事务,比如这样:

  1. BEGIN;
  2. SELECT ... # 事务中的一条语句
  3. UPDATE ... # 事务中的一条语句
  4. ... # 事务中的其它语句
  5. BEGIN; # 此语句会隐式的提交前边语句所属于的事务
  • 加载数据的语句

比如我们使用LOAD DATA语句来批量往数据库中导入数据时,也会隐式的提交前边语句所属的事务。

  • 关于MySQL复制的一些语句使用START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO等语句时也会隐式的提交前边语句所属的事务。
  • 其它的一些语句使用ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH、 LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET等语句也会隐式的提交前边语句所属的事务。

    保存点

    ```sql SAVEPOINT 保存点名称;

回滚到某个保存点 ROLLBACK [WORK] TO [SAVEPOINT] 保存点名称;

想删除某个保存点 RELEASE SAVEPOINT 保存点名称;

mysql> SELECT * FROM account; +——+————+————-+ | id | name | balance | +——+————+————-+ | 1 | 狗哥 | 11 | | 2 | 猫爷 | 2 | +——+————+————-+ 2 rows in set (0.00 sec)

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

mysql> UPDATE account SET balance = balance - 10 WHERE id = 1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0

mysql> SAVEPOINT s1; # 一个保存点 Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM account; +——+————+————-+ | id | name | balance | +——+————+————-+ | 1 | 狗哥 | 1 | | 2 | 猫爷 | 2 | +——+————+————-+ 2 rows in set (0.00 sec)

mysql> UPDATE account SET balance = balance + 1 WHERE id = 2; # 更新错了 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0

mysql> ROLLBACK TO s1; # 回滚到保存点s1处 Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM account; +——+————+————-+ | id | name | balance | +——+————+————-+ | 1 | 狗哥 | 1 | | 2 | 猫爷 | 2 | +——+————+————-+ 2 rows in set (0.00 sec) ```