MySql事物隔离级别
1. 未提交读
未提交读(read uncommitte)是最低的隔离级别,其含义是允许一个事物读取另外一个事物没有提交的数据。未提交读是一种危险的隔离级别,所以一般在我们实际的开发中应用不广,但是它的优点在于并发能力高,适合哪些对数据一致性没有要求而追求高并发的场景,它的最大坏处是出现脏读。让我们看看可能发生的脏读场景,如表6-3所示。
表6-3 脏读现象
时刻 | 事物1 | 事物2 | 备注 |
T0 | ...... | ...... | 商品库存初始化为2 |
T1 | 读取库存为2 | ||
T2 | 扣减库存 | 库存未1 | |
T3 | 扣减库存 | 库存未0,读取事物1未提交的库存数量 | |
T4 | 提交事物 | 库存保存未0 | |
T5 | 回滚事物 | 因为第一类丢失更新已经克服,所以不会回滚未2,库存为0,结果错误 |
表6-3中的T3时刻,因为采用未提交读,所以事物2可以读取事物1未提交的库存数量为1,这里当它扣减库存后则数据未0,然后它提交了事物,库存就变未了0,未事物1在T5时刻回滚事物,因为第一类丢失更新已经被克服,所以它不会将库存回滚到2,那么最后的结果就变为了0,这样就出现了错误。
脏读一边是比较危险的隔离级别,在我们实际应用中采用得不多。为了克服脏读的问题,数据库隔离级别还提供了读写提交(read committed)的级别。
2. 读写提交
读写提交(read committed)隔离级别,是指一个事物只能读取另外一个事物已经提交的数据,不能读取未提交的数据。例如,表6-3的场景在限制为读写提交后,就变为表6-4描述的场景了。
表6-4 克服脏读
时刻 | 事物1 | 事物2 | 备注 |
T0 | ...... | ...... | 商品库存初始化为2 |
T1 | 读取库存为2 | ||
T2 | 扣减库存 | 库存为1 | |
T3 | 扣减库存 | 库存为1,读取不到事物1未提交的库存数据 | |
T4 | 提交事物 | 库存保存为1 | |
T5 | 回滚事物 | 因为第一类丢失更新已经克服,所以不会回滚未2,库存为1,结果正确 |
在T3时刻,由于采用了读写提交的隔离级别,因此事物2不能读取到事物1中未提交的库存1,所以扣减库存的结果依旧为1,然后它提交事物,则库存在T4时刻就变为了1。T5时刻,事物1回滚,因为第一类丢失更新已经克服,所以最后结果库存为1,这是一个正确的结果。但是读写提交也会产生下面的问题,如表6-5所描述的场景。
表6-5 不可重读场景
时刻 | 事物1 | 事物2 | 备注 |
T0 | ...... | ...... | 商品库存初始化为1 |
T1 | 读取库存为1 | ||
T2 | 扣减库存 | 事物未提价 | |
T3 | 读取库存为1 | 认为可扣减 | |
T4 | 提交事物 | 库存变为0 | |
T5 | 扣减库存 | 失败,因为此时库存为0,无法扣减 |
在T3时刻事物2读取库存的时候,因为事物1未提交事物,所以读出的库存为1,于是事物2认为当前可扣减库存;在T4时刻,事物1已经提交事物,所以在T5时刻,它扣减库存的时候就发现库存为0,于是就无法扣减库存了。这里的问题在于事物2之前认为可以扣减,而到扣减那一步却发现已经不可以扣减,于是库存对于事物2而言是一个可变化的指,这样的现象我们称为不可重复读,这就是读写提交的一个不足。为了克服这个不足,数据库的隔离级别还提出了可重复读的隔离级别,它能够消除不可重读的问题。
3. 可重复读
可重复读的目标是克服读写提交中出现的不可重复读的现象,因为在读写提交的时候,可能出现一些值的变化,影响当前事物的执行,如上述的库存是个变化的值,这个时候数据库提出了可重复读的隔离级别。这样就能够克服不可重复读的现象如表6-6所示。
表6-6 克服不可重读
时刻 | 事物1 | 事物2 | 备注 |
T0 | ...... | ...... | 商品库存初始化为1 |
T1 | 读取库存为1 | ||
T2 | 扣减库存 | 事物未提价 | |
T3 | 尝试读取库存 | 不允许读取,等待事物1提交 | |
T4 | 提交事物 | 库存变为0 | |
T5 | 读取库存 | 库存为0,无法扣除 |
可以看到,事物2在T3时刻尝试读取库存,但是此时这个库存已经被事物1实现读取,所以这个时候数据库就阻塞它的读取,直到事物1提交,事物2才能读取库存的值。此时已经是T5时刻,而读取到的值为0,这时就已经无法扣减了,显然在读写提交中出现的不可重复读的场景被消除了。但是这样也会引发新的问题的出现,这就是幻读。假设现在商品交易正在进行中,而后台有人也在进行查询分析和打印业务,让我们看看如表6-7所示可能发生的场景。
表6-7 幻读
时刻 | 事物1 | 事物2 | 备注 |
T0 | 读取库存50件 | 商品库存初始化为100,现在已经销售50笔 | |
T1 | 查询交易记录,50笔 | ||
T2 | 扣减库存 | 事物未提价 | |
T3 | 插入1笔交易记录 | 不允许读取,等待事物1提交 | |
T4 | 提交事物 | 库存49件,交易记录51笔 | |
T5 | 读取库存 | 这里与查询的不一致,在事物2看来有一笔是虚幻的,与之前查询的不一致 |
这便是幻读现象,可重复读和幻读,是比较难理解的内容,这里分析一下。首先这里的笔数不是数据库存储的值,而是一个统计值,商品库存则是数据库存储的值,这一点是需要注意的。也就是幻读不是针对一条数据库记录而言,而是多条记录,例如,这51笔交易笔数就是多条数据库记录统计出来的。而可重复读针对数据库的单一记录,例如,商品的库存是以数据库里面的一条记录存储的,它可以产生可重复读,而不能产生幻读。
4. 串行化
串行化(Serializable)是数据库最高的隔离级别,它会要求所有的SQL都会按照顺序执行,这样就可以克服上述隔离级别出现的各种问题,所以它能够完全保证数据的一致性。
posted on 2021-03-07 20:24 bigstrong_code 阅读(99) 评论(0) 编辑 收藏 举报