数据库笔记1——mvcc(多版本并发控制)
一、数据库4大隔离级别
为了解决并发事务存在的脏读(读到了其他事务已修改还未提交的数据)、不可重复读(一个事务里两次读同一个数据,但数据不一样,因为这个数据在两次读的间隔里,有其他事务修改了这个数据并提交了)、幻读(a事务读取表的记录数为x,然后有其他事务对这个表进行了新增或删除行,a事务再读取该表时,发现记录数不再是x)等问题,数据库大叔设计了四种隔离级别。分别是读未提交,读已提交,可重复读,串行化(Serializable)。
- 读未提交隔离级别:只限制了两个数据不能同时修改,但是修改数据的时候,即使事务未提交,都是可以被别的事务读取到的,这级别的事务隔离有脏读、不可重复读、幻读的问题;
- 读已提交隔离级别:当前事务只能读取到其他事务提交的数据,所以这种事务的隔离级别解决了脏读问题,但还是会存在不可重复读、幻读问题;
- 可重复读:sqlserver通过限制了读取数据的时候,不可以进行修改,所以解决了不可重复读的问题,但是读取范围数据的时候,是可以插入数据,所以还会存在幻读问题(而mysql是通过mvcc:多版本并发控制,解决了不可重复读和幻读)
- 串行化:事务最高的隔离级别,在该级别下,所有事务都是进行串行化顺序执行的。可以避免脏读、不可重复读与幻读所有并发问题。但是这种事务隔离级别下,事务执行很耗性能。
二、数据库默认隔离级别:
mysql ---可重复读(在这个级别,因为普通查询都是快照读(其他事务的新增记录不会被看到,另一种是当前读),所以不会造成幻读),
oracle,sql server ---读已提交,sqlserver可以设置SNAPSHOT (快照隔离)来解决幻读。
三、 Mysql的Mvcc:多版本并发控制,解决了幻读
每一行数据都有两列隐藏列:创建版本号和删除(更新)版本号,这两列的版本号就是当时执行插入、修改、删除时的全局事务id。
SELECT
InnoDB会根据以下两个条件检查每行记录:
A、 InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。(解决了其他事务新增了行的幻读)
B、 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除,因为如果事务开始前就已经被删除,那么行的删除版本号会小于当前事务版本号。如果行在事务开始后被其他事务删除了,该行的删除版本号一定大于当前事务版本号,此时再读一次还是可以读出和前一次相同的记录。(解决了其他事务删除了行的幻读)
只有符合上述两个条件的记录,才能返回作为查询结果
INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
DELETE
InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
UPDATE
InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。