面试的时候碰到了此类问题,此前认知一直模棱两可,特在此重新学习总结一下:
1、脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例如:
张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
与此同时,
事务B正在读取张三的工资,读取到张三的工资为8000。
随后,
事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
最后,
事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。
2、不可重复读:是指在一个事务中两次读取的数据因为另一个事务的影响而不同的情况。
例如:
在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
与此同时,
事务B把张三的工资改为8000,并提交了事务。
随后,
在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
3、幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。(针对新增)
例如:
目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
此时,
事务B插入一条工资也为5000的记录。
这是,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。
第一类丢失更新
A事务撤销时,把已经提交的B事务的更新数据覆盖了(一个撤销的事务覆盖了另一个成功提交的事务);
第二类丢失更新
A事务覆盖B事务已经提交的数据,造成B事务所做的操作丢失 (一个成功提交的事务影响了另一个成功及提交的事务)
事务隔离级别
读未提交(Read Uncommitted)
原理:任何操作都不加锁
读提交(Read Commit)
原理:读操作不加锁,写操作加锁。读被加锁的数据时,读事务每次都读undo log中的最近版本,因此可能对同一数据读到不同的版本(不可重复读),但能保证每次都读到最新的数据。
可重复读(Reapable Read)
原理:第一次读数据的时候就将数据加行锁(共享锁),使其他事务不能修改当前数据,即可实现可重复读。但是不能锁住insert进来的新的数据,当前事务读取或者修改的同时,另一个事务还是可以insert提交,造成幻读。
串行化(Serializable)
原理:锁表,读锁和写锁阻塞。
Mysql事务特性:
幻读的问题理应由更高的隔离级别来解决,但mysql和其它数据不一样,它同样在可重复读的隔离级别解决了这个问题.
也就是说, mysql的可重复读的隔离级别解决了 "不可重复读" 和 “幻读” 2个问题. 稍后我们可以看见它是如何解决的.
而oracle数据库,可能需要在 “SERIALIZABLE ” 事务隔离级别下才能解决 幻读问题.
mysql默认的隔离级别也是: REPEATABLE READ(可重复读)
MVCC(Multi-Version Concurrency Control) 多版本并发控制
MVCC的实现,是通过保存数据在某个时间点的快照来实现的.
InnoDB的MVCC是通过在每行记录后面保存2个隐藏的列来实现的,一列保存了行的创建时间,一列保存了行的过期时间(或删除时间).但它们都存储的是系统版本号
MVCC最大的作用是: 实现了非阻塞的读操作,写操作也只锁定了必要的行.
MYSQL的MVCC 只在 read committed 和 repeatable read 2个隔离级别下工作.
在MVCC的机制下,mysql InnoDB(默认隔离级别)的增删改查变成了如下模式:
SELECT:
1, InnoDB只查找版本早于当前事务版本的数据行(行的系统版本号小于等于事务的系统版本号)
2, 行的删除号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行,在事务开始之前未被删除.
INSERT:
InnoDB 为新插入的每一行保存当前系统版本号做为行版本号。
DELETE:
INNODB 为删除的每一行保存当前系统版本号作为行删除标识
UPDATE:
InnoDB 为插入的每一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识.