“事务”,一个经常能听到的概念,它到底是个什么东西呢?
数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成,事务的正确执行,使得数据从一种状态转换到另外一种状态。
它具有四大特性:
A(原子性):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节;
张三给李四转账,必须是张三扣钱、李四加钱这两个环节一起完成,这两个动作必须是原子化的,要么都成功,要么都失败
C(一致性):事务开始前和结束后,数据库的完整性约束没有被破坏;
还是上面转账的例子,双方必须保持一致,不能说张三的钱被扣了,但是李四的钱没有加上,这样就会导致数据错乱
I(隔离性):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰;
在张三转账这个事务没有完成之前,其他事务看到张三的钱不会变少,假设开始有100元,那么在他没完成转账之前,看到的还应该是100元
D(持久性):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚;
转账完成之后,数据就会固定下来,不可回滚,此时其他事务可以正确看到张三扣钱之后的金额
ACD都好理解,关于I(隔离性),又细分了四种隔离级别,单纯从字面上理解,就是不让事务之间相互看数据,为什么需要做的这么复杂呢?
那我们就需要来看一下“并发”了,当单一的线程在执行任务时,有很多的问题都显现不出来,而一旦在多线程并发的情况下,在数据读写的时候,会产生:脏读、幻读、不可重复读这几个问题。
脏读:查询到了其他事务还未提交的数据;
这个概念很好理解,就拿转账来说,张三只是想转账给李四,不代表一定能成功,有可能中途出错回滚了,而如果在没提交之前就被其他事务看到了,那张三账户里的钱,相当于就平白无故的少了
不可重复读:在同一个事务中,两次查询相同条件的数据,看到的结果不一致;
在转账过程中,如果有人查询张三的账户余额,那么在成功前后看到的金额不一致,也就是说明明查询条件一样,但是返回的结果却不一样
幻读:两次操作相同条件的数据,看到的结果不一致;
我想在想给系统中的人都打一个业务flag(假设目前只有张三、李四两个人),此时我直接一条update语句修改所有数据,但是如果在我update的时候,王五注册了账号,那么我update完了之后一看数据库,诶,怎么少update了一条,是否我自己出现了幻觉?
注意:不可重复读和幻读的区别,在于前者是针对于修改,后者则是针对新增、删除。按照我个人的理解来看,不可重复读和幻读,其实只是对理论概念的强行解释,针对的是人是否看错了,是否看花眼了,而且,在数据库的默认隔离级别“读已提交”也很好的证明了这一点。
好了,不扯多了,既然有问题,那么就会有解决方案:设置事务的隔离级别
READ_UNCOMMITTED:读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用;
READ_COMMITED:读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读;
REPEATABLE_READ:可重复读取,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以修改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决;
SERLALIZABLE:串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了;
总结如下:
事务隔离级别
|
脏读
|
不可重复读
|
幻读
|
读未提交(read-uncommitted)
|
是
|
是
|
是
|
读已提交(read-committed)
|
否
|
是
|
是
|
可重复读(repeatable-read)
|
否
|
否
|
是
|
串行化(serializable)
|
否
|
否
|
否
|
隔离级别并非越高越好,因为隔离级别越高,就意味着数据库需要花费更多的精力,加更多地锁来处理,也就意味着性能越差,所以需要根据实际情况做选择,一般情况下就选择read-commited,避免了脏读,性能也还可以。
转自:不忘初心 https://www.jiweichengzhu.com/article/52dd32eec13c4c3aa41ab1078caf7818
文章评论