zoukankan      html  css  js  c++  java
  • mysql RC下不存在则插入

    mysql版本:5.7

    目的:在RC下,name列上仅有key索引,并发插入name时不出现重复数据

    RC不加gap lock,并且复合select语句是不加锁的快照读,导致两个事务同时进行都可插入,测试如下:

    client1:

    mysql> set tx_isolation='read-committed';
    
    mysql> select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | READ-COMMITTED |
    +----------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> create table t (id int primary key, name int, key(name))engine=innodb;
    Query OK, 0 rows affected (0.24 sec)
    
    .......
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    +----+------+
    7 rows in set (0.00 sec)
    
    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> show variables like 'autocommit';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit    | OFF   |
    +---------------+-------+
    1 row in set (0.03 sec)
    
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into t select 8,8 from dual where not exists (select name from t where name=8);
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    +----+------+
    8 rows in set (0.00 sec)

    client2设置同client1,设置略,然后起事务插入:

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into t select 9,8 from dual where not exists (select name from t where name=8);
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  9 |    8 |
    +----+------+
    8 rows in set (0.00 sec)

    可以看到并未阻塞,这不同于RR,在RR下会阻塞,因为加了gap lock。

    难道这时候没有加任何锁吗,其实并不是,client1执行如下,并查看锁:

    mysql> select name from t where name=8 lock in share mode;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
    mysql> select * from information_schema.innodb_locks;
    +-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    | lock_id         | lock_trx_id | lock_mode | lock_type | lock_table  | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    | 164163:469:4:10 | 164163      | S         | RECORD    | `test1`.`t` | name       |        469 |         4 |       10 | 8, 9      |
    | 164168:469:4:10 | 164168      | X         | RECORD    | `test1`.`t` | name       |        469 |         4 |       10 | 8, 9      |
    +-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)

    看看加锁的数据。client2如下:

    mysql> select name from t where name=8 lock in share mode;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
    mysql> select * from information_schema.innodb_locks;
    +----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    | lock_id        | lock_trx_id | lock_mode | lock_type | lock_table  | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    | 164168:469:4:9 | 164168      | S         | RECORD    | `test1`.`t` | name       |        469 |         4 |        9 | 8, 8      |
    | 164163:469:4:9 | 164163      | X         | RECORD    | `test1`.`t` | name       |        469 |         4 |        9 | 8, 8      |
    +----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)

    看看加锁的数据,可见client1和2都上了锁,是在insert时上的。

    那么为了能达到加锁阻塞的目的,可以使用如下方式,client1:

    mysql> insert into t select 8,8 from dual where not exists (select name from t where name=8 for update);
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0

    client2则阻塞:

    mysql> insert into t select 9,8 from dual where not exists (select name from t where name=8 for update);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
    mysql> select * from information_schema.innodb_locks;
    +-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    | lock_id         | lock_trx_id | lock_mode | lock_type | lock_table  | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    | 164170:469:4:10 | 164170      | X         | RECORD    | `test1`.`t` | name       |        469 |         4 |       10 | 8, 8      |
    | 164169:469:4:10 | 164169      | X         | RECORD    | `test1`.`t` | name       |        469 |         4 |       10 | 8, 8      |
    +-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)

    如果client1在client2阻塞时 commit:

    mysql> insert into t select 8,8 from dual where not exists (select name from t where name=8 for update);
    Query OK, 1 rows affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    mysql> commit;
    Query OK, 0 rows affected (0.04 sec)

    client2:

    mysql> insert into t select 9,8 from dual where not exists (select name from t where name=8 for update);
    Query OK, 0 rows affected (4.79 sec)
    Records: 0  Duplicates: 0  Warnings: 0

    还有一个需要注意的地方是,如果不加for update,则并发插入时,都会插入新数据,client1:

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    +----+------+
    8 rows in set (0.00 sec)
    
    mysql> insert into t select 9,9 from dual where not exists (select name from t where name=9);
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    |  9 |    9 |
    +----+------+
    9 rows in set (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    |  9 |    9 |
    | 10 |    9 |
    +----+------+
    10 rows in set (0.00 sec)

    与client1并发执行的client2:

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    +----+------+
    8 rows in set (0.00 sec)
    
    mysql> insert into t select 10,9 from dual where not exists (select name from t where name=9);
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    | 10 |    9 |
    +----+------+
    9 rows in set (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.04 sec)
    
    mysql> select * from t;
    +----+------+
    | id | name |
    +----+------+
    |  1 |    1 |
    |  2 |    2 |
    |  3 |    3 |
    |  4 |    4 |
    |  5 |    5 |
    |  6 |    6 |
    |  7 |    7 |
    |  8 |    8 |
    |  9 |    9 |
    | 10 |    9 |
    +----+------+
    10 rows in set (0.00 sec)

    可见,根本起不到不存在则插入的效果。

  • 相关阅读:
    虚方法和抽象方法
    c#_实现FTP方法(一) FtpWebRequest
    sql server 分页
    5ucms进阶
    图片处理函数
    [转]C++11新特性:Lambda函数
    [STL]for_each详细用法[转]
    [算法]hash table 与 hash map 实现
    [算法]字典树
    [STL]vector的使用[转]
  • 原文地址:https://www.cnblogs.com/drizzlewithwind/p/8577723.html
Copyright © 2011-2022 走看看