起因
有一天同事问我,晨哥晨哥,什么是数据库的隔离级别,我,额,稍等,冒汗,以前看过,现在忘了。
有时间写一篇,下次再忘了还能看看。主要是自己看,顺便满足一下自己写作的欲望,如果你不小心看到了,证明咱俩有缘分,别因为我写的不好喷我。
什么是事务
你以为第一件事是介绍数据库隔离级别的定义吗,
错,
我们要先了解为什么有数据库的隔离级别。
我们了解一件新知识的时候先要明白它是解决什么问题的,数据库隔离级别解决的是事务并发的问题。
那么问题就来了,什么是事务。
数据库事务的概念比较简单,不考试的情况下用口语表达就是几个事件要么一起生效,要么一起失效。
当然这个事件是数据库的事件,生活中的事儿不要硬往上套。😄,笑脸,手动滑稽。
不闹了,说正经的。正经脸。
并发事务的问题
事务并发会出现什么问题,这还不简单,两个覆盖问题,三个查询问题。
这就有意思了,什么叫两个覆盖,三个查询。
咱们以最经典的银行卡余额为例。 假设有一张表,里面就一个字段 money , 咱来看一下并发事务的问题。
money |
---|
1000 |
第一种覆盖,因为回滚覆盖
时间点 | 事务A | 事务B |
---|---|---|
T1 | 开启事务 | 开启事务 |
T2 | 读到 1000 | 读到 1000 |
T3 | 给账号 + 500 | |
T4 | 账号余额 1500 | |
T5 | 提交事务 | |
T6 | 给账号 -500 | |
T7 | 账号余额 500 | |
T8 | 回滚事务 | |
T9 | 账号变 1000 |
最后丢失了事务 B 提交的记录。
第二种覆盖,因为提交覆盖
时间点 | 事务A | 事务B |
---|---|---|
T1 | 开启事务 | 开启事务 |
T2 | 读到 1000 | 读到 1000 |
T3 | 给账号 + 500 | |
T4 | 账号余额 1500 | |
T5 | 提交事务 | |
T6 | 给账号 -500 | |
T7 | 账号余额 500 | |
T8 | 提交事务 | |
T9 | 账号变 500 |
这是因为提交覆盖了 B 提交的事务。
第一种查询,查询到未提交的数据
时间点 | 事务A | 事务B |
---|---|---|
T1 | 开启事务 | 开启事务 |
T2 | 读到 1000 | |
T3 | 给账号 + 500 | |
T4 | 账号余额 1500 | |
T5 | 查询到账户余额 1500(脏读) | |
T6 | 再给账户 +100 | 回滚事务 |
T7 | 账号余额 1600 | |
T8 | 提交事务 | |
T9 | 账号变 1600 |
因此,读到未提交的数据,也叫脏读。
第二种查询,查询到已提交的数据
时间点 | 事务A | 事务B |
---|---|---|
T1 | 开启事务 | 开启事务 |
T2 | 读到 1000 | 读到 1000 |
T3 | 给账号 + 500 | |
T4 | 账号余额 1500 | |
T5 | 提交事务 | |
T6 | 又读一次,变 1500 了 | |
T7 | 读两次效果不一样 |
这种读两次效果不一样的,也叫不可重复读。
第三种查询,查询到新插入的数据
时间点 | 事务A | 事务B |
---|---|---|
T1 | 开启事务 | 开启事务 |
T2 | sum() 一下值是 1000 | |
T3 | 插入一条新的 1000 的记录 | |
T4 | 提交事务 | |
T5 | 再 sum() 一下值变成 2000 了 |
这种奇幻的效果就叫做幻读。
数据库隔离级别
既然事务并发会有这么多的问题,那我们设计系统的时候总得有所取舍,到底哪些现象是和合理的,哪些现象是不合理的要避免的。
数据库设计也是一样,这些问题有些是不合理一定要避免的,有些呢,在业务场景中是合理的,不能一棍子打死。
那到底哪些场景下是合理的哪些是不合理的,数据库把它设计成了一个可选项,就叫数据库隔离级别,供开发人员选择。
serializable (串行,建议记英文,中文表达不好)
所有事务操作都是串行的,一个事务执行完才执行下一下,这样肯定不会用问题,当然效率也是最慢。
repeatable read (可重复读)
就是读不到别的事务的对记录的更新【包括已提交和未提交】,能读到已经提交的插入。InnoDB 存储引擎通过多版本并发控制,MVCC 解决了幻读的问题。
read committed (读已提交)
就是能读到别的事务对记录的更新,也能读到已插入的记录
read uncommited (读未提交)(也叫脏读)
可以看到其他事务未提交的更新,插入也能看到。