TCL(Transaction Control Language)事务控制语言
事务
事务是指由一系列数据库操作组成的一个完整的逻辑过程,这个过程中的所有操作要么都成功,要么都不成功。比如:常见的例子就是银行转账的例子,一次转账操作会包含多个数据库操作,而这些数据库操作需要放到一个事务当中,保证其要么都成功,要么都不成功。
mysql中innodb支持事务,而myisam,memory等不支持事务
/*
#开启事务
SET autocommit=0;
START TRANSACTION;
#一组增删改查sql
...
...
#结束事务
COMMIT/ROLLBACK;
*/
事务的ACID特性
CID是事务的四个特性,指的是atomicity,原子性;consistency,一致性;isolation,隔离性;durability,持久性。
- 原子性(atomicity): 指所有在事务中的操作要么都成功,要么都不成功,所有的操作都不可分割,没有中间状态。一旦某一步执行失败,就会全部回滚到初始状态。
- 一致性(consistency): 指的是逻辑上的一致性,即所有操作是符合现实当中的期望的。具体参考下一节
- 隔离性(isolation): 即不同事务之间的相互影响和隔离的程度。比如,不同的隔离级别,事务的并发程度也不同,最强的隔离状态是所有的事务都是串行化的(serializable)(即一个事务完成之后才能进行下一个事务),这样并发性也会降到最低,在保证了强一致性的情况下,性能也会受很大影响,所以在实际工程当中,往往会折中一下。
- 持久性(durability): 可以简单地理解为事务执行完毕后数据不可逆并持久化存储于存储系统当中
理解一致性
实际上我们通常说的数据库事务的一致性和分布式系统的一致性并不是一个概念。这里可以区分成“内部一致性”和“外部一致性”。“内部一致性”搞数据库的人很少这么说,一般就直接说一致性,更准确的说是“Consistency in ACID”(“事务 ACID 属性中的一致性”);“外部一致性”是针对分布式系统而言的,分布式领域提及的 Consistency 表示系统的正确性模型,著名的也是臭名昭著的 CAP 理论中的 C 就是这个范畴的。这主要是由于分布式系统写入和读取都可能不在同一台机器上,而这必然会有一段时间导致不同机器上所存的数据不一致的情况,这就是所谓的“不一致时间窗口”。
1. 内部一致性
要理解内部一致性也就是我们通常所说的ACID中的一致性,就必须从反面考虑什么情况下是不一致的。不一致的情况主要有以下几种情况:
- 修改丢失:丢失修改是事务A和B先后更改数据数据x(假设初始是x0),但是在A未正式更改前,B已经读取了原先的数据x0,最后A更改后为x1,B更改的并不是A更新后的x1,而是更改的x0,更改后假设为x2,这时x2将x1覆盖了,相当于事务A针对x的更改丢失了。
- 脏读: 事务T1读取了T2更改的x,但是T2在实际存储数据时可能出错回滚了,这时T1读取的实际是无效的数据,这种情况下就是脏读
- 不可重复读:是说在T1读取x时,由于中间T2更改了x,所以T1前后两次读取的x值不相同,这就是所谓的不可重复读
- 幻读:在T1读取符合某个条件的所有记录时,T2增加了一条符合该条件的记录,这就导致T1执行过程中前后读取的记录可能不一致,即T2之后读取时会多出一条记录。
其中前三种(丢失修改、不能重复读、脏读)都是由于并发事务在修改同一份数据的时候导致的问题,此类问题可以通过对同一个资源加锁的方式来解决,而最后一种情况是由于不同事务并发时,新增数据导致的问题,对于新增的记录是无法加锁的,此种情况只能通过事务的串行化来解决。而串行化与并发是矛盾的,所以要在性能和事务的一致性强度上取得一个平衡,就涉及到不同的隔离等级,关于隔离等级,详见理解隔离性一节。
2. 外部一致性
在分布式系统中我们所说的一致性,也就是外部一致性,通常会分为强一致性,弱一致性,还有最终一致性,而要理解外部一致性,需要对CAP理论(Consistency,Availability和Partition Tolerance)有所了解,关于CAP详见CAP定理一节。
- 强一致性:指系统中的某个数据被成功更新后,后续任何对该数据的读取操作都将得到更新后的值
- 弱一致性:弱一致性是相对于强一致性而言,它不保证总能得到最新的值;
- 最终一致性:是弱一致性的特殊形式,即保证在没有新的更新的条件下,经过一段“不一致时间窗口”,最终所有的访问都是最后更新的值。最常见的是DNS服务,更新域名指向的机器后,多级缓存要等到expiration time的时候才会更新,但是随着时间的推移,最终数据会趋于一致。
理解隔离性
事务的隔离级别从低到高有
读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
- Read Uncommitted:事务读数据时不会加锁,写数据时会有行级共享锁。假设事务1先于事务2,当事务1更新数据的时候,事务2可以读取事务1未提交的数据,但是不能更新事务1正在更新的数据。而如果事务1只是读数据,那么事务2既可以读数据,也可以更新数据。
这种情况下无法规避脏读,不可重复读的问题。 - Read Committed:即在一个事务修改数据过程中,如果事务还没提交,其他事务不能读该数据,或者说只能读取committed的数据。事务读数据的瞬间会加行级共享锁,一旦读完该行,立即释放该行级共享锁;而写数据的瞬间会加行级排它锁,直到事务结束。这种情况下就避免了脏读,但是却不能避免不可重复读的问题
- Repeatable Read:当然就再升一级,为的就是避免不可重复读的问题,所以名字叫repeatable read。怎么实现的呢,我们知道read committed是,事务读操作只在读的一瞬间加锁,读完这行就释放锁了,而repeatable read级别是读的一瞬间加锁,但是一直到事务结束才释放锁。但是repeatable read不能解决幻读的问题,因为幻读是增加记录,并不是更改原先的记录。
- Serialization:到达这一级别的隔离,可以彻底解决一致性的所有问题。一般来说是通过加表锁来解决串行化的问题。