事务的基本概念
事务就是一组原子性的操作,这些操作要么全部发生,要么全部不发生。事务把数据库从一种一致性状态转换成另一种一致性状态。
事务最经典也经常被拿出来说例子就是转账了。
假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
-
原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
-
一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。
-
隔离性:一个事务的执行不能其它事务干扰。即一个事务内部的/操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
-
持久性:也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务隔离级别
-
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
存在脏读问题,A用户开启事务修改数据,在修改完未提交的时候,B用户查询了这条数据,查到的修修改了未提交的数据,如果这个时候A用户因为某种原因回滚了,B就拿到了脏数据。
简单点说就是读取到了未提交的数据。
-
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别会导致所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。存在的问题:不可重复
解决了脏读问题
但存在不可重复读问题,A用户在开启事务,查询完第一个select后,B用户对这一行数据进行更新并提交,A用户执行了和第一次一样的sql语句,发现查询出的数据和第一次不一样。
-
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。
解决了不可重复读问题
但存在幻读问题,假设我们要为state为VA的客户发放积分,假设满足VA只有一个用户,用户A先开启事务执行查询语句,只查到了一个用户,在提交之前,用户B执行了更新语句并进行提交,这个时候数据库中有两个VA用户了,但A用户只能查到一个,因为可重复读,执行的结果都是一样的,只有当A用户提交之后再查,才能查到2个用户。
-
Serializable(串行化)
通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
那可串行化是如何解决幻读问题的呢?
当用户B开启事务,执行完更新语句,提交之前,用户A开启事务,进行查询,这时候会转圈圈,也就是阻塞等待,他会意识到有其他人在更新这个表,我要再等等,当用户B提交了之后,用户A就可以查询出新增的这条记录了。
MySQL默认采用的REPEATABLE_READ隔离级别,Oracle默认采用的READ_COMMITTED隔离级别
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVCC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容),但是你要知道的是InnoDB存储引擎默认使用REPEATABLE-READ并不会有任何性能损失。
脏读、不可重复读、幻读
脏读是指一个事务读取到了其他事务没有提交的数据
不可重复读是指一个事务内多次根据同一个查询条件查询出来的同一行记录的值不一样(update)
幻读是指一个事务内多次根据同个条件查出来的记录行数不一样(insert:插入意向锁; delete: record lock)
INNODB 在RR级别如何解决幻读
在快照读情况下,mysql通过mvcc来避免幻读。
在当前读情况下,mysql通过next-key来避免幻读。