我们现场人员执行了类似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>