mysql中lock tables與unlock tables


官網:https://dev.mysql.com/doc/refman/5.0/en/lock-tables.html

LOCK TABLES
    tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...

lock_type:
    READ [LOCAL]
  | [LOW_PRIORITY] WRITE

UNLOCK TABLES

MySQL enables client sessions to acquire table locks explicitly for the purpose of cooperating with other sessions for access to tables, or to prevent other sessions from modifying tables during periods when a session requires exclusive access to them. A session can acquire or release locks only for itself. One session cannot acquire locks for another session or release locks held by another session.

Locks may be used to emulate transactions or to get more speed when updating tables. This is explained in more detail later in this section.

LOCK TABLES explicitly acquires table locks for the current client session. Table locks can be acquired for base tables or (as of MySQL 5.0.6) views. You must have the LOCK TABLES privilege, and the SELECT privilege for each object to be locked.

。。。

lock tables 命令是為當前線程鎖定表.這里有2種類型的鎖定,一種是讀鎖定,用命令 lock tables tablename read;另外一種是寫鎖定,用命令lock tables tablename write.下邊分別介紹:
1. lock table 讀鎖定
如果一個線程獲得在一個表上的read鎖,那么該線程和所有其他線程只能從表中讀數據,不能進行任何寫操作。
下邊我們測試下,測試表為user表。
不同的線程,可以通過開多個命令行MySQL客戶端來實現:

時刻點
 線程A(命令行窗口A)
 線程B(命令行窗口B)
 1
 mysql> lock tables user read;
Query OK, 0 rows affected (0.00 sec)
mysql>
對user表加讀鎖定。
  
 
2
 mysql> select * from user;
+------+-----------+
| id   | name      |
+------+-----------+
|   22 | abc       |
|  223 | dabc      |
| 2232 | dddabc    |
|   45 | asdsagd   |
|   23 | ddddddddd |
+------+-----------+
5 rows in set (0.00 sec)
mysql>
自己的讀操作未被阻塞
 mysql> select * from user;
+------+-----------+
| id   | name      |
+------+-----------+
|   22 | abc       |
|  223 | dabc      |
| 2232 | dddabc    |
|   45 | asdsagd   |
|   23 | ddddddddd |
+------+-----------+
5 rows in set (0.00 sec)
mysql>
其他線程的讀也未被阻塞
 
3
 mysql> insert into user values(12,'test');
ERROR 1099 (HY000): Table 'user' was locked with a READ lock and can't be updated
mysql>
發現本線程的寫操作被阻塞
 其他線程:mysql> insert into user values(22,'2test');
發現沒有任何反應,一直等待中,說明沒有得到寫鎖定,一直處於等待中
 
4
 mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql>
釋放讀鎖定。
 mysql> insert into user values(22,'ddd');
Query OK, 1 row affected (1 min 27.25 sec)
mysql>
在線程A釋放讀鎖后,線程B獲得了資源,剛才等待的寫操作執行了。
 
5
 mysql> lock tables user read local;
Query OK, 0 rows affected (0.00 sec)
mysql>
獲得讀鎖定的時候增加local選項。
 mysql> insert into user values(2,'b');
Query OK, 1 row affected (0.00 sec)
mysql>
發現其他線程的insert未被阻塞。(我測試還是阻塞,不知道為什么)
 
6
  
 mysql> update user set name  = 'aaaaaaaaaaaaaaaaaaaaa' where id = 1;
但是其他線程的update操作被阻塞了。
 

注意:user表必須為Myisam表,以上測試才能全部OK,如果user表為innodb表,則lock tables user read local命令可能沒有效果,也就是說,如果user表為innodb表,第6時刻將不會被阻塞,這是因為INNODB表是事務型的,對於事務表,例如InnoDB和BDB,--single-transaction是一個更好的選項,因為它不根本需要鎖定表。
 
 2. lock table 寫鎖定
如果一個線程在一個表上得到一個   WRITE   鎖,那么只有擁有這個鎖的線程可以從表中讀取和寫表。其它的線程被阻塞
寫鎖定的命令:lock tables user write.user表為Myisam類型的表。
參考如下測試:
 
時刻點
 線程A(命令行窗口A)
 線程B(命令行窗口B)
 
 1
 mysql> lock tables user write;
Query OK, 0 rows affected (0.00 sec)
對user表加寫鎖定。
  
 
2
 mysql> select * from user;
+----+-----------------------+
| id | name                  |
+----+-----------------------+
|  1 | aaaaaaaaaaaaaaaaaaaaa |
|  2 | b                     |
+----+-----------------------+
2 rows in set (0.00 sec)
自己可以繼續進行讀操作
 mysql> select * from user;
其他線程讀操作被阻塞。
 
3
 mysql> unlock tables ;
Query OK, 0 rows affected (0.00 sec)
釋放鎖定。
  
4
  mysql> select * from user;
+----+-----------------------+
| id | name                  |
+----+-----------------------+
|  1 | aaaaaaaaaaaaaaaaaaaaa |
|  2 | b                     |
+----+-----------------------+
2 rows in set (32.56 sec)
其他線程獲得資源,可以讀數據了。
 
 
以上所有結果均在MySQL 5.4.3下測試通過。
 
在MySQL中如何用命令查看表是MyISAM類型還是INNODB類型的?

show create table ***

ENGINE=MyISAM AUTO_INCREMENT=14696 DEFAULT CHARSET=utf8 |

 

 -----------------------------

一篇文章:

mysql 的 表鎖 lock tables 感覺就像一個 封閉的空間

mysql發現 lock tables 命令的時候,會將帶有鎖標記的表(table) 帶入封閉空間,直到 出現 unlock tables 命令 或 線程結束, 才關閉封閉空間。

進入封閉空間時 , 僅僅只有鎖標記的表(table) 可以在里面使用,其他表無法使用。


鎖標記 分為 read 和 write 下面是 兩種 鎖的區別

--------------------------------------------------------------------


//如 將 table1 設為read鎖, table2 設為write鎖, table3 設為read鎖

lock tables [table1] read,[table2] write,[table3] read;
----------------------------------------------------------------------


//執行到這里時,進入封閉空間。

1. table1 僅允許[所有人]讀,[空間外]如需寫、更新要等待[空間退出],[空間內]如需寫、更新會引發mysql報錯。
2. table2 僅允許[空間內]讀寫更新,[空間外]如需寫、更新要等待[空間退出]。
3. table3 僅允許[所有人]讀,[空間外]如需寫、更新要等待[空間退出],[空間內]如需寫、更新會引發mysql報錯。


----------------------------------------------------------------------
//執行到這里時,退出封閉空間,釋放所有表鎖

unlock tables
----------------------------------------------------------------------

當前線程關閉時,自動退出封閉空間,釋放所有表鎖,無論有沒有執行 unlock tables

上面一堆東西感覺很亂,下面我們看個實例吧。

在某個地方看到有個例子,具體描述類似如下:商店現在某商品只有1件庫存,然后A與B在網上進行下訂,A與B幾乎同時(或許也就差幾毫秒,A比B快那么一點點)進行。

很明顯是只有A才能成功下單的,B則會收到庫存不足的提示,但是作為放置在服務端的那個頁面(或者稱為腳本程序)我們得怎樣去處理這個問題呢?或者我先放出一段代碼吧。

 $sql = "select number from goods where id=1";
    $number = intval( $db->result( $db->query( $sql ), 0 ) );
    if ( $number > 0 ) {
    sleep( 2 );
    $sql = "update goods set number=number-1 where id = 1";
    if ( $db->query( $sql ) ) {
    echo 'Ok!Here you are!';
    } else {
    echo 'Sorry!Something go wrong!Try it again.';
    }
    } else {
    echo 'No more!you are so late!';
    }

這部分代碼除了缺少一定注釋外都寫得沒錯,當然$db是一個操作數據庫的類,我只是將大部分方法封裝了,這里的邏輯也是很明顯了。

先獲取id為1這個東東的庫存數,看看是否為0,如果為0就訂購不成功了,如果大於0則將庫存減1然后提示ok。這確實沒有任何錯誤,邏輯也對。如果請求是一個接一個地產生的,那么什么問題都沒有,但當一些並發情況(paperen也不想用這種專業的名詞,其實就是上面那個例子的情況,在相差不明顯的時間內有多個請求產生)出現時就可能出現一些無厘頭的問題了。你想啊,是不是可能存在一種情況,A剛發出請求,腳本處理到update之前B又發出請求,那么現在庫存依然還有1,因為A的update還沒有執行呢,所以$number不少於0,這次完了,B也下單了,於是庫存變成-1了(假設原來只有1件),確實是一個荒謬而且比較搞笑的結果。

出現問題的原因很明顯,就是忽略了這種並發情況的考慮,處理下訂應該是種隊列方式,也就是先來先得,就是說在執行這個下訂動作是要排隊的,前面的那個先下訂然后后者才能下訂,當然當后者下訂前才再判斷庫存的數量。那么怎樣解決這個問題呢,在程序層面上貌似真的沒有方法去解決這個問題(paperen可沒想到代碼上的解決方案,有思路的可以留個言),所以在此才提到鎖表的概念,你想啊,上面出現這個問題的歸根於沒有控制一個select number的先后順序(或者可以這么說吧),因為在A執行update之前你又允許B去查詢庫存,當然結果還是1,至少要等待A更新庫存后才允許其他人的任何操作,也就是對goods表進行一個排隊操作,對goods表進行鎖定。

說到這里,請不要以為鎖表有多么高深,其實它就是一條sql

    LOCK TABLE `table` [READ|WRITE]

解鎖

    UNLOCK TABLES;

引用專業的描述是

LOCK TABLES為當前線程鎖定表。 UNLOCK TABLES釋放被當前線程持有的任何鎖。當線程發出另外一個LOCK TABLES時,或當服務器的連接被關閉時,當前線程鎖定的所有表會自動被解鎖。 

如果一個線程獲得在一個表上的一個READ鎖,該線程和所有其他線程只能從表中讀。 如果一個線程獲得一個表上的一個WRITE鎖,那么只有持鎖的線程READ或WRITE表,其他線程被阻止。

已經是有種隊列的味道,對不,所以解決方案很簡單嘛,在select前加鎖,執行完后面邏輯代碼后解鎖。或許有沒有人會有一個疑問,就是如果萬一鎖表后線程就斷掉了那么是不是就一直鎖表了,這個確實是可能存在但是既然你想到了那么數據庫的設計人員也一定考慮到了,可以告訴你關於unlock的一些資料:當線程發出另一個 LOCK TABLES,或當與服務器的連接被關閉時,被當前線程鎖定的所有表將被自動地解鎖。這下放心了吧。

好,看下改進后的代碼。

$db->lock( 'goods', 2 );
    $sql = "select number from goods where id=1";
    $number = intval( $db->result( $db->query( $sql ), 0 ) );
    if ( $number > 0 ) {
    sleep( 2 );
    $sql = "update goods set number=number-1 where id = 1";
    if ( $db->query( $sql ) ) {
    echo 'Ok!Here you are!';
    } else {
    echo 'Sorry!Something go wrong!Try it again.';
    }
    } else {
    echo 'No more!you are so late!';
    }
    $db->unlock();

只加了兩行代碼,不過也不能這么說,因為paperen我修改了自己那個操作數據庫的類,加了兩個方法lock與unlock,其實這兩個方法也很簡單。

  /**
    * 鎖表
    * @param string $table 表名
    * @param int $type 讀鎖1還是寫鎖2
    */
    public function lock( $table, $type = 1 ) {
    $type = ( $type == 1 ) ? 'READ' : 'WRITE';
    $this->query( "LOCK TABLE `$table` $type" );
    }
     
    /**
    * 解鎖
    */
    public function unlock() {
    $this->query( "UNLOCK TABLES" );
    }

關於lock自己可以再斟酌一下,因為第二個參數這樣弄看上去並不太舒服。嗯哼~那怎測試呢?paperen使用jmeter進行測試結果

關於jmeter可以在http://jakarta.apache.org/site/downloads/downloads_jmeter.cgi 這里下載,在邪惡的人手中可以是一個恐怖的工具在善良的人手中是一個友好的工具。

您需要創建兩個線程,其實就是對服務器發出兩個請求

具體配置paperen在此不說,我導出了一個計划文件,大家可以試着打開就能看到paperen是怎測試的了。http://iamlze.cn/demo/locktable/locktable.jmx

保存下來然后導入必需調整一下你本地測試的路徑,最后ctrl+R(運行),在線程下查看結果樹就有請求的回應信息了。

首先測試不加鎖表的情況(就是一開始不加lock與unlock操作的代碼)看看兩個線程出來的結果。

都是ok~~再看數據庫

然后將number改回1,再將lock與unlock,鎖表操作加上,再運行

好吧,數據表就不用看了吧,結果已經很明顯了,再前一個請求對表操作完成之前,之后那些請求都要在等待,直到前面請求完成了才能操作,也就是隊列的味道。

老實說mysql的事務也需要下點功夫研究一下,paperen關於鎖表的了解也就是在查看事務的過程中產生的,在高級的應用過程中這種技術就更加重要,更加嚴謹的邏輯代碼與嚴謹的數據庫管理才能更進一步保證數據的真實與准確性。真是后知后覺。

參考了:http://www.111cn.net/database/mysql/55482.htm

 更多:http://blog.itpub.net/15480802/viewspace-755980/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM