我们现场人员执行了类似insert into t1 select * from t2;这样的语句,由于我们使用的是tokudb引擎,会对t2进行加锁,在这个语句执行的过程中实时入库数据无法进入t2。导致数据有丢失。

下面来说说这个语句。

对于insert into t1 select * from t2;这样的语句。不同的引擎锁的情况不一样。

这里讨论下对于t2表锁的问题。

(1)对于innodb引擎,不会对t2加锁。

(2)对于myisam引擎,会对t2进行加锁(在5.6.6之后,只对这个sql中t2所涉及的分区进行加锁)。

(3)对于tokudb引擎,会对t2进行加锁。


下面实验一下tokudb引擎关于(insert into ...select ...)锁问题

数据库版本:

mysql> select @@version;
+-------------------------+
| @@version               |
+-------------------------+
| 5.5.41-tokudb-7.5.5-log |
+-------------------------+
1 row in set (0.00 sec)

表结构:

mysql> show create table t1;
 CREATE TABLE `t1` (
  `id` int(11) NOT NULL DEFAULT '0',
  `name` varchar(30) DEFAULT NULL
) ENGINE=TokuDB DEFAULT CHARSET=utf8 


mysql> show create table t2;
 CREATE TABLE `t2` (
  `id` int(11) NOT NULL DEFAULT '0',
  `name` varchar(30) DEFAULT NULL
) ENGINE=TokuDB DEFAULT CHARSET=utf8


注意:这里都没有主键



会话1上执行如下:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1 select * from t2;
Query OK, 4 rows affected (0.09 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> 
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|             242 |
+-----------------+
1 row in set (0.00 sec)

说明:从上面看会话1的会话ID是242.



会话2上执行如下:

mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|             243 |
+-----------------+
1 row in set (0.00 sec)

mysql> 
mysql> insert into t2 select 5,'panda5';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

说明:从上面看会话2的会话ID是243. 会话2插入t2表由于拿不到锁插入超时。



会话3上执行(这个是在会话2执行插入操作且未超时时执行的):

mysql> select * from TokuDB_trx\G select * from TokuDB_locks\G select * from TokuDB_lock_waits\G
*************************** 1. row ***************************
             trx_id: 1956249
trx_mysql_thread_id: 242
*************************** 2. row ***************************
             trx_id: 1956265
trx_mysql_thread_id: 243
2 rows in set (0.00 sec)

*************************** 1. row ***************************
               locks_trx_id: 1956249
      locks_mysql_thread_id: 242
                locks_dname: $ydb_internal
             locks_key_left: 2e2f746573742f74312d6d61696e00
            locks_key_right: 2e2f746573742f74312d6d61696e00
         locks_table_schema: 
           locks_table_name: 
locks_table_dictionary_name: 
*************************** 2. row ***************************
               locks_trx_id: 1956249
      locks_mysql_thread_id: 242
                locks_dname: ./test/t2-main
             locks_key_left: -infinity
            locks_key_right: +infinity
         locks_table_schema: test
           locks_table_name: t2
locks_table_dictionary_name: main
*************************** 3. row ***************************
               locks_trx_id: 1956249
      locks_mysql_thread_id: 242
                locks_dname: ./test/t1-main
             locks_key_left: -infinity
            locks_key_right: +infinity
         locks_table_schema: test
           locks_table_name: t1
locks_table_dictionary_name: main
3 rows in set (0.00 sec)

*************************** 1. row ***************************
               requesting_trx_id: 1956265
                 blocking_trx_id: 1956249
                lock_waits_dname: ./test/t2-main
             lock_waits_key_left: 0005000000
            lock_waits_key_right: 0005000000
           lock_waits_start_time: 1472000929558
         lock_waits_table_schema: test
           lock_waits_table_name: t2
lock_waits_table_dictionary_name: main
1 row in set (0.00 sec)



说明:查询TokuDB_trx表可以看出,正在执行的会话有两个,242和243,分别对应的事物ID是1956249和1956265。

            查询TokuDB_locks表可以看出,会话242拿到了三个锁,锁的名称分别是$ydb_internal、./test/t2-main(对应t1表,并且锁locks_key_left为-infinity,locks_key_right为+infinity,即锁住了整个表)、./test/t1-main(对应t2表,locks_key_left为-infinity,locks_key_right为+infinity,锁住了整个表)。

            查询TokuDB_lock_waits表可以看出,事物1956249(对应的会话是242)阻塞了事物1956265(对应的会话是243)。


       从上面这个可以看出会话242拿到了t1表的锁./test/t2-main,并且locks_key_left: -infinity locks_key_right: +infinity。所以别的会话这是也无法对t1表进行插入操作。下面来实验下。

mysql> insert into t1 select 5,'panda5';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

说明: 在刚才的会话2中执行对t1的插入语句发现等待会超时。


说明:上面的测试两个表都没有主键,下面调整表结构,对t1和t2表都添加上主键再测试一次。

调整后的表结构:

mysql> show create table t1;
 CREATE TABLE `t1` (
  `id` int(11) NOT NULL DEFAULT '0',
  `name` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8 

mysql> show create table t2;
 CREATE TABLE `t2` (
  `id` int(11) NOT NULL DEFAULT '0',
  `name` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8



会话一:


mysql>  begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1 select * from t2;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select connection_id();  
+-----------------+
| connection_id() |
+-----------------+
|            6800 |
+-----------------+
1 row in set (0.01 sec)


会话二:

mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|            6801 |
+-----------------+
1 row in set (0.00 sec)

mysql> insert into t2 select 4,'panda4'; 
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>


会话三(这个是在会话2执行插入操作且未超时时执行的):

mysql> select * from TokuDB_trx\G select * from TokuDB_locks\G select * from TokuDB_lock_waits\G
*************************** 1. row ***************************
             trx_id: 1968082
trx_mysql_thread_id: 6800
*************************** 2. row ***************************
             trx_id: 1968106
trx_mysql_thread_id: 6801
2 rows in set (0.00 sec)


*************************** 1. row ***************************
               locks_trx_id: 1968082
      locks_mysql_thread_id: 6800
                locks_dname: ./test/t2-main
             locks_key_left: -infinity
            locks_key_right: +infinity
         locks_table_schema: test
           locks_table_name: t2
locks_table_dictionary_name: main
*************************** 2. row ***************************
               locks_trx_id: 1968082
      locks_mysql_thread_id: 6800
                locks_dname: ./test/t1-main
             locks_key_left: 0001000000
            locks_key_right: 0001000000
         locks_table_schema: test
           locks_table_name: t1
locks_table_dictionary_name: main
*************************** 3. row ***************************
               locks_trx_id: 1968082
      locks_mysql_thread_id: 6800
                locks_dname: ./test/t1-main
             locks_key_left: 0002000000
            locks_key_right: 0002000000
         locks_table_schema: test
           locks_table_name: t1
locks_table_dictionary_name: main
*************************** 4. row ***************************
               locks_trx_id: 1968082
      locks_mysql_thread_id: 6800
                locks_dname: ./test/t1-main
             locks_key_left: 0003000000
            locks_key_right: 0003000000
         locks_table_schema: test
           locks_table_name: t1
locks_table_dictionary_name: main
*************************** 5. row ***************************
               locks_trx_id: 1968082
      locks_mysql_thread_id: 6800
                locks_dname: ./test/t1-main
             locks_key_left: 0004000000
            locks_key_right: 0004000000
         locks_table_schema: test
           locks_table_name: t1
locks_table_dictionary_name: main
5 rows in set (0.00 sec)


*************************** 1. row ***************************
               requesting_trx_id: 1968106
                 blocking_trx_id: 1968082
                lock_waits_dname: ./test/t1-main
             lock_waits_key_left: 0004000000
            lock_waits_key_right: 0004000000
           lock_waits_start_time: 1473758672437
         lock_waits_table_schema: test
           lock_waits_table_name: t1
lock_waits_table_dictionary_name: main
1 row in set (0.01 sec)



说明:

查询TokuDB_trx表可以看出,正在执行的会话有两个,6800和6801,分别对应的事物ID是1968082和1968106。

            查询TokuDB_locks表可以看出,会话6800拿到了五个锁,分别是./test/t2-main(对应t2表,并且锁locks_key_left为-infinity,locks_key_right为+infinity,即锁住了整个表)、./test/t1-main四个锁(对应t1表,从locks_key_left和locks_key_right看出分别锁住了0001000000、0002000000、0003000000、0004000000)。

1968082(对应的会话是6800)阻塞了事物1968106(对应的会话是6801)。



   从上面这个可以看出会话6800拿到了t1表的锁./test/t2-main,并且从locks_key_left、locks_key_right看出只锁住了0001000000、0002000000、0003000000、0004000000。所以别的会话对t1表进行插入时,如果需要这几个锁则无法插入,如果不需要这几个锁,则可以插入。下面来实验下。

mysql> insert into t1 select 5,'panda5';
Query OK, 1 row affected (0.07 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> 
mysql> insert into t1 select 4,'panda4';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>