zoukankan      html  css  js  c++  java
  • mysql是如何解决幻读的

    幻读:
    幻读指的是一个事务在进行一次查询之后发现某个记录不存在,然后会根据这个结果进行下一步操作,此时如果另一个事务成功插入了该记录,那么对于第一个事务而言,其进行下一步操作(比如插入该记录)的时候很可能会报错。从事务使用的角度来看,在检查一条记录不存在之后,其进行插入应该完全没问题的,但是这里却抛出主键冲突的异常。
    简单来说:事务A的两次读之间有其他事务写操作,比如事务A统计年龄>30,当A两次读数据之间其他事务新添加了记录,所以事务A第二次读取到的数据突然多了一个,仿佛出现了幻觉一般,这就是一种幻读。
    串行化:

    • 事务在读操作时,先加表级别的共享锁,直到事务结束才释放;
    • 事务在写操作时,先加表级别的排它锁,直到事务结束才释放;

    串行化锁定了整张表,幻读不存在的!!!
    多版本并发MVCC:
    InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(systemversionnumber)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
    MVCC只在REPEATABLEREAD和READCOMMITTED两个隔离级别下工作。
    MVCC快照读需满足条件:

    1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
    2. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。

    MVCC解决了基于快照读下的幻读,事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。并不会读到其他事务的写操作!!!但是MVCC无法解决当前读下的幻读。在mvcc下,快照读select是不能读取别的事务,但是update修改是当前读操作,update之后的读操作时可以读取别的事务已经提交的数据。
    当前读:

    select * from tb where ? lock in share mode;
    select * from tb where ? for update;
    • forupdate:IX锁(意向排它锁),即在符合条件的rows上都加了排它锁
    • lockinsharemode:是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁
    • 排它锁:X锁、写锁,事务A对一个资源加了X锁后只有A本身能对该资源进行读和写操作,其他事务对该资源的读和写操作都将被阻塞,直到A释放锁为止
    • 共享锁:S锁、读锁,事务A锁定的数据其他事务可以共享读该资源,但不能写,直到事务A释放

    InnoDB默认开启间隙锁,innodb_locks_unsafe_for_binlog参数表示是否禁用GapLock间隙锁,默认值0,启动间隙锁
    测试时,设置参数为1禁用间隙锁,演示幻读场景

    SHOW VARIABLES LIKE '%innodb_locks_unsafe_for_binlog%'

    start transaction;
    select * from tb where id>100 for update;
    update tb set product_num=product_num-1 where id>100;

    由于Mysql Server会针对update和delete操作里面的where条件查找满足条件的记录,(查找的不是快照)然后Innodb引擎会返回的满足条件的加锁记录,当其他事务进行Insert 操作后,进行一次当前读时,就会读到其他事务 Insert 记录,可以明显的发现其会导致幻读。当前读不会读取其他事务(写操作)未提交的数据,且当前读会阻塞,一直等到其它写操作事务提交,才能读取数据,这样避免脏数据的发生。

    锁的机制:
    Next-Key Lock是Gap Lock(间隙锁)和Record Lock(行锁)的结合版,都属于Innodb的锁机制
    select * from tb where id>100 for update;

    • 主键索引 id 会给 id=100 的记录加上 record行锁
    • 索引 id 上会加上 gap 锁,锁住 id(100,+无穷大)这个范围

    其他事务对 id>100 范围的记录读和写操作都将被阻塞;
    插入 id=1000的记录时候会命中索引上加的锁会报出事务异常;
    Next-Key Lock会确定一段范围,然后对这个范围加锁,保证A在where的条件下读到的数据是一致的,因为在where这个范围其他事务根本插不了也删不了数据,都被Next-Key Lock锁堵在一边阻塞掉了。

    郭慕荣博客园
  • 相关阅读:
    js 能实现监听F5页面刷新子iframe 而父页面不刷新
    Thinkpad X201 Gobi2000 上电信3G网络
    【M30】代理类
    C++数组
    【M27】要求或者禁止对象产生于heap之中
    C++ delete operator做了什么事
    【M33】将非尾端类设计为抽象类
    【M32】在未来时态下发展程序
    【M34】如何在同一个程序中结合C++和C
    【M25】将构造方法和非成员方法虚化
  • 原文地址:https://www.cnblogs.com/jelly12345/p/14886559.html
Copyright © 2011-2022 走看看