Mysql事务

  • 事务单元就是完成一个具体的业务的最小单元,事务单元与事务单元的关系只有四种:读读、读写、写读、写写。

1 事务的四个特性

  • 原子性:事务是一个不可分割的工作单位,事务中的指令,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。

  • 一致性:数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性,事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定(保证操作前和操作后数据或者数据结构的一致性)。

    • 假设用户A和用户B两者的钱加起来一共是20000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是20000
    • 一致性是对数据可见性的约束,保证在一个事务中的多次操作的数据中间状态对其他事务不可见的。因为这些中间状态,是一个过渡状态,与事务的开始状态和事务的结束状态是不一致的。
      举例:张三给李四转账100元。事务要做的是从张三账户上减掉100元,李四账户上加上100元。一致性的含义是其他事务要么看到张三还没有给李四转账的状态,要么张三已经成功转账给李四的状态,而对于张三少了100元,李四还没加上100元这个中间状态是不可见的。
    • 事务的一致性和线程安全所面对的问题一模一样,要想维持一致性,需要保证两点:共享变量的可见性、临界区代码访问的顺序性。
  • 隔离性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。

    要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

  • 持久性:事务一旦提交,则持久化保存在数据库中,不会被回滚,对于数据的改变是永久性的。即使出现了任何事故比如断电等。

  • 一致性和原子性的区别:原子性和一致性的的侧重点不同:原子性关注状态,要么全部成功,要么全部失败,不存在部分成功的状态。而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见

2 并发事务会出现的问题

  • 在许多事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据时,会带来一些的问题。
2.1 脏读
  • 在一个事务处理过程里读取了另一个未提交的事务中的数据(这个数据有可能回滚)。
  • 例如:小明的银行卡余额里有100元。现在他打算点外卖,需要付款10元。但是这个时候,他的女朋友看中了一件衣服95元,她正在使用小明的银行卡付款。于是小明在付款的时候,程序后台读取到他的余额只有5块钱了,不够10元,所以系统拒绝了他的交易,告诉他余额不足。但是小明的女朋友最后因为密码错误,无法进行交易,余额还是100元。
2.2 幻读
  • 也叫虚读。一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,是因为另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象。
  • 举例:事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
2.3 不可重复读
  • 一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。
2.4 区别
  • 不可重复读和脏读的区别:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
  • 幻读和不可重复读都是读取了另一条已经提交的事务,所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

3 数据库事务的隔离级别

  • 事务的隔离级别有4种,由低到高分别为Read uncommitted(读未提交)、Read committed(读已提交) 、Repeatable read(可重复读)、Serializable(序列化)。事务隔离级别越低,并行度越好,一致性越低。
  • Oracle和SQLServer都是读已提交,但MySQL默认的隔离级别是可重复读。
3.1 RU 读未提交:
  • 一个事务可以读取另一个未提交事务的数据。

  • 会出现幻读,不可重复读,脏读。

  • 更新数据时加上行级共享锁,事物结束即释放。
    Mysql事务

    只加写锁,读不用申请锁,这样“读读”、“读写”、“写读”都可以并行,但“写写”还不能并行,于是所有的写都是串行,于是就有了脏读、不可重复读、幻读等等。

  • 举例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

3.2 RC 读已提交:
  • 一个事务要等另一个事务提交后才能读取数据。

  • 不会出现脏读。会出现幻读,不可重复读。

  • 写数据加行级排他锁,这样写过程是无法读取的,直到事务处理完毕才释放排他锁,给读的数据加行级共享锁,这样读的时候也是无法写的,但是一旦读完该行就释放共享锁(这样读完就允许其他事务修改,该事务中的下次读结果就会不同)。

  • MySQL会在SQL语句开始执行时创建一个视图
    Mysql事务

    读锁可以被升级为写锁,那么当对共享资源正在读时,可以被写请求升级为写锁,那么这样“读读”、“读写”可以并行,于是出现了幻读、不可重复读等等现象。

  • 举例:程序员拿着信用卡去享受生活(卡里只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!程序员的妻子把钱全部转出,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…

3.3 RR 可重复读:
  • 在开始读取数据(事务开启)时,不再允许修改操作。一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。

  • 不会出现不可重复读,脏读。会出现幻读。

  • 给写的数据加行级排他锁,事务结束释放,给读的数据加行级共享锁,事务结束后释放

  • MySQL会在事物开始时创建一个一致性视图(接下面的MVCC),事物结束时销毁
    Mysql事务

    读锁不能被升级为写锁,那么对共享资源的写,就进不来,这样“读读”是可并行的,这样会出现幻读,因为在这个级别,表是不会被看做是共享资源的,所以可以insert。

  • 举例:程序员拿着信用卡去享受生活,当他埋单时(事务开启,不允许其他事务的修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
    但是会出现幻读:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

3.4 S 序列化:
  • 最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

  • 事务读数据则加表级共享锁,事务写数据则加表级排他锁
    Mysql事务

  • 隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。

  • 设置数据库的隔离级别一定要是在开启事务之前。

4 Mysql中的事务

【参考文档】
https://www.cnblogs.com/Kevin-ZhangCG/p/9038371.html
https://www.cnblogs.com/william-dai/p/10903929.html
https://blog.nowcoder.net/n/477a70139a34447589b6f3186f04550b
https://blog.csdn.net/weixin_34255055/article/details/91540286