8

我有一個存儲過程mysql這是執行需要同步的任務,如果兩個應用程序調用存儲過程,只有一個可以訪問一段代碼來執行任務,保持另一個被阻止,直到第一個完成任務。在mysql中同步存儲過程執行

DELIMITER $$ 
CREATE PROCEDURE SP_GEN_ID(IN NAME VARCHAR(20)) 

BEGIN 
    DECLARE maxLen int default 0; 
START TRANSACTION; 
    #the section of code that needs to be synchronized 
COMMIT 
END; 
$$ 

DELIMITER ; 

因此,如果兩個應用程序同時調用存儲過程,則任務必須同步。

a。但開始交易COMMIT沒有同步執行。

b。和LOCK TABLES TABLEA不能在存儲過程中使用,以確保同步了。

c。我試圖在應用程序級別同步存儲過程調用。我用

boost_interprocess scoped_lock lock();

它在提升1.41

但進程間鎖定互斥的工作完全正常的助推1.34庫,這是在我的情況下,可用不支持。

有沒有一種方法,當兩個呼叫同時進行,一個被其他被執行之前阻止同步的代碼,例如存儲過程部分?

(已添加以下內容) 已編輯的代碼:說明我正在嘗試在存儲過程的同步塊中執行什麼操作。

它得到的最後分配的ID,並通過一個遞增,並檢查它是否不用於一些其它的名字「記錄。 找到一個有效的ID後,更新最後分配的ID記錄表,然後將其與給定的「名稱」相關聯。

DELIMITER $$ 
CREATE PROCEDURE SP_GEN_ID(IN NAME VARCHAR(20)) 

BEGIN 
    DECLARE maxLen int default 0; 
START TRANSACTION; 
    #the section of code that needs to be synchronized 
    SELECT lastid into lastgenerated FROM DB_last_id WHERE key = 'NAME_ID';  
findid_loop: 
    LOOP 
    set lastid = lastid + 1; 
    #this is to check whether it was assigned with someother name before. 
    IF not EXISTS (SELECT 1 FROM user_name_id WHERE name_id = lastgenerated) then 
        set nameid = lastgenerated; 
        set found = true; 
        LEAVE findid_loop; 
      END IF; 

      #for loop limit check 
      IF (counter < loopLimit) then 
        set counter = counter + 1; 
        ITERATE findid_loop; 
      ELSE 
        #reached the limit and not found. 
        LEAVE findid_loop; 
      END IF; 
    END LOOP findid_loop; 

    #if a valid id, update last id and assign to name. 
    IF (found) THEN 
      #update the id. 
      update DB_last_id set lastid = nameid where key = 'NAME_ID'; 
      insert into user_name_id values (nameid ,name); 
    ELSE 
      #return an empty string for the application to take action on the failure. 
      set nameid = ''; 
    END IF; 
#end transaction here. 
COMMIT 

END; 
$$ 

DELIMITER ; 
+0

你的表使用什麼存儲引擎?他們必須是InnoDB才能進行交易/鎖定工作。 – eggyal

+0

存儲引擎是InnoDB的表。但它似乎還沒有工作。爲了驗證這一點,我做了一個睡眠(15),在START TRANSACTION之後立即等待15秒。當我同時調用存儲過程時,兩者似乎在15秒後出現。如果START TRANSACTION確保同步,則在存儲過程的第二次調用應該在30秒左右後結束。對? (第一次來電15秒,然後第二次進入START TRANSACTION,睡15秒) –

+0

你想要同步的代碼是什麼?如果您只是希望SQL操作是原子操作,那麼在事務中執行它們應該可以實現這一點。如果您正在影響其他狀態,如系統變量,則需要獲取專用於此目的的表/記錄的鎖定。在第二次調用中沒有延遲並不一定表示鎖失敗(例如,可能存在緩存問題,使第二次調用比第一次調用更快;或者第二次調用可能執行不同的執行第一條路徑)。請提供完整的代碼。 – eggyal

回答

8

用START TRANSACTION啓動事務實際上並不啓動它。 START TRANSACTION之後的第一個表訪問會執行。打開一個事務並不意味着併發控制。如果你需要的話,你可以依靠MySQL提供的諮詢鎖系統GET_LOCK(),RELEASE_LOCK()和一些其他相關的功能。

這次通過事務實現併發控制的另一種方法是依靠獨佔行鎖。由於SELECT語句在InnoDB中是非鎖定的,因此發出此類查詢會啓動一個事務,但它既不設置任何鎖也不尊重任何預先存在的鎖。如果有一個SELECT語句實際上阻止了在相同信息(行)上運行的早期事務,則必須使用FOR UPDATE子句。例如:

START TRANSACTION; 
SELECT lastid into lastgenerated FROM DB_last_id WHERE key = 'NAME_ID' FOR UPDATE; 
... 

利用這種結構永遠不會有過去的SELECT聲明被明確告知要執行鎖定讀同一'NAME_ID'運行兩個併發事務。

+0

工作得很好!謝謝。 –

9

正如我上面的評論中提到,你應該找到一個事務足以滿足大多數需求;但是,如果你需要明確等到其他調用已完成,使用GET_LOCK(str,timeout)

試圖獲得由字符串str一個名字的鎖,使用的timeout超時秒。如果成功獲得鎖,0嘗試是否超時(例如,由於另一個客戶端已先前鎖定的名稱),或者NULL如果發生錯誤(比如內存溢出或線程用mysqladmin kill被殺)返回1 。如果你有GET_LOCK()獲得一把鎖,它是當你執行RELEASE_LOCK(),執行新的GET_LOCK(),或者連接終止(正常或非正常)釋放。使用GET_LOCK()獲得的鎖不會與交易進行交互。也就是說,提交事務不會釋放交易期間獲得的任何此類鎖定。

此函數可用於實現應用程序鎖定或模擬記錄鎖定。名稱在服務器範圍內鎖定。如果名稱已被一個客戶端鎖定,則GET_LOCK()將阻止其他客戶端對具有相同名稱的鎖的任何請求。這使得同意給定鎖名稱的客戶端可以使用該名稱執行合作諮詢鎖定。但請注意,它還可以使不屬於協作客戶端的客戶端無意或故意鎖定名稱,從而防止任何合作客戶端鎖定該名稱。降低這種可能性的一種方法是使用特定於數據庫或特定於應用程序的鎖定名稱。例如,使用的形式db_name.strapp_name.str的鎖名稱。

mysql>SELECT GET_LOCK('lock1',10); 
     -> 1 
mysql>SELECT IS_FREE_LOCK('lock2'); 
     -> 1 
mysql>SELECT GET_LOCK('lock2',10); 
     -> 1 
mysql>SELECT RELEASE_LOCK('lock2'); 
     -> 1 
mysql>SELECT RELEASE_LOCK('lock1'); 
     -> NULL 

第二RELEASE_LOCK()調用返回NULL因爲鎖'lock1'已自動由第二GET_LOCK()呼叫釋放。

如果多個客戶端正在等待鎖定,他們將獲取它的順序是未定義的,並取決於諸如正在使用的線程庫等因素。具體而言,應用程序不應該假定客戶端將以與發出鎖定請求相同的順序獲取鎖定。

注意
的MySQL 5.5.3之前,如果客戶端試圖獲取已被另一客戶持有的鎖,根據timeout論證它的塊。如果被阻止的客戶端終止,它的線程不會死亡,直到鎖定請求超時。

該函數對於基於語句的複製是不安全的。從MySQL 5.5.1開始,如果在binlog_format設置爲STATEMENT時使用此函數,則會記錄一條警告。 (Bug#47995)

+0

我從mysql文檔中閱讀了關於GET_LOCK的內容。然後我試着通過執行mysql> SELECT GET_LOCK('lock2',10)來測試它。 - > 1在同一個會話中兩次,並假定第二次調用GET_LOCK具有相同的名稱(lock2)dint等待第一個完成。我剛剛通過調用兩個不同會話的存儲過程在我的存儲過程中測試了這一點。它工作得很好。第二次調用GET_LOCK等待第一個釋放之前釋放。謝謝,eggyal! –

+0

雖然你的解決方案和Mushu的解決我的情況,但我只能爲這個問題選擇一個答案。我選擇了他的那個,因爲那個更簡單,更適合我的情況。 –

+0

抱歉,我仍然無法'upvote',因爲我沒有足夠的積分來這樣做。當我得到足夠的分數時,我會回到這裏。謝謝! –