面试必问基础-数据库事务隔离级别原理详解
Posted 小猿快充
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试必问基础-数据库事务隔离级别原理详解相关的知识,希望对你有一定的参考价值。
优秀的SQLBoy怎能只满足于写CRUD?
项目经理:“小王,这里有个需求,需要我们先这样,再那样......”。balabala三小时后,小王已经了解了需求,打开IDE工具,开整。那么第一步,就是接触数据库,建表写sql,这可是基础呀!某位哲学家曾经说过,地基打不好,房子高不了。所以今天的大脑充电计划便是——数据库基础之事务隔离级别原理。
什么是事务?
事务(Transaction)一词,是伴随着并发产生的。并发问题的本质在于一条逻辑代码在机器层面可能需要几条指令来完成,也就是说这条逻辑代码可能在多个机器周期内完成,如果在顺时执行时这样执行是不会存在问题的,而在并发执行时就会出现数据不一致的情况,事务的引入便是为了解决这一问题。事务具有ACID四个缺一不可的基本特征。
原子性(Atomicity)、一致性(Consistency)
隔离性(Isolation) 、持久性(Durability)
本文为对事务隔离性的解析
隔离性解析
01、隔离的是什么?
多个并发的事务同时访问一个数据库时,有的执行插入操作,有的执行查询操作,有的执行删除操作。一个事务不应该被另一个事务所干扰,每个并发的事务间要相互进行隔离。
02、事务是什么?
一个sql语句就是一个事务,事务可以保证一组sql语句要么都成功,要么都失败。最好理解的就是插入100条数据,如果不加事务,成功63条,数据对比将是一个令人非常头大的问题,加入事务则可以很好的解决这一问题。有数据插入失败,直接回滚,无事发生。我们只需要处理好程序重新执行插入就可以。
03、如果不加隔离,将会产生的情况。
假设我们现有一张表(test),内含ID和NAME两个字段,现有以下三条数据,假设我们操作时不加事务,则会出现下面几种情况。
不加事务情况分析
01、脏读(dirty read)
假如现有一条加入事务的插入sql,需要往上面的数据库表中添加(4,“赵六”)(5,“钱七”)两条数据:insert into test values (4, '赵六'),(5,“钱七”);
当(4,“赵六”)数据插入完成但(5,“钱七”)还没插入完成时,来了一条加入事务的查询sql:select Name from test;他将会查出含有(4,“赵六”)的4条记录,而上面的插入sql事务并未提交,当执行到插入(5,“钱七”)数据时,由于某种原因sql插入报错了,插入sql事务回滚。此时,数据库中并没有存入(4,“赵六”),由于没有事务隔离,查询sql却将他查了出来,这就是脏读(dirty read)。
02、不可重复读(unrepeatable read)
假如我现在需要查一下ID为1的数据NAME值是什么,于是执行了一条查询sql:select Name from test where ID = 1;可以查到结果张三。此时,由于张三改名了,于是执行了一条修改sql:update test set Name = '张大三' where ID = 1;接着刚才查名字的sql想再看看查询结果,于是又执行了一遍,结果这次查询的结果是张大三。由于没有事务隔离,导致两次查询结果不同,这就是不可重复读(unrepeatable read)。
03、幻读(phantom problem)
假如我们需要查一下test表都有哪些数据,于是执行查询sql:select * from test;查询到三条记录。此时,我们新插入一条记录:insert into test values (4, '赵六');接着,刚才的查询sql想再确定一下查询记录,于是又执行了一遍,结果,这次查询到四条记录。由于没有事务隔离,导致两次查询结果不同,这就是幻读(phantom problem)。
隔离级别
根据隔离级别由低到高,可分为四种:
1.读未提交(Read Uncommitted)
读未提交,就如我们上面解释脏读情况一样,可以读到未提交的内容。这种隔离级别的一致性最差,可能会产生“脏读”、“不可重复读”、“幻读”。由于并未作出任何限制,所以无特殊情况,基本不会用到这种隔离级别。
2.读提交(Read Committed)
读提交,即可以读到已提交的内容。隔离级别高于读未提交,是SQL Server和Oracle的默认隔离级别。这种隔离级别能够有效的避免脏读,但除非在查询中显示的加锁,如:
select * from test where ID=2 lock in share mode;
select * from test where ID=2 for update;
普通的查询是不会加锁的。
“读提交”和“读未提交”,都没有查询加锁,但是却可以避免脏读,这是因为使用了“快照(snapshot)”机制,故读提交也被称为“快照读(Snapshot Read)”。假设没有“快照读”,那么当一个更新的事务没有提交时,另一个对更新数据进行查询的事务会因为无法查询而被阻塞,这种情况下,并发能力就相当的差。而“快照读”就可以完成高并发的查询。“读提交”只能避免“脏读”,并不能避免“不可重复读”和“幻读”。
3.可重复读(Repeated Read)
可重复读,是专门针对“不可重复读”这种情况而制定的隔离级别。而它也是MySql的默认隔离级别。在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了,而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。
4.串行化(Serializable)
串行化是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率极低,性能开销大,基本没人用。
小结
(1)Read uncommitted(读未提交):允许脏读、幻读、不可重复读。
(2)Read committed(读已提交):禁止脏读,允许幻读、不可重复读。
(3)Repeatable read(可重复读):禁止脏读、不可重复读,允许幻读。
(4)Serializable(串行化):禁止脏读、不可重复读、幻读。
理解使用
为什么会出现“脏读”?
因为没有“select”操作没有规矩。
为什么会出现“不可重复读”?
因为“update”操作没有规矩。
为什么会出现“幻读”?
因为“insert”和“delete”操作没有规矩。
“读未提(Read Uncommitted)”能预防啥?
啥都预防不了。
“读提交(Read Committed)”能预防啥?
使用“快照读(Snapshot Read)”,避免“脏读”。
“可重复读(Repeated Red)”能预防啥?
使用“快照读(Snapshot Read)”,锁住被读取记录,避免出现“脏读”、“不可重复读”。
“串行化(Serializable)”能预防啥?
避免“脏读”、“不可重复读”、“幻读”,不过效率极低,开销极大。
END
以上是关于面试必问基础-数据库事务隔离级别原理详解的主要内容,如果未能解决你的问题,请参考以下文章