1. 程式人生 > >MySQL 表鎖以及FLUSH TABLES操作

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 |
+------+------+-----------+------+---------+------+-------+------+