Mysql innoDB事务隔离性

Sunday, February 5, 2023 • 预计阅读时间 4 分钟

事务有哪些特性?

并不是所有的引擎都能支持事务,比如 MySQL 原生的 MyISAM 引擎就不支持事务 事务看起来感觉简单,但是要实现事务必须要遵守 4 个特性,分别如下:

  • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,如果事务在执行过程中发生错误,会被回滚到事务开始前的状态
  • 一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,不同隔离级别事务之间的相关干扰的程度也是不一样的
  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?

  • 持久性是通过 redo log (重做日志)来保证的,重做日志保证事务提交后的正确执行,一般用于掉电等故障恢复
  • 原子性是通过 undo log(回滚日志) 来保证的,事务如果没有正常提交,则进行回滚
  • 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
  • 一致性则是通过持久性+原子性+隔离性来保证;

并行事务会引发什么问题?

脏读

如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象。

不可重复读

在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象。

幻读

在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象

在同一事务中,多次查询的读必须是同种类型的读,不同类型的读的用法是不正确的也是无法避免上述问题的。 如:同一个事务中,如果不对数据做修改,用select时则应该用普通select;反之,如果要对相关数据修改且要用select,则应该用当前读

读的类型

快照读

  • 不加锁的select操作(注:事务级别不是串行化)

当前读

  • select lock in share mode (共享锁)
  • select for update (排他锁)
  • update (排他锁)
  • insert (排他锁)
  • delete (排他锁)
  • 串行化事务隔离级别

SELECT … LOCK IN SHARE MODE的应用场景: 适合于两张表存在关系时的写操作,拿mysql官方文档的例子来说,一个表是child表,一个是parent表,假设child表的某一列child_id映射到parent表的c_child_id列,那么从业务角度讲,此时我直接insert一条child_id=100记录到child表是存在风险的,因为刚insert的时候可能在parent表里删除了这条c_child_id=100的记录,那么业务数据就存在不一致的风险。正确的方法是再插入时执行select * from parent where c_child_id=100 lock in share mode,锁定了parent表的这条记录,然后执行insert into child(child_id) values (100)就ok了。 原文链接:https://blog.csdn.net/cug_jiang126com/article/details/50544728

不同隔离级别如何解决问题

隔离级别 存在问题 解决方式
读未提交 脏读、不可重读、幻读
读提交 不可重读、幻读 MVCC(快照读)+ 读写锁(当前读)
可重复读 MVCC(快照读)+ next-key lock(当前读)
串行化 读写锁(快照读+当前读)

读提交

  • 快照读:在该隔离级别下,两个事务的快照读在MVCC的作用下,根据事务的版本号,不会读到其他事务未提交的数据,解决了脏读(两个事务间的读写冲突),读相同数据时,也不会阻塞(两个事务间的读写冲突读读冲突)
  • 当前读:在该隔离级别下,两个事务的当前读需要对加对应的读写锁,在修改数据时,没有锁的事务会被阻塞(写写冲突)。但是会出现幻读,因为锁只锁住了存在的数据,如果另外一个事务添加了数据并提交了事务,再次当前读会读到刚刚添加的数据

可重复读

  • 快照读:在该隔离级别下,两个事务的快照读在MVCC的作用下,根据事务的版本号,不会读到其他事务未提交的数据,解决了脏读(两个事务间的读写冲突),读相同数据时,也不会阻塞(两个事务间的读写冲突读读冲突)
  • 当前读:在该隔离级别下,两个事务的当前读需要对加对应的临间锁(next-key lock),在修改数据时,没有锁的事务会被阻塞(写写冲突)。当前读不会再出现幻读,因为临间锁不仅锁住了存在的数据,还锁住了数据的区间,如果其他事务添加数据,因为当前区间被锁住了,所以会被阻塞住

参考: https://juejin.cn/post/7134186501306318856 https://xiaolincoding.com/mysql/transaction/mvcc.html#%E8%AF%BB%E6%8F%90%E4%BA%A4%E6%98%AF%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C%E7%9A%84

数据库mysql事务

《Kubernetes in Action》读书笔记第一章:Kubernetes介绍

初探内存泄露

comments powered by Disqus