定义:
事务是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是个不可分割的工作单位。如果某一事务成功,则在该事务中进行的所有数据更改均会提交,成为数据库中永久的组成部分。如果事务遇到错误,则必须取消或回滚,所有数据均被更改清除。
特性(ACID):
1)原子性(Atomicity):
事务是数据库的逻辑工作单元,事务中包括的诸多操纵要么全做,要么全不做。
2)一致性(Consistency):
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
3)隔离性(Isolation):
一个事务的执行不能被其他事务干扰。
4)持续性/永久性(Durability):
一个事务一但提交,它对数据库中数据的改变就应该是永久性的。
隔离级别:
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这呲个级别可以逐个解决脏读、不可重复读和幻读等问题。
在理解事务的隔离级别之前,先看看什么是脏读、不可重复读、幻读。
脏读:
允许事务B可以读到事务A修改而未提交的数据,可能会造成了脏读。脏读本质就是无效的数据,只有当事务A回滚,那么事务B读到的数据才为无效的,所以这里只是可能造成脏读,当事务A不回滚的时候,事务B读到的数据就不为脏数据,也就是有效的数据,脏数据会导致以后的操作都会发生错误,一定要去避免,不能凭借侥幸,事务A不能百分之百保证不回滚,所以这种隔离级别很少用于实际应用,并且它的性能也不比其他级别好多少。
不可重复读:
不可重复读是指在一个事务范围中2次或者对次查询同一数据M返回了不同的数据。例如:事务B读取某一数据,事务A修改了该数据M并且提交,事务B又读取该数据M(可能是再次校验),在同一个事务B中,读取同一个数据M的结果集不同。
幻读:
当用户读取某一个范围的数据行时,另一个事务又在该数据范围内查询了新行,当用户再读取该范围的数据行时,会发现会有新的“幻影行”,例如:事务B读某一个数据M,事务A对数据M增加了一行并提交,事务B又读数据M,发生多出了一行造成结果不一致(如果行数相同,则是不可重复读)。
做了一份兼职,老板给小张把3000元打到小张的账号上,但是该事务并未提交,而小张正好去查看账户,发现工资已经到账,是3000元,甚是高兴。但非常不幸的是,领导发现给小张的工资金额不对,是300元,于是迅速回滚了事务,修改金额后,将事务提交,最后小张的实际工资只有300元,小张很是伤心。
这就是我们所说的脏读,事务A:领导给小张发工资,事务B:小张查询工资账户,事务B读取了事务A尚未提交的数据。当隔离级别设定为未提交读是,就可能出现脏读。
Read committed 提交读
小张拿着工资卡去消费,系统读取到卡里确实有300元,而此时她的女朋友也正好在网上转账,把小张工资卡的300元转到另一账户,并在小张之前提交了事务,当小张扣款是时,系统检查到小张的工资卡已经没有钱,扣款失败,小张十分纳闷,这是什么鬼。。。
这就是我们所说的可重复读,两个并发的事务,事务A:小张消费,事务B:小张的女朋友网上转账,事务A实现读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
当隔离级别设置为提交读时,避免了脏读,但是可能会造成不可重复读。大多数的数据库的默认级别就提交读,如:SqlServer,Oracle。
Repeatable read 重复读
当隔离级别设置为重复读时,可以避免不可重复读。当小张拿着工资卡去消费时,一旦系统开始读取工资卡的信息,小张的女朋友就不可能对该记录进行修改,也就是小张的女朋友不能在此时转账。
虽然重复读避免了不可重复读,但还有可能出现幻读。
小张的女朋友在银行部门工作,她时常通过银行内部系统查看小张的信用卡消费记录。有一天,她在查询到小张的当月信用卡的总消费金额(select sum(amount)from transaction where month=本月)为25元,而小张此时正好在外面胡吃海塞后再收银台买单,消费200元,即新增了一条100元的消费记录(insert transaction。。。),并提交了事务,随后小张的女朋友将小张当月信用卡消费的明细打印到A4纸上,却发现消费总额为250元,小张的女朋友很诧异,以为出现了幻觉,幻读就是这种情况。
MySQL的默认隔离级别是重复读。
Serializable序列化
序列化是最高的事务隔离级别,同时代价也最高,性能最低,一般很少用,在该级别下事务顺序执行,不仅可以避免脏读,不可重复读,还避免了幻读。
总结:
关于事务的简单总结就这么多,想要有更深的体会还得多多实践,希望“小张和她女朋友”的故事可以对大家有所帮助。