事务以及隔离级别详解
一、什么是事务?
事务的原子性:通俗一点讲,事务的原子性就是一件完整的事情(有可能只有一步,有可能有很多步),要么做完,要么不做!这一点,也就是事务最终要的一点:事物的原子性!
事务的一致性:也就是执行完事务之后,数据不会被破坏!打个比方说:如果重A账户抓张到B账户,不能因为A账户扣了钱,而B账户没有收到钱,这个时候用户一定非常气愤!
事物的隔离性:当我们编写了一条update语句后,提交到数据库的时候,有可能别人对同一条数据执行了另一个update语句(或者delete语句),这个时候如果不加控制,后果不堪设想。我们必须保证事务实践是隔离的。同一条数据同一个时刻,只能有一个事务在操作!不能被其他事务所干扰!
事物的持久性:很简单了,我执行了一个事务,必须是留下“痕迹”,将结果记录下来,否则,我执行事务干嘛!
其实,这几个基本概念,稍微捋一下,其内部逻辑很明了:原子性基础,隔离是手段,持久性是结果,都是围绕着这个结果的“一致性”这个“老大”混的!如果数据不一致,那能叫健康的系统吗?
二、隔离级别
既然隔离性是手段,那么如果做到隔离呢?也就是说我哪知道当有两个事务同时进行的时候,哪个先来哪个后做怎么做呢?这就是我们给事务定义的四个级别了:
Read-Uncommited:读未提交的数据!--一个事务可以读取另一个事务未提交的数据!
打个比方说吧:老板给你发工资,你的工资本来是1.2万,但是,老板一不小心按成了1.5,这个时候该事务尚未提交,但是你可以读取数据,你一看,涨了3k很happy。后来,老板在提交前发现了这个问题,事务回滚,重新改过来了,你再去查又变回了1.2,很不爽,去找老板.....这就是脏读! 由于都去了未提交的数据而导致的错误!
Read-Commited:读已提交!
顾名思义,只能读取已经提交的内容!显然,这个能够解决上一个问题。但是,又有新问题来了。如果你拿着1.2w的工资出去happy,happy完后准备付钱,
(你用手机银行支付的时候好男人一般会上交工资卡,哎....),这个时候系统显示余额是1.2w,就在这个时候,你老婆耍你的卡买了个电视,1.1w多,当系统准备扣费的时候,系统检测你只剩下零头了(此次检测显然要等老婆那边扣款完成并提交),瞬间郁闷了!
(注意:查余额-->输入金额-->查余额-->扣款这显然是你这次happy付款的一个事务)
这就是读取已提交,虽然解决了脏读问题,但是又来了一个新问题:那就是同一个事务范围内,两个相同的查询却出现了不同的结果!这就是不可重复读的!因为你这个事务开启了,但是另一个事务还是可以修改事务所涉及的数据!
Repeatable-Read:可重复读
此处的可重复读就是解决上面的问题的,即当一个事务开始后,就不允许其他事务对该事务涉及的那条数据进行修改了!那么在该事务后续对该数据的查询中就不会出现同一个事务中查询同一条数据但是查询结果不一样了(不要理解错了,不是可以重复读一条数据!)
ok,可重复读的问题解决了,那么,还有一个问题------幻读!
还是以你拿工资出去happy的例子老说吧!(这个比较有切身体会......)
你出去花钱,老婆有可能会拿对账单查账(每一次消费都会生成一条消费记录,然后汇总);当你某一天打算去买一台电脑的时候,你老婆正好查你的账,本来查着你本月只花了2k,老婆很开心,那么她就准备打印对账单了,此时,你正好刷卡买电脑,新增了一条消费记录,完成后,老婆提交打印请求,打出来后,我去,一看总价花了1.2w,心里瞬间抓狂。。。感觉出现了“幻觉”!这就是幻读,针对的就是在一个事务执行时,另一个事务插入里另一条记录,导致该事务两次查询结果的不一致!
由此可以看到,可重复读针对的是对同一条数据的update,而幻读是针对同一组数据的insert
因为重复读只能让该事务涉及的单条数据不能修改,而不会管其他数据不插入!
Serializable:序列化!
那么,序列化就是解决幻读的!在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
说到这里,其实大家也都了解了,只又脏读是绝对不允许的,他会破坏数据的一致性,其余的其实也就是跟具体业务相关了!所以,在mysql中,默认的事务隔离级别就是Read-Commited!
搞清楚了上面这段话,那么以后设计系统,需要考虑那种级别的事务处理,就不是难事了!