今天忽然被问到M有SQL的事务隔离级别有几个,今天复习下。
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERIALIZABLE
这个4个级别
什么是事务?
事务拿我们票务系统来说事:游客在购票 有2个操作,一个是生成一个订单信息、一个是生成票面信息 。生成订单信息后 ,可能系统问题或者票面值的问题(假设之前没有验证票的信息)。没有生产票面信息。这个就不对了,系统需要生成订单和票面信息,要么一起成功,要么同时失败。
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
了解了事务的大致作用之后呢,来了解下事务的特性。
事务的特性(ACID)
并发事务带来的问题
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
举个例子: 小明正在访问数据库给客户修改票面的信息【例如取票码不知为什么出现了重复,需要修改下】他进行了修改,。但是修改还没有提交到数据库中,这个时,小红也访问到这个客户的信息了【客户又打电话来投诉要取票码了】,因为这个时候小明还没有提交修改数据,这个时候的数据是还没有提交修改的数据,那么小红读取出来的就是”脏数据“,小红用这个数据做出的给游客的访问妥妥的会出问题的。
还是那个例子:小红提供数据给游客,游客发现还是不能用,而且和刚刚他原来的取票码是一样的,然后呢,游客就又投诉了,小红这个时候来了,她修改了状态为游客投诉,备注是取票码错了,游客特别生气,加急 处理。这个时候呢,小明也来了。因为他刚刚操作了订单,忘了修改状态和备注了。小明修改了状态为已经没有问题了,然后备注是修改完成。然后这个订单小红做出的有问题需要反馈的这个状态和备注就被覆盖了。这个就是丢失修改了。
还是票出问题了,这次游客还没去取票,他买好了票,他买好后获取了订单信息。刚好这时小明呢,下午循例打开了数据库来检查下,然后发现了重复的取票码的问题,然后他就修改了2个重复的取票码。游客这时呢,点击了分享发给了他的家人。刚好这个分享时需要读取后端的取票码数据分享的,2次事务读取的取票码不一样这个就是不可重复读了
这次这个游客生气了,投诉到景点的特别特别大的领导那里。然后领导要求这个运营室处理下这个事情,运营室的室长开会告诉了小红,然后告诉了开发室的室长这个事情,然后让开发室室长送他们一张小朋友的票。开发室的室长也开会了,让小明加急处理加一张小孩子的票。小红点开查看了下订单的信息发现是2个大人的票,然后同时小明保存了数据。小红刚刚不小心关了订单。再次打开,发现怎么是2个大人还多了一个小孩的票呢? 一定是最近加班太多了,出现幻觉了。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
小明修改了取票码,搞的游客2个取票码不一样的情况,这个就是不可重复读。数据的问题在修改
小明添加了一个儿童票搞的小红感觉自己出现幻觉,是不是自己刚刚看错了。明明刚刚是2人现在便3人了。
这个就是幻读。感觉自己出现幻觉了。
事务隔离级别
SQL 标准定义了四个隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过
- SELECT @@tx_isolation;
命令来查看,MySQL 8.0 该命令改为
- SELECT @@transaction_isolation;
- mysql> SELECT @@tx_isolation;
- +-----------------+
- | @@tx_isolation |
- +-----------------+
- | REPEATABLE-READ |
- +-----------------+
这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)**隔离级别。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
在下面我会使用 2 个命令行mysql ,模拟多线程(多事务)对同一份数据的脏读问题。
MySQL 命令行的默认配置中事务都是自动提交的,即执行SQL语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令:START TARNSACTION
。
我们可以通过下面的命令来设置隔离级别。
- SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
我们再来看一下我们在下面实际操作中使用到的一些并发控制语句:
脏读(读未提交)
避免脏读(读已提交)
不可重复读
还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题。
可重复读
防止幻读(可重复读)
一个事务对数据库进行操作,这种操作的范围是数据库的全部行,然后第二个事务也在对这个数据库操作,这种操作可以是插入一行记录或删除一行记录,那么第一个是事务就会觉得自己出现了幻觉,怎么还有没有处理的记录呢? 或者 怎么多处理了一行记录呢?
幻读和不可重复读有些相似之处 ,但是不可重复读的重点是修改,幻读的重点在于新增或者删除。