MySQL 表鎖以及FLUSH TABLES操作
建立測試表t1, t2
use test;
CREATE TABLE `t1` (
`i` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `t2` (
`i` int(255) NOT NULL,
PRIMARY KEY (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
先來測試一下read鎖
SESSION 1
mysql> lock table t1 read;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t1 limit 1;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.02 sec)
mysql> insert into t1 values(104);
ERROR 1099 (HY000): Table 't1' was locked with a READ lock and can't be updated
mysql> update t1 set i=105 where i=104;
ERROR 1099 (HY000): Table 't1' was locked with a READ lock and can't be updated
可見,對於加了讀鎖的表,在執行加讀鎖的session中可讀取該表,但不能插入和更新,當然也不能進行刪除。
mysql> select * from t2 limit 1;
ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES
mysql> insert into t2 values(106);
ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES
mysql> update t2 set i=105 where i=104;
ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES
對於沒有加鎖的表,不能在執行加鎖的session中對錶進行訪問(包括增刪改查)
SESSION 2
mysql> select * from t1 limit 1;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.04 sec)
mysql> select * from t2 limit 1;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.01 sec)
mysql> insert into t1 values(105);
ERROR 1099 (HY000): Table 't1' was locked with a READ lock and can't be updated
可見,對於加了讀鎖的表,在不同的SESSION中不可以進行插入操作(更新和刪除同理)。而且可以對任意的表進行讀取。
再來測試一下write鎖(記得先在SESSION 1解鎖剛剛被加鎖的表)
SESSION 1
mysql> lock table t1 write;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t1 limit 1;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.03 sec)
mysql> insert into t1 values(1000000);
Query OK, 1 row affected (0.03 sec)
mysql> update t1 set i=10000001 where i=1000000;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> delete from t1 where i=10000001;
Query OK, 1 row affected (0.03 sec)
SESSION 2
mysql> select * from t1 limit 1;
可見,若SESSION中對錶加了寫鎖,則同一SESSION中對該表的增刪改查均可進行,但其他SESSION中的讀取和修改操作會被阻塞,直至表鎖被釋放。
接下來,來了解加鎖前表上有尚未執行完成的query會怎樣?
SESSION 1(記得先解鎖剛剛被加鎖的表)
mysql> select i,sleep(60) from t1 limit 1;
+---+-----------+
| i | sleep(60) |
+---+-----------+
| 1 | 0 |
+---+-----------+
1 row in set (1 min 0.03 sec)
SESSION 2
mysql> lock table t1 read;
Query OK, 0 rows affected (0.00 sec)
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql> lock table t1 write ;
Query OK, 0 rows affected (9.15 sec)
可見,表上有尚未完成的查詢操作時可以加讀鎖,但寫鎖會被阻塞。
SESSION 1
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t1 values(1000000);
Query OK, 1 row affected (13.33 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
SESSION 2
mysql> lock table t1 read;
Query OK, 0 rows affected (0.00 sec)
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql> lock table t1 write ;
可見,表上有完成但尚未提交的插入操作時可以立刻獲取讀鎖,但寫鎖會被阻塞。
接下來,來了解FLUSH TABLES時表上有尚未執行完成的查詢會怎樣?
SESSION 1
mysql> select i,sleep(60) from t1 limit 1;
SESSION 2
mysql> flush tables t1;
可見,由於將要被flush的表上有查詢尚未完成,因此flush tables 操作被阻塞,直至所有表上的操作完成,flush tables操作才得以完成
SESSION 3
mysql> select * from t1 limit 1;
由於flush tables t1被阻塞,導致後續其他session中的對於該表的查詢被阻塞
SESSION 4
mysql> select * from processlist where db='test';
+------+------+-----------+------+---------+------+-------------------------+------------------------------------+
| ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO |
+------+------+-----------+------+---------+------+-------------------------+------------------------------------+
| 8827 | root | localhost | test | Query | 31 | Waiting for table flush | select * from t1 limit 1 |
| 8825 | root | localhost | test | Query | 60 | User sleep | select i,sleep(60) from t1 limit 1 |
| 8826 | root | localhost | test | Query | 45 | Waiting for table flush | flush tables t1 |
+------+------+-----------+------+---------+------+-------------------------+------------------------------------+
另外的一個session中看到的執行緒狀態。直至ID為8825的執行緒執行完了SQL後續的flush tables動作才得以執行,繼而後續的select操作才順利完成。
可見執行flush tables操作或者隱含包含flush tables的操作時要小心謹慎。
mysql> select * from processlist where db='test';
+------+------+-----------+------+---------+------+-------+------+
| ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO |
+------+------+-----------+------+---------+------+-------+------+
| 8827 | root | localhost | test | Sleep | 245 | | NULL |
| 8825 | root | localhost | test | Sleep | 274 | | NULL |
| 8826 | root | localhost | test | Sleep | 259 | | NULL |
+------+------+-----------+------+---------+------+-------+------+