MySQL锁与事务隔离级别(3)

e. 脏数据展现:在客户端A执行更新语句update account set balance=balance-50 where id=1,lilei的balance没有变成350,而是400,数据��一致,因为在这过程中,并不知道客户端B会话回滚了,行数据实际上是450,450-50=400,可以采用读已提交的隔离级别。

② 读已提交:

a. 打开一个客户端A,并设置当前事务隔离级别为read committed(读已提交),set tx_isolation='read-committed',查询account表的初始值:

b. 在客户端A的事务提交之前,打开另一个客户端B,并设置当前事务隔离级别为read committed,更新account表:

c. 这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读的问题:

d. 客户端B的事务提交:

e. 客户端A执行与上一步相同的查询,结果与上一步不一致,即产生了不可重复读的问题。

③ 可重复读

a. 打开一个客户端A,并设置当前事务隔离级别为repeatable read(可重复读),set tx_isolation='repeatable-read',查询account表的初始值:

b. 在客户端A的事务提交之前,打开另一个客户端B,并设置当前事务隔离级别为repeatable read,更新account表:

c. 在客户端A查询account表的所有记录,与步骤a的查询结果一致,没有出现不可重复读的问题。

d. 在客户端A执行update account set balance=balance-50 where id=1,balance没有变成350-50=300,lilei的balance值用的是步骤b中的300来算的,所以是250,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)

e. 重新打开客户端B,插入一条新数据后提交:

f. 在客户端A查询account表的所有记录,没有查出新增数据,所以没有出现幻读:

g. 验证幻读:在客户端A执行update account set balance=666 where id=4;能更新成功,再次查询能查到客户端B新增的数据:

④ 串行化

a. 打开一个客户端A,并设置当前事务隔离级别为serializable(串行化),set tx_isolation='serializable',查询account表的初始值:

b. 打开一个客户端B,并设置当前事务隔离级别为serializable,插入一条记录报错,表被锁了插入失败,MySQL中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,但这种隔离级别并发性能极低,开发中很少会用到

提问:MySQL默认级别是repeatable-read,有办法解决幻读问题吗?

间隙锁在某些情况下可以解决幻读问题。要避免幻读可以用间隙锁在session_1下面执行update account set where id>3 and id<=20,则其他session没法插入这个范围内的数据。

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁

无索引行锁升级为表锁:varchar如果不加 ' ',将导致系统自动转换类型,行锁变表锁,例如:update table set name=Zeki where id=1,这个语句会导致行锁变表锁,其他session无法对这个表做操作,5.7之后的版本这样写SQL会报错。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/6a834fd85f6c94639067fafbabd7ef61.html