zoukankan      html  css  js  c++  java
  • 14.3.4 Phantom Rows 幻影行

    14.3.4 Phantom Rows   幻影行
    
    所谓的幻读为发生在一个事务 当相同的查询产生不同的结果集在不同的时间。
    
    比如,如果一个SELECT被执行2次, 但是第2次返回的记录不是第一次返回的记录,
    
    
    行是幻行
    
    
    假设在child 表的id列上有一个索引 ,你需要读取和锁定所有的行 值大于100的,
    
    
    以便更新选择行的一些列:
    
    
    SELECT * FROM child WHERE id > 100 FOR UPDATE;
    
    
    查询扫描索引从第一个记录id大于100开始, 让表包含记录90和102.
    
    如果锁设置在index records 在扫描的范围 不堵塞插入 在区间上(在这个例子里,区间是90和102)
    
    另外一个会话可以插入新值到表 id值为101.
    
    
    如果你执行相同的查询 在相同的session,你会看到新的值id=101(一个幻读)
    
    
    在查询返回的结果。 如果我们将一组行作为数据项, 新的幻读的child 会违背事务隔离原理,
    
    一个事务应该在事务期间所读取的数据不会改变。
    
    
    mysql> show variables like '%tx_isolation%';
    +---------------+-----------------+
    | Variable_name | Value           |
    +---------------+-----------------+
    | tx_isolation  | REPEATABLE-READ |
    +---------------+-----------------+
    1 row in set (0.01 sec)
    
    
     CREATE TABLE `child` (
      `sn` int(11) NOT NULL AUTO_INCREMENT,
      `id` int(11) DEFAULT NULL,
      `info` varchar(40) DEFAULT NULL,
      PRIMARY KEY (`sn`)
    );
    
    
    
    
    
    mysql> show variables like '%tx_isolation%';
    +---------------+----------------+
    | Variable_name | Value          |
    +---------------+----------------+
    | tx_isolation  | READ-COMMITTED |
    +---------------+----------------+
    1 row in set (0.00 sec)
    
    
    Session 1:
    mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
    Empty set (0.00 sec)
    
                                                              Session 2:
                                                              mysql> select * from child;
                                                              +----+------+------+
                                                              | sn | id   | info |
                                                              +----+------+------+
                                                              |  1 |    1 | a1   |
                                                              |  2 |   99 | a99  |
                                                              +----+------+------+
                                                              2 rows in set (0.00 sec)
    
                                                             mysql> insert into child(id,info) 
    
    values(101,'a101');
                                                            Query OK, 1 row affected (0.00 sec)
    
                                                        mysql> commit;
                                                       Query OK, 0 rows affected (0.01 sec)
    
    
    
    
    
    
    
    
    
    
    
    继续测试:
    
    Session 1:
    mysql>  show variables like '%tx_isolation%';
    +---------------+----------------+
    | Variable_name | Value          |
    +---------------+----------------+
    | tx_isolation  | READ-COMMITTED |
    +---------------+----------------+
    1 row in set (0.00 sec)
    
    mysql>  SELECT * FROM child;
    +----+------+------+
    | sn | id   | info |
    +----+------+------+
    |  1 |    1 | a1   |
    |  2 |   99 | a99  |
    |  3 |  101 | a101 |
    +----+------+------+
    3 rows in set (0.00 sec)
    
    mysql> insert into child(id,info) values(110,'a110');
    Query OK, 1 row affected (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> select * from child;
    +----+------+------+
    | sn | id   | info |
    +----+------+------+
    |  1 |    1 | a1   |
    |  2 |   99 | a99  |
    |  3 |  101 | a101 |
    |  4 |  110 | a110 |
    +----+------+------+
    4 rows in set (0.00 sec)
    
    mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
    +----+------+------+
    | sn | id   | info |
    +----+------+------+
    |  3 |  101 | a101 |
    |  4 |  110 | a110 |
    +----+------+------+
    2 rows in set (0.00 sec)
    
                                                                                Session 2:
    
                                                                            mysql> select * from 
    
    child;
    
    
                                                                          +----+------+------+
                                                                          | sn | id   | info |
                                                                          +----+------+------+
                                                                          |  1 |    1 | a1   |
                                                                          |  2 |   99 | a99  |
                                                                          |  3 |  101 | a101 |
                                                                          |  4 |  110 | a110 |
                                                                          +----+------+------+
                                                                         4 rows in set (0.00 sec)
    
                                                                        mysql> insert into child
    
    (id,info) values(105,'a105');
                                                                        Query OK, 1 row affected 
    
    (0.00 sec)
    
                                                                       mysql> commit;
                                                                       Query OK, 0 rows affected 
    
    (0.01 sec)
    
                                                                      mysql> select * from child;
                                                                      +----+------+------+
                                                                      | sn | id   | info |
                                                                      +----+------+------+
                                                                      |  1 |    1 | a1   |
                                                                      |  2 |   99 | a99  |
                                                                      |  3 |  101 | a101 |
                                                                      |  4 |  110 | a110 |
                                                                      |  5 |  105 | a105 |
                                                                      +----+------+------+
                                                                      5 rows in set (0.00 sec)
    
    
    
    为了防止幻读,InnoDB使用一个算法叫做 next-key locking   由 index-row locking和gap locking 组成。
    
    
    InnoDB 执行 row-level locking 以这种方式 当它搜索或者扫描表的索引,
    
    
    它设置共享锁或者排它锁 在它遇到的index records上.
    
    
    因此, row-level locks 实际上是 index-record locks.
    
    此外, a next-key lock  在一个Index record 也影响了 “gap” before that index record.
    
    
    也就是说,一个next-key lock  是一个index record lock 加上一个gap lock 
    
    在index record 之前的区间。
    
    
    如果一个会话有一个共享锁或者排它锁在记录R上 在一个index里,
    
    
    另外的会话不能立即插入到一个新的index record 在一个gap (在记录R之前)
    
    
    当InnoDB  扫描一个Index, 它也可以lock gap 在最后一条记录后面 在index里,
    
    比如之前的例子,防止任何插入到表 id值大于100的数据,
    
    
    InnoDB设置锁包含一个lock区间 在id=102以后的区间
    
    
    
    测试:
    Session 1:
    
    mysql> show variables like '%tx_isolation%';
    +---------------+-----------------+
    | Variable_name | Value           |
    S+---------------+-----------------+
    | tx_isolation  | REPEATABLE-READ |
    +---------------+-----------------+
    1 row in set (0.00 sec)
    
    
    
    
    Database changed
    mysql> select * from child;
    +----+------+------+
    | sn | id   | info |
    +----+------+------+
    |  1 |   90 | a90  |
    |  2 |  102 | a102 |
    +----+------+------+
    2 rows in set (0.00 sec)
    
    mysql> select * from child where id>100 for update;
    +----+------+------+
    | sn | id   | info |
    +----+------+------+
    |  2 |  102 | a102 |
    +----+------+------+
    1 row in set (0.00 sec)
    
    mysql> show index from child;
    +-------+------------+------------+--------------+-------------+-----------+-------------
    
    +----------+--------+------+------------+---------+---------------+
    | Table | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | 
    
    Sub_part | Packed | Null | Index_type | Comment | Index_comment |
    +-------+------------+------------+--------------+-------------+-----------+-------------
    
    +----------+--------+------+------------+---------+---------------+
    | child |          0 | PRIMARY    |            1 | sn          | A         |           4 |     
    
    NULL | NULL   |      | BTREE      |         |               |
    | child |          0 | child_idx1 |            1 | id          | A         |           4 |     
    
    NULL | NULL   | YES  | BTREE      |         |               |
    +-------+------------+------------+--------------+-------------+-----------+-------------
    
    +----------+--------+------+------------+---------+---------------+
    2 rows in set (0.00 sec)
    
    
    
    
    Session 2:
    mysql> insert into child(id,info) values(91,'a91');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(90,'a90');
    ERROR 1062 (23000): Duplicate entry '90' for key 'child_idx1'
    mysql> insert into child(id,info) values(89,'a89');
    Query OK, 1 row affected (0.00 sec)
    
    mysql> rollback;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> insert into child(id,info) values(88,'a88');
    Query OK, 1 row affected (0.00 sec)
    
    mysql> rollback;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into child(id,info) values(92,'a92');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(93,'a93');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(94,'a94');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(95,'a95');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(96,'a96');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(97,'a97');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(98,'a98');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(99,'a99');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(100,'a100');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(101,'a101');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(102,'a102');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(103,'a103');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> insert into child(id,info) values(104,'a104');
    
    
    x∈[3,4]表示3≤x≤4 因为两端有等号,所以叫闭区间
    x∈(3,4)表示3<x<4 因为两端没等号,所以叫开区间
    
    
    
    锁了(90,无穷)
    
    你可以使用 next-key locking 来实现一个唯一性检查 在你的应用里:
    
    
    如果你读取你的数据 在共享模式,没有看到重复的 对于一条记录准备被插入,
    
    然后你可以安全的插入你的记录,知道 next-key lock 设置在你的行的继承者 在读取阻止任何
    
    同时插入一个重复的记录。因此,the next-key locking 让你可以lock 你表中不存在的东西
    

  • 相关阅读:
    第一次个人编程作业
    第一次博客作业
    Put-Me-Down项目Postmortem
    Alpha总结
    git分支与版本管理、版本回退、冲突解决记录
    【Alpha】Daily Scrum Meeting第五次
    【Alpha】Daily Scrum Meeting第四次
    【Alpha】Daily Scrum Meeting第三次
    【Alpha】Daily Scrum Meeting第二次
    一、Daily Scrum Meeting【Alpha】------Clover
  • 原文地址:https://www.cnblogs.com/zhaoyangjian724/p/6199837.html
Copyright © 2011-2022 走看看