事务的四个特性以及事务的隔离级别


🎵 什么是事务

事务是指是程序中一系列严密的逻辑操作,而且所有操作必须全部成功完成,否则在每个操作中所作的所有更改都会被撤消。可以简单理解为:就是把多件事情当做一件事情来处理,好比大家同在一条船上,要活一起活,要完一起完 。

🔥 为什么需要事务

事务是为解决 数据安全 操作提出的,事务控制实际上就是控制数据的安全访问。

Example:银行转帐业务,账户A要将自己账户上的1000元转到B账户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操作就需要事务,将A账户资金减少和B账户资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性。

🎉 事物的四个特性(ACID)

  1. 原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
  2. 一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
    Example:拿转账来说,假设用户A和用户B两者的钱加起来一共是20000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是20000,这就是事务的一致性。
  3. 隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  4. 持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。
    Example:比如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

🎃 并发事务导致的问题

在许多事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据时,会带来一些严重的问题。

  1. 第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。
    Example:小明到银行存钱,他的账户里原来的余额为100元,现在打算存入100元。在他存钱的过程中,银行年费扣了5元,余额只剩95元。突然他又想着这100元要用来请女朋友看电影吃饭,不打算存了。在他撤回存钱操作后,余额依然为他存钱之前的100元。所以那5块钱到底扣了谁的?
  2. 脏读(针对未提交数据):脏读是指在一个事务处理过程中读取了另一个未提交事务中的数据。
    Example:小明的银行卡余额中有100元,现在他准备点一份外卖,需要付款15元,但是这个时候,他的女朋友逛街看中了一件衣服95元,她也正在使用小明的银行卡进行付款。于是小明在付款的时候,程序后台只读取到他的余额只有5块钱了,不够付款去点外卖了,所以系统拒绝了他的交易,并告知余额不足。但是小明的女朋友最后因为密码不记得,无法进行交易。小明非常郁闷,明明银行卡中还有100元,怎么会余额不足呢?
  3. 幻读也叫虚读(针对增删操作):一个事务执行两次查询,第二次结果集包含了第一次中没有或者已经被删除的数据,造成了两次的结果不一致,只因另一个事务在这两次查询中进行插入或删除数据造成的。幻读是事务非独立执行的时候产生的一种现象。
    Example:事务T1对一个表中所有行的某个数据项做了从“1”改为“2”的操作,这时事务T2又对这个表中插入了一行该数据项为“1”的数据,并且提交给数据库。而操作事务T1的用户再次查看刚刚修改的数据时,发现还有一行没有被修改,其实是因为这个数据是从事务T2添加的,这就好像产生了幻觉一样,所以称之为幻读。
  4. 不可重复读(针对修改操作):一个事务两次读取同一行数据,结果得到不同状态的结果。是因为正好另一个事务更新了该数据,造成结果不同。
    Example:例如小明到银行进行查询自己的银行卡余额为100万,但是就在这个时候他女朋友从该银行卡取走50万,此时余额变为50万,小明再一次查询余额变成了50万,小明心态炸了(哈哈哈),这对于小明而言两次结果不一致就是不可重复读。
  5. 第二类丢失更新:是不可重复读的情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。
    Example:小明和女朋友一起去逛街。女朋友看中了一支口红,小明大方的掏出了自己的银行卡,告诉女朋友:亲爱的,随便刷,随便买,我坐着等你。然后小明就坐在商城座椅上玩手机,等着女朋友。这个时候,程序员的聊天群里有人推荐了一本书,小明一看,哎呀,真是本好书,还是限量发行呢,我一定更要买到。于是小明赶紧找到购买渠道,进行付款操作。而同时,小明的女朋友也在不亦乐乎的买买买,他们同时进行了一笔交易操作,但是这个时候银行系统出了问题,当他们都付款成功后,却发现,银行只扣了小明的买书钱,却没有扣去女朋友此时交易的钱。哈哈哈,小明真是太开心了!

🎧 数据库事务的隔离级别

为了应对上面并发情况下出现的问题,事务的隔离级别就产生了。当事务的隔离级别越高的时候,上面的问题就会越少,但是性能消耗也会越大。所以在实际生产过程中,要根据需求去确定隔离级别。事务隔离级别由低到高分别为 Read uncommitted 、Read committed 、Repeatable read 、Serializable 。

  1. Read uncommitted(读未提交):读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种。
  2. Read committed(读已提交):已提交,即能够读到那些已经提交的数据,能够防止脏读,但是无法解决不可重复读和幻读的问题。
  3. Repeatable read(可重复读):重复读取,即在数据读出来之后加锁,类似“select * from xxx for update”,明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。Repeatable read 的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决。
  4. Serializable(序列化):串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

🔔 提示

  1. 大多数数据库默认的事务隔离级别是 Read committed(读提交),比如 Sql Server、Oracle。Mysql 的默认隔离级别是 Repeatable read(重复读)。
  2. 隔离级别的设置只对当前链接有效。对于使用 MySQL 命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于 JDBC 操作数据库来说,一个 Connection 对象相当于一个链接,而对于 Connection 对象设置的隔离级别只对该 Connection 对象有效,与其他链接 Connection 对象无关。
  3. 设置数据库的隔离级别一定要是在开启事务之前。

评论