Mysql

我需要鎖定讀取嗎?

  • January 23, 2014

我在表中插入行,基於值和行的存在,我從SELECT語句中獲得。這個函式可以同時執行,所以我顯然需要做一些額外的事情。基本步驟是(虛擬碼)

function doStuff(int someID){
   found = `SELECT someIDFROM targettable` 
   if(found){
       /*we have done this allready, so skip.*/
   }else{
      value =' SELECT value FROM statetable`
      //insert value;
       `INSERT INTO targettable
       VALUES (someID,value)'
   }
}

問題當然是如果 process1 讀取 uniqueID,認為它應該插入該值,並且 process2 在寫入完成之前執行相同操作,我的targettable. 這是可能的(someID 對於該表來說不是唯一的),但在這種情況下是不需要的,因為由於並發性,它是在錯誤的假設下完成的。

現在我的假設/感覺是僅靠交易是不夠的。我認為第二個過程將具有一致的讀取,但這無助於阻止第二個INSERT,是嗎?所以這不行(我想,請就此給我建議。這可能是我要去的地方)。(目前我很確定這不足以確保正確的行為,因為測試)

function doStuff(int someID){
   'START TRANSACTION HERE'
   found = `SELECT someIDFROM targettable where someid = someID ` 
   if(found){
       /*we have done this allready, so skip.*/
   }else{
      value =' SELECT value FROM statetable`
      //insert value;
       `INSERT INTO targettable
       VALUES (someID,value)'
   }
   COMMIT TRANSACTION
} 

從我讀到的關於InnoDB 鎖定的內容來看,我可能應該使用它們,但我不確定,因為我從未使用過它們。也可能是交易已經足夠了,而我才剛剛開始愚蠢。

我目前的想法是使用SELECT someID FROM targettable FOR UPDATE,我認為它會給我一個鎖。但我不確定是什麼鎖,因為它取決於隔離級別。這看起來像這樣:

function doStuff(int someID){
   found = `SELECT someIDFROM targettable where someid = someID FOR UPDATE` 
   if(found){
       /*we have done this allready, so skip.*/
   }else{
      value =' SELECT value FROM statetable`
      //insert value;
       `INSERT INTO targettable
       VALUES (someID,value)'
   }
}

我不確定如何處理value我從另一張桌子上得到的資訊,但這可能是另一回事。

總結:我可以只使用事務,我是否需要使用鎖定讀取(如果需要,如何?)還是我錯過了另一個選項?

更新:如果我做類似的事情

START TRANSACTION;
SELECT * FROM targettable WHERE someid = 3 FOR UPDATE

並在另一個執行緒中查詢,select在此事務完成之前我沒有得到任何結果(鎖定?)。但是,如果我穿上INDEX那個someid,我願意!我可能錯過了一些東西,但我沒想到會這樣。

我怎麼能確定我不會兩次插入這一行?

我有一個建議可以簡化您的程式碼和 SQL

此時,您正在通過檢查每個人進行往返,someID然後返回客戶端,查看檢查是否失敗。如果檢查失敗,則可以執行 INSERT。這又是一次往返。您將需要某種鎖定來進行讀取。

如果您必須插入 10000 行:

  • 在最好的情況下,您執行 10000 次 SELECT 會發現 someID 存在
  • 在最壞的情況下,您執行 10000 次 SELECT,每個 someID 都不存在,然後執行 10000 次 INSERT。

為什麼不用一條 SQL 語句來執行所有這些操作呢?

function doStuff(int someID) {
   getvalue = `SELECT value FROM statetable`
   INSERT IGNORE INTO targettable VALUES (someID,getvalue);
}

僅當 someID 在targettable

在您的程式碼中,嘗試送出 someID 值列表,迭代該列表,然後為所有 someID 值送出 INSERT IGNORE 命令。你可以在一個START TRANSACTION/COMMIT塊中做到這一點。

function doStuff(int someIDList) {
   int idcount = 0;
   getvalue = `SELECT value FROM statetable`
   for someID in someIDList
   do
       if idcount = 0 then
           START TRANSACTION
       endif;
       idcount++;
       INSERT IGNORE INTO targettable VALUES (someID,getvalue);
   end while;
   if idcount > 0 then
       COMMIT
   endif;
}

我認為這個連結可能會做你想要完成的事情。基本上它是在不存在的地方插入語句:https ://stackoverflow.com/a/3025332/16501

引用自:https://dba.stackexchange.com/questions/34807