我需要鎖定讀取嗎?
我在表中插入行,基於值和行的存在,我從
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