2017-01-11 39 views
1

我想在使用MySQL數據庫編寫的PHP應用程序中創建計數器。用戶給出一個字符串作爲輸入。該字符串表示數據庫記錄的關鍵字。它存儲一個整數計數器值。用戶獲取增加的值。在MySQL中創建可靠的計數器

這是我如何做到這一點的時刻:

// connect to database 
try { 
    $db = new PDO('mysql:host='.$dbhost.';dbname='.$dbname, $dbuser, $dbpasswd); 
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
    $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); 
} catch(Exception $ex) { 
    echo "DB Error: ".$ex->getMessage(); 
    exit; 
} 

// update and get new value 
try { 
    $db->beginTransaction(); 
    $nc = updateNC($db, $grundsig, $library); 
    $db->commit(); 
} catch(Exception $ex) { 
    $db->rollBack(); 
    $err = 'DB Error: '.$ex->getMessage(); 
} 

// [...] 

function updateNC($db, $grundsig, $library) { 

    // increment counter ("nc") 
    $query = $db->prepare(' 
     update numeruscurrens n 
     join libraries l using(libid) 
     set n.nc = n.nc+1 
     where n.grundsig = :grundsig and l.libname = :libname 
    '); 
    $query->execute([ 
     ':grundsig' => $grundsig, 
     ':libname' => $library 
    ]); 

    // get new counter value 
    $query = $db->prepare(' 
     select * 
     from numeruscurrens n 
     join libraries l using(libid) 
     where n.grundsig = :grundsig and l.libname = :libname 
    '); 
    $query->execute([ 
     ':grundsig' => $grundsig, 
     ':libname' => $library 
    ]); 
    $result = $query->fetchAll(); 
    return $result[0]['nc']; 
} 

的重要組成部分發生在updateNC功能。我做了更新來增加計數器,我想要新的計數器值。

現在問題出現在這兩個命令之間,另一個用戶可以進行更新。可能發生兩個用戶得到錯誤的計數器值。

如何可靠地遞增計數器併爲此用戶獲取新值?

我也想過用一個鍵 - 值數據庫Redis的一樣它有INCR命令。但我認爲這會有點誇張。

+0

我不確定它是否有意義,你正在嘗試。但我能問你有多少用戶?兩個用戶同時輸入相同鍵值的機會有多大?它應該接近0. – Twinfriends

+0

我認爲如果有機會,應該找到一種繞過它的方法。一個約20人的團隊正在使用這些計數器。如果發生這樣的錯誤,這將是一個很大的問題。贏得彩票也是幾乎不可能的,但有些人會這樣做。 – Michael

+0

他們要我們這個計數器所有秒/分鐘?也可以解釋你想要做什麼/爲了你想要使用它。我認爲有比你嘗試的更好的解決方案。另外,如果我瞭解你,會有X計數器,因爲你說用戶可以把鑰匙交給櫃檯?那麼爲什麼你的團隊的所有成員都必須編輯所有的計數器?爲什麼不簡單地爲每個成員創建一個計數器,給他們一個鍵,一切都會好起來的。? – Twinfriends

回答

1

如果你想要的是,以確保他們將得到立即增加值,那麼你應該用一種方式來鎖定你要commiting交易之前更新的行。這可以使用FOR UPDATE提示和先前的SELECT查詢來鎖定即將更新的行。

我只會添加SQL代碼來關注實際問題。

SET @counter = 0; 

BEGIN; 

SELECT n.nc INTO @counter 
FROM numeruscurrens n 
JOIN libraries l using(libid) 
WHERE n.grundsig = @grundsig and l.libname = @libname 
FOR UPDATE; 

update numeruscurrens n 
join libraries l using(libid) 
set n.nc = n.nc+1 
where n.grundsig = @grundsig and l.libname = @libname 

/* You are always incrementing, aren't you? */ 
SET @counter = @counter + 1; 

SELECT @counter; 

COMMIT; 

另請注意,這不是免費的。實際上,必須將此增量值正確地返回給用戶,因爲此鎖定會由於行鎖定而對性能產生負面影響,並且可能會造成死鎖。最後但並非最不重要的,確保表格使用InnoDB引擎。 MyIsam不支持行級鎖。

+1

看起來相當不錯。你的解決方案引導我到那裏注意到在MySQL中只使用一個表訪問的更好的方法的文檔:UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); SELECT LAST_INSERT_ID(); – Michael

+0

我不知道那個,很整齊。 – jorgonor