MySQL中lock tables和unlock tables淺析html
在MySQL中提供了鎖定表(lock tables)和解鎖表(unlock tables)的語法功能,ORACLE與SQL Server數據庫當中沒有這種語法。相信剛接觸MySQL的人,都想詳細、深刻的瞭解一下這個功能.下面就儘可能全面的解析、總結一下MySQL中lock tables與unlock tables的功能,若有不足或不正確的地方,歡迎指點一二。mysql
鎖定表的語法:sql
LOCK TABLES數據庫
tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}服務器
[, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...session
LOCAL修飾符表示能夠容許在其餘會話中對在當前會話中獲取了READ鎖的的表執行插入。可是當保持鎖時,若使用Server外的會話來操縱數據庫則不能使用READ LOCAL。另外,對於InnoDB表,READ LOCAL與READ相同。app
The LOCAL modifier enables nonconflicting INSERT statements (concurrent inserts) by other sessions to execute while the lock is held. (See Section 8.11.3, 「Concurrent Inserts」.) However, READ LOCAL cannot be used if you are going to manipulate the database using processes external to the server while you hold the lock. For InnoDB tables, READ LOCAL is the same as READ.ide
修飾符LOW_PRIORITY用於以前版本的MySQL,它會影響鎖定行爲,可是從MySQL 5.6.5之後,這個修飾符已經被棄用。若是使用它則會產生警告。測試
[LOW_PRIORITY] WRITE lock:ui
The session that holds the lock can read and write the table.
Only the session that holds the lock can access the table. No other session can access it until the lock is released.
Lock requests for the table by other sessions block while the WRITE lock is held.
The LOW_PRIORITY modifier has no effect. In previous versions of MySQL, it affected locking behavior, but this is no longer true. As of MySQL 5.6.5, it is deprecated and its use produces a warning. Use WRITE without LOW_PRIORITY instead.
解鎖表的語法:
UNLOCK TABLES
LOCK TABLES爲當前會話鎖定表。 UNLOCK TABLES釋放被當前會話持有的任何鎖。官方文檔「13.3.5 LOCK TABLES and UNLOCK TABLES Syntax」已經對LOCK TALES與UNLOCK TABLES作了很多介紹,下面咱們經過一些測試例子來深刻的理解一下鎖表與解鎖表的相關知識點。咱們先準備一下測試環境用的表和數據。
mysql> create table test( id int, name varchar(12));
Query OK, 0 rows affected (0.07 sec)
mysql> insert into test
-> select 10001, 'kerry' union all
-> select 10002, 'richard' union all
-> select 10003, 'jimmy' ;
Query OK, 3 rows affected (0.05 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql>
當前會話(會話ID爲61)持有test表的READ鎖後,那麼當前會話只能夠讀該表,而不能往表中寫入數據,不然就會報「Table 'test' was locked with a READ lock and can't be updated」這樣的錯誤。
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 61 |
+-----------------+
1 row in set (0.00 sec)
mysql> show open tables where in_use >=1;
Empty set (0.00 sec)
mysql> lock tables test read;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test | 1 | 0 |
+----------+-------+--------+-------------+
1 row in set (0.01 sec)
mysql> select * from test;
+-------+---------+
| id | name |
+-------+---------+
| 10001 | kerry |
| 10002 | richard |
| 10003 | jimmy |
+-------+---------+
3 rows in set (0.00 sec)
mysql> insert into test
-> values(10004, 'ken');
ERROR 1099 (HY000): Table 'test' was locked with a READ lock and can't be updated
mysql>
另外,咱們測試一下修飾符LOCAL的用途,以下所示:
mysql> create table test2( id int , name varchar(12)) engine=MyISAM;
Query OK, 0 rows affected (0.05 sec)
mysql> insert into test2
-> select 1001, 'test';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 66 |
+-----------------+
1 row in set (0.00 sec)
mysql> lock tables test2 read local;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test2;
+------+------+
| id | name |
+------+------+
| 1001 | test |
+------+------+
1 row in set (0.00 sec)
mysql> insert into test2
-> select 1002, 'kkk';
ERROR 1099 (HY000): Table 'test2' was locked with a READ lock and can't be updated
mysql>
在其它會話當中,你能夠看到表test2能夠被插入。固然前提是表的存儲引擎不能是InnoDB引擎,不然使用修飾符LOCAL和不用LOCAL是同樣的,其它會話沒法對錶寫入。
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 65 |
+-----------------+
1 row in set (0.00 sec)
mysql> select * from test2;
+------+------+
| id | name |
+------+------+
| 1001 | test |
+------+------+
1 row in set (0.00 sec)
mysql> insert into test2
-> select 1002, 'kkk';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
那麼其餘會話是否也能讀此表呢? 其它會話可否也能鎖定該表(LOCK TABLES READ)? 其它會話是否也能鎖定該表呢?(LOCK TABLES WRITE)
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 62 |
+-----------------+
1 row in set (0.01 sec)
mysql> select * from test;
+-------+---------+
| id | name |
+-------+---------+
| 10001 | kerry |
| 10002 | richard |
| 10003 | jimmy |
+-------+---------+
3 rows in set (0.00 sec)
mysql> lock tables test read;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test | 2 | 0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test | 1 | 0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)
mysql> lock tables test write;
如上測試所示,若是一個會話在一個表上得到一個READ鎖後,該會話和全部其餘會話只能從表中讀。不能往表中寫,其它會話也可在該表獲取一個READ鎖,此時你會在show open tables裏面看到in_use的值增長。其實LOCK TABLES READ是一個表鎖,並且是共享鎖。可是當一個會話獲取一個表上的READ鎖後,其它會話就不能獲取該表的WRITE鎖了,此時就會被阻塞,直到持有READ鎖的會話釋放READ鎖。
另外須要注意的是,當前會話若是鎖定了其中一個表,那麼是沒法查詢其它表的。不然會報「ERROR 1100 (HY000): Table 'worklog' was not locked with LOCK TABLES」錯誤。
那麼咱們再來看看WRITE鎖吧。測試前,先在上面兩個會話中執行 unlock tables命令。而後得到表TEST上的一個WRITE鎖,以下所示,當前會話能夠讀寫表TEST
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 61 |
+-----------------+
1 row in set (0.00 sec)
mysql> show open tables where in_use >=1;
Empty set (0.00 sec)
mysql> lock tables test write;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+-------+---------+
| id | name |
+-------+---------+
| 10001 | kerry |
| 10002 | richard |
| 10003 | jimmy |
+-------+---------+
3 rows in set (0.00 sec)
mysql> update test set name='ken' where id=10003;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql>
其它會話沒法讀寫表TEST,都會被阻塞,固然也沒法獲取表TEST的READ鎖或WRITE鎖。也就是說當一個會話得到一個表上的一個WRITE鎖後,那麼只有持鎖的會話READ或WRITE表,其餘會話都會被阻止。
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql>
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test | 1 | 0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)
mysql> select * from test;
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 63 |
+-----------------+
1 row in set (0.00 sec)
mysql> show processlist;
+----+------+-----------+------+---------+------+---------------------------------+--------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+---------------------------------+--------------------+
| 61 | root | localhost | MyDB | Sleep | 86 | | NULL |
| 62 | root | localhost | MyDB | Query | 40 | Waiting for table metadata lock | select * from test |
| 63 | root | localhost | MyDB | Query | 0 | init | show processlist |
| 64 | root | localhost | MyDB | Sleep | 2551 | | NULL |
+----+------+-----------+------+---------+------+---------------------------------+--------------------+
4 rows in set (0.00 sec)
UNLOCK TABLES釋放被當前會話持有的任何鎖,可是當會話發出另一個LOCK TABLES時,或當服務器的鏈接被關閉時,當前會話鎖定的全部表會隱式被解鎖。下面咱們也能夠測試看看
mysql> lock tables test read;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test | 1 | 0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)
mysql> lock tables worklog read;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+---------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+---------+--------+-------------+
| MyDB | worklog | 1 | 0 |
+----------+---------+--------+-------------+
1 row in set (0.00 sec)
mysql>
那麼咱們如何在當前會話鎖定多個表呢?以下所示:
mysql> show open tables where in_use >=1;
Empty set (0.00 sec)
mysql> lock tables test read, worklog read;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+---------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+---------+--------+-------------+
| MyDB | worklog | 1 | 0 |
| MyDB | test | 1 | 0 |
+----------+---------+--------+-------------+
2 rows in set (0.00 sec)
mysql>
另外,還有一些細節問題,LOCK TABLES是否能夠爲視圖、觸發器、臨時表加鎖呢?
mysql> create table test2( id int, sex bit);
Query OK, 0 rows affected (0.06 sec)
mysql> insert into test2
-> select 10001, 1 union all
-> select 10002, 0 union all
-> select 10003, 1;
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> create view v_test
-> as
-> select t1.id, t1.name, t2.sex
-> from test t1 left join test2 t2 on t1.id =t2.id;
Query OK, 0 rows affected (0.01 sec)
mysql> lock tables v_test read;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test2 | 1 | 0 |
| MyDB | test | 1 | 0 |
+----------+-------+--------+-------------+
2 rows in set (0.00 sec)
mysql>
如上測試所示,對於VIEW加鎖,LOCK TABLES語句會爲VIEW中使用的全部基表加鎖。對觸發器使用LOCK TABLE,那麼就會鎖定觸發器中所包含的所有表(any tables used in triggers are also locked implicitly)
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql> create temporary table tmp like test;
Query OK, 0 rows affected (0.04 sec)
mysql> show open tables where in_use >=1;
Empty set (0.00 sec)
mysql> select database();
+------------+
| database() |
+------------+
| MyDB |
+------------+
1 row in set (0.00 sec)
mysql> select * from tmp;
Empty set (0.00 sec)
mysql> insert into tmp
-> select 1001, 'kerry' ;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql>
LOCK TABLES 與 UNLOCK TABLES只能爲本身獲取鎖和釋放鎖,不能爲其餘會話獲取鎖,也不能釋放由其餘會話保持的鎖。一個對象獲取鎖,需具有該對象上的SELECT權限和LOCK TABLES權限。LOCK TABLES語句爲當前會話顯式的獲取表鎖。最後,關於LOCK TABLES與事務當中鎖有那些異同,能夠參考官方文檔13.3.5.1 Interaction of Table Locking and Transactions:
LOCK TABLES and UNLOCK TABLES interact with the use of transactions as follows:
· LOCK TABLES is not transaction-safe and implicitly commits any active transaction before attempting to lock the tables.
· UNLOCK TABLES implicitly commits any active transaction, but only if LOCK TABLES has been used to acquire table locks. For example, in the following set of statements,UNLOCK TABLES releases the global read lock but does not commit the transaction because no table locks are in effect: