事务的产生是为了简化我们的编程模型,使我们在开发的过程中不用考虑各种潜在的错误和并发问题,而不是伴随着数据库系统天生就存在的。
事务支持是在引擎层实现的,InnoDB支持事务而MyISAM不支持。
假设数据库不支持事务,看下面的示例:
a账户要转给b账户100元:
update acount set amount =amount -100 where user=a // 1
update acount set amount =amount +100 where user=b // 2
假设程序第一步执行完成了执行到第2步的时候出了问题,那么将导致a账户白白少了100块。此时就能看到事务的重要性了。
数据库事务需要具备四大特性(ACID)
-
原子性(Atomicity)
原子性是指事务不允许部分执行。事务包含的所有操作要么全部成功,要么全部失败并回滚。
-
一致性(Consistency)
如果事务执行期间没有出现系统错误或其他事务错误,并且数据库在事务开始期间是数据一致的,那么在该事务结束时,我们认为数据库仍然保证了一致性。
-
隔离性(Isolation)
如果事务之间不是隔离的,可能会出现以下问题:
(1) 脏读(dirty read):一个事务在处理过程中读取了另外一个事务未提交的数据。例如事务A中a给b转100块钱,在该事务中首先a的账户减100块,而在此时事务B中查询a的账户发现a少了100块,然后事务A中b账户加钱时发生了意外导致事务A回滚。这个时候事务B拿到的a账户就是脏数据了。
(2) 不可重复读(none-repeatable read):在一个事务范围内多次查询某个数据却得到不同的结果。例如事务C中b要提现100块,首先查询b账户余额发现有100块满足提现要求,此时事务D中b转账100给a并且提交事务成功,在事务C中再次查询b的账户余额发现已经没有钱了。
脏读和不可重复读的区别在于脏读是读取到了另一个事务未提交的数据,不可重复读是读取到了其他事务提交的数据。
(3) 幻读(phantom read):事务E中对一个表中所有数据做了从0修改为1的操作,这时事务F又向这个表插入了一行数据,而这个数据项中值为0并提交事务。此时事务E查询会发现还有一行数据没有修改,这就是幻读。
不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
-
持久性(Durability)
在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
数据库为我们提供了四种隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
MySQL
查询当前窗口的事务隔离级别
8以前:select @@tx_isolation;
8:select @@transaction_isolation;
- 事务隔离级别为读提交时,写数据只会锁住相应的行。
- 事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
- 事务隔离级别为串行化时,读写数据都会锁住整张表。
- 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
在MySQL
中,默认的隔离级别是REPEATABLE-READ(可重复读),并且解决了