数据库隔离级别
这部分主要看的是ACID特性中的隔离性。隔离级别越高,事务越安全,但是并发性会变差。
数据库的三种隔离问题:
- 脏读:事务A更新了数据,但未提交,事务B读取了这个更新后的数据。随后事务A回滚,结果导致事务B读取的是脏数据,因此叫脏读。
- 不可重复读:事务A对同一个数据先后读两次。第一次读取完毕后,事务B更新了这个数据,随后事务A进行第二次读取,发现这个数据转眼间竟然前后不一致。这就是不可重复读。(重点在修改)
- 幻读:事务A查询或者修改数据表的符合范围条件的数据记录,但期间事务B向表中这个范围内插入了一条新的数据。则事务A操作完成后,发现竟然还有一条没有修改的数据行,就像发生了幻觉一样,所以叫幻读。(重点在新增)
数据库有四种隔离级别(由低到高):
- 读未提交:可以读到未提交的数据。会出现脏读、不可重复读、幻读的问题。一般不使用这种隔离级别。
- 读提交:只能读到已经提交了的数据。可以防止脏读,但不能防止不可重复读和幻读。
- 可重复读:可以保证一个事务中对于同一行记录的前后多次读取是一致的,可以防止脏读和不可重复读,但不能防止幻读,因为这个隔离级别没有限制新增操作。
- 可串行化:是完全服从ACID的最高隔离级别。所有的事务依次逐个执行,完全舍弃并发,当然没有并发时的隔离问题,但效率很低。
在MySQL中,默认的隔离级别是可重复读,并且使用间隙锁解决了幻读问题。也就是说,mysql默认解决了脏读、不可重复读和幻读的问题。
MYSQL有两种方式实现可重复读的隔离级别
- 一种是基于锁的并发控制。MySQL可以通过加锁(事务结束才释放)来实现可重复读级别。
- 另一种是MVCC(多版本并发控制)。MVCC最大的优势是写加锁,但读不加锁,使得读写不阻塞,在读多写少的应用场景下能够显著提高系统的并发性能。
MVCC 多版本并发控制
读未提交与脏读
读未提交会导致脏读的问题,因为这个级别没有加锁。加共享锁和排他锁可以解决脏读的问题,但是会使读操作和写操作无法并发执行,降低效率。
MVCC解决脏读的方法类似 copyonwrite,MVCC允许每行数据有多个版本,并且对于写操作和读操作有限制
- 写操作不直接在原数据上修改,而是拷贝一份数据出来,在拷贝数据上修改,写操作完成并且提交后再原子性地替换数据并且加上新的版本号。
- 读操作只能读取到读操作执行时的当前版本及之前的旧版本。
假设事务A要读数据时,事务B正在写这行数据而且未提交,则此时数据上只有旧的版本号,读操作不会读到事务B未提交的数据修改,因此MVCC可以解决脏读问题,此时隔离级别已经提升至读提交级别。
读提交与不可重复读
读提交虽然解决了脏读的问题,但是仍会有不可重复读的问题。
上面MVCC可以使读操作和写操作并发执行,但是仅限于每个事务都只有一条语句的情况下。假设事务A对同一个数据先后读两次,期间事务B修改数据完毕并且提交,则第二次读操作执行时版本号已更新,会读出修改后的数据,导致两个语句读出来的数据前后不一致。
所以关键在于,前面MVCC对读操作的限制是以语句为单位的,限制读操作只能读到该语句执行时以及之前的版本号。而MVCC对于不可重复读的解决方法是,对读操作的限制提升为以事务为单位,即一个事务里的所有读操作都只能读事务开始时以及之前的版本号。这样即使期间另一个事务更新的版本号,后面的读操作也只能读到旧的数据,从而使读到的数据前后一致。此时隔离级别已经提升至可重复读级别。