1. 程式人生 > >mysql lock table/unlock table 表鎖/解鎖

mysql lock table/unlock table 表鎖/解鎖

在某個地方看到有個例子,具體描述類似如下:商店現在某商品只有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。這確實沒有任何錯誤,邏輯也對。如果請求是一個接一個地產生的,那麼什麼問題都沒有,但當一些併發情況出現時就可能出現一些無厘頭的問題了。你想啊,是不是可能存在一種情況,A剛發出請求,指令碼處理到update之前B又發出請求,那麼現在庫存依然還有1,因為A的update還沒有執行呢,所以$number不少於0,這次完了,B也下單了,於是庫存變成-1了(假設原來只有1件),確實是一個荒謬而且比較搞笑的結果。

出現問題的原因很明顯,就是忽略了這種併發情況的考慮,處理下訂應該是種佇列方式,也就是先來先得,就是說在執行這個下訂動作是要排隊的,前面的那個先下訂然後後者才能下訂,當然當後者下訂前才再判斷庫存的數量。那麼怎樣解決這個問題呢,在程式層面上貌似真的沒有方法去解決這個問題,所以在此才提到鎖表的概念,上面出現這個問題的歸根於沒有控制一個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" );
    }

相關推薦

mysql lock table/unlock table /

在某個地方看到有個例子,具體描述類似如下:商店現在某商品只有1件庫存,然後A與B在網上進行下訂,A與B幾乎同時(或許也就差幾毫秒,A比B快那麼一點點)進行。 很明顯是隻有A才能成功下單的,B則會收到庫存不足的提示,但是作為放置在服務端的那個頁面(或者稱為指令碼程式)我們

mysql -

mysql 解鎖表遇到問題“”用工具navicat打開一張表的時候,有的時候會發現這張表怎麽打不開,關了navicat工具,再打開,也是同樣的狀態。查看表鎖:show OPEN TABLES where In_use > 0;查看是否是表鎖住了。-- 查看進程號 show processlist; 這樣

mysql 查看

var KS 進程 tab obj har div select arch select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName from s

Oracle查詢被方法

v$lock req 查找 rom 方法 session lock acl oracle 1.用下列sql語句查詢操作不當引起鎖表的進程的SESSION_ID及USERNAME SELECT S.SID SESSION_ID, S.USERNAME, DECODE(LMOD

for update造成的Oracle

執行 lec 如果 pl/sql 查看 數據 system oracle log 我遇到的情況: 當使用select語句查詢表時,後面跟著for update , select * from table for update 當修改表中數據,但是沒有com

Oracle系列-解決方案(操刀版)

ria 目錄 time terminal serial 終極 logo add 系統 【Oracle系列-鎖表與解鎖解決方案(大招版)】 --1查看被鎖的表 select b.owner,b.object_name,a.session_id,a.locked_mode fr

Oracle系列-解決方案(基礎版)

使用 因此 logo 才會 解決方案 date操作 order rest table 【Oracle鎖表查詢和解鎖解決方案】 一、了解原因(借鑒整理) 數據庫操作語句的分類 DDL:數據庫模式定義語言,關鍵字:createDML:數據操縱語言,關鍵字:Insert、del

pl/sql查看

system sel alter oracl acl locked objects _id obj ① 查看用戶鎖表 select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_

程序的原子性,python實現原子性,上threading.Lock()

執行 結束 錯誤 多個 加鎖 lease mut 同時 帳戶 A想要從自己的帳戶中轉1000塊錢到B的帳戶裏。那個從A開始轉帳,到轉帳結束的這一個過程,稱之為一個事務。在這個事務裏,要做如下操作:1. 從A的帳戶中減去1000塊錢。如果A的帳戶原來有3000塊錢,現在就變成

oracle 鎖定

evo sel from sid tail revoke 相關 art 執行命令 Oracle數據庫操作中,我們有時會用到鎖表查詢以及解鎖和kill進程等操作,那麽這些操作是怎麽實現的呢?本文我們主要就介紹一下這部分內容。(1)鎖表查詢的代碼有以下的形式:select co

RC與RR隔離級別下MySQL不同的加方式

RC與RR隔離級別下MySQL不同的加鎖解鎖方式 RC隔離級別 RR隔離級別 半一致讀semi-consistent read 半一致讀發生條件 innodb_locks_unsafe_for_binlog 一開始的例子 R

Oracle查詢被鎖定以及

1、查詢被鎖資訊 SELECT object_name, machine, s.sid, s.serial# FROM gv$locked_object l, dba_objects o, gv$session s WHERE l.object_id = o.obj

查看Sql Server被以及

代碼 pan 數據庫名 數據庫 被鎖 varchar weight _id col 查看被鎖表: select spId from master..SysProcesses where db_Name(dbID) = ‘數據庫名稱‘ and spId <

oracle for update造成的Oracle

我遇到的情況: 當使用select語句查詢表時,後面跟著for update , select * from table for update 當修改表中資料,但是沒有commit就關掉PL/SQL,下次再開啟,執行帶for update的sql語句,就會卡死  

Oracle會話 查詢和關閉會話 殺會話 殺程序

  Oracle會話查詢等 2016年05月11日 10:27:00 波特王子 閱讀數:4432 如何檢視oracle當前連線數,會話數 收藏 檢視session: select * from v$session where username is

Oracle資料庫方法

1、下面的語句用來查詢哪些物件被鎖: select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects b where b.object_id = a.obje

Oracle

    之前在專案中做同步任務的時候,需要從一個介面中拿到資料,插入到資料庫中,使用的是Merge Into;但是在執行單元測試的時候,IDEA一直在轉圈,不結束;最後問了老大,老大說是鎖表的問題;這才解決;同時也百度了別人是怎麼寫的,在這裡整理一下;     對錶的操

[LeetCode] Android Unlock Patterns 安卓模式

Given an Android 3x3 key lock screen and two integers m and n, where 1 ≤ m ≤ n ≤ 9, count the total number of unlock patterns of the Android lock screen,

oracle 、資料恢復

SELECT /*+ rule */ s.username, decode(l.type,'TM','TABLE LOCK', 'TX','ROW LOCK', NULL) LOCK_LEVEL, o.owner,o.object_name,o.object_type, s.sid,s.serial#,s

Oracle的 、Oracle使用者

SELECT /*+ rule */ s.username, decode(l.type,'TM','TABLE LOCK', 'TX','ROW LOCK', NULL) LOCK_LEVEL, o.owner,o.object_name,o.object_type, s.sid,s.serial#,s.