事务简介
事务的起源
狗哥和猫爷是⼀对好基友,他们都到银⾏开⼀个账户,他们在现实世界中拥有的资产就会体现在数据库世界的account表中。⽐如现在狗哥有11元,猫爷只有2元,那么现实中的这个情况映射到数据库的account表就是这样:
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 狗哥 | 11 |
| 2 | 猫爷 | 2 |
+----+--------+---------+
⽐⽅说有⼀次猫爷在赌场赌博输了钱,急忙打电话给狗哥要借10块钱,不然那些看场⼦的就会把⾃⼰剁了。现实世界中的狗哥⾛向了ATM机,输⼊了猫爷的账号以及10元的转账⾦额,然后按下确认,狗哥就拔卡⾛⼈了。
但是这⾥头有个问题,银行把狗哥的钱扣了,忽然服务器断电了,并没有把扣得前给猫爷转过去,那猫爷还是逃脱不了被砍死的噩运~
原⼦性(Atomicity)
要么全做,要么全不做的规则称之为原⼦性。
隔离性(Isolation)
我们将狗哥向猫爷同时进⾏的两次转账操作分别称为T1和T2,在现实世界中T1和T2是应该没有关系的,可以先执⾏完T1,再执⾏T2,或者先执⾏完T2,再执⾏
T1,对应的数据库操作就像这样:
但是很不幸,真实的数据库中T1和T2的操作可能交替执⾏,⽐如这样:
如果按照上图中的执⾏顺序来进⾏两次转账的话,最终狗哥的账户⾥还剩6元钱,相当于只扣了5元钱,但是猫爷的账户⾥却成了12元钱,相当于多了10元钱
⼀致性(Consistency)
数据库中的数据全部符合现实世界中的约束(all defined rules),我们说这些数据就是⼀致的,或者说符合⼀致性的。
例如:银行中,个人的存款不是是负数、每次存款的变动时收入和支出的是一样的等等。
持久性(Durability)
如果当狗哥⾛掉之后,银⾏⼜把这次转账操作给撤销掉,恢复到没转账之前的样⼦,那猫爷不就惨了,⼜得被砍死了,所以这个持久性是⾮常重要的
事务的概念
把原⼦性(Atomicity)、隔离性(Isolation)、⼀致性(Consistency)和持久性(Durability)这四个词对应的英⽂单词⾸字⺟提取出来就是A、I、C、D,稍微变换⼀下顺序可以组成⼀个完整的英⽂单词:ACID。
把需要保证原⼦性、隔离性、⼀致性和持久性的⼀个或多个数据库操作称之为⼀个事务(英⽂名是:transaction)。
事务的状态:
- 活动的(active)
事务对应的数据库操作正在执⾏过程中时,我们就说该事务处在活动的状态。 - 部分提交的(partially committed)
当事务中的最后⼀个操作执⾏完成,但由于操作都在内存中执⾏,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。 - 失败的(failed)
当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库⾃身的错误、操作系统错误或者直接断电等)⽽⽆法继续执⾏,或者⼈为的停⽌当前事务的执⾏,我们就说该事务处在失败的状态。 - 中⽌的(aborted)
如果事务执⾏了半截⽽变为失败的状态,⽐如我们前边唠叨的狗哥向猫爷转账的事务,当狗哥账户的钱被扣除,但是猫爷账户的钱没有增加时遇到了错误,从⽽当前事务处在了失败的状态,那么就需要把已经修改的狗哥账户余额调整为未转账之前的⾦额,换句话说,就是要撤销失败事务对当前数据库造成的影响。
书⾯⼀点的话,我们把这个撤销的过程称之为回滚。当回滚操作执⾏完毕时,也就是数据库恢复到了执⾏事务之前的状态,我们就说该事务处在了中⽌的状态。 - 提交的(committed)
当⼀个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。
MySQL中事务的语法
开启事务
我们可以使⽤下边两种语句之⼀来开启⼀个事务:
BEGIN [WORK];
BEGIN语句代表开启⼀个事务,后边的单词WORK可有可⽆。开启事务后,就可以继续写若⼲条语句,这些语句都属于刚刚开启的这个事务。
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> 加⼊事务的语句…
START TRANSACTION;
START TRANSACTION语句和BEGIN语句有着相同的功效,都标志着开启⼀个事务,⽐如这样:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> 加⼊事务的语句…
不过⽐BEGIN语句⽜逼⼀点⼉的是,可以在START TRANSACTION语句后边跟随⼏个修饰符,就是它们⼏个:
- READ ONLY:标识当前事务是⼀个只读事务,也就是属于该事务的数据库操作只能读取数据,⽽不能修改数据。
- READ WRITE:标识当前事务是⼀个读写事务,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据。
- WITH CONSISTENT SNAPSHOT:启动⼀致性读
提交事务
开启事务之后就可以继续写需要放到该事务中的语句了,当最后⼀条语句写完了之后,我们就可以提交该事务了,提交的语句也很简单:
COMMIT [WORK]
⼿动中⽌事务
如果我们写了⼏条语句之后发现上边的某条语句写错了,我们可以⼿动的使⽤下边这个语句来将数据库恢复到事务执⾏之前的样⼦:
ROLLBACK [WORK]
⽀持事务的存储引擎
MySQL中并不是所有存储引擎都⽀持事务的功能,⽬前只有InnoDB和NDB存储引擎⽀持。
⾃动提交
MySQL中有⼀个系统变量autocommit:
mysql> SHOW VARIABLES LIKE 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row inset (0.01 sec)
可以看到它的默认值为ON,
隐式提交
当我们使⽤START TRANSACTION或者BEGIN语句开启了⼀个事务,或者把系统变量autocommit的值设置为OFF时,事务就不会进⾏⾃动提交,但是如果我们输⼊了某
些语句之后就会悄悄的提交掉,这种因为某些特殊的语句⽽导致事务提交的情况称为隐式提交。这些会导致事务隐式提交的语句包括:
- 定义或修改数据库对象的数据定义语⾔(Data definition language,缩写为:DDL)
- 隐式使⽤或修改mysql数据库中的表
- 事务控制或关于锁定的语句
- 当我们在⼀个事务还没提交或者回滚时就⼜使⽤START TRANSACTION或者BEGIN语句开启了另⼀个事务时,会隐式的提交上⼀个事务
- 或者当前的autocommit系统变量的值为OFF,我们⼿动把它调为ON时,也会隐式的提交前边语句所属的事务
- 或者使⽤LOCK TABLES、UNLOCK TABLES等关于锁定的语句也会隐式的提交前边语句所属的事务。
- 加载数据的语句
- 关于MySQL复制的⼀些语句
- 其它的⼀些语句
使⽤ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH、 LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET等语句也会隐式的提交前边语句所属的事务。
保存点
如果你开启了⼀个事务,并且已经敲了很多语句,忽然发现上⼀条语句有点问题,你只好使⽤ROLLBACK语句来让数据库状态恢复到事务执⾏之前的样⼦,然后⼀切从头再来,总有⼀种⼀夜回到解放前的感觉。所以设计数据库的⼤叔们提出了⼀个保存点(英⽂:savepoint)的概念。
- 定义保存点的语法如下:
SAVEPOINT 保存点名称; - 当我们想回滚到某个保存点时,可以使⽤下边这个语句(下边语句中的单词WORK和SAVEPOINT是可有可⽆的):
ROLLBACK [WORK] TO [SAVEPOINT] 保存点名称;
不过如果ROLLBACK语句后边不跟随保存点名称的话,会直接回滚到事务执⾏之前的状态。 - 如果我们想删除某个保存点,可以使⽤这个语句:
RELEASE SAVEPOINT 保存点名称;