Mysql
MySQL儲存過程中的鎖定/事務
多個上游 Kapacitor 伺服器正在向負載平衡的 django 應用程序發送通知。如果所有上游伺服器都正常工作,應用程序將始終收到這些通知的副本(因為所有上游通知器都應該發送相同的通知;這只是為了冗餘)。我希望能夠過濾掉這些重複項。但是,由於 python 應用程序是負載平衡的,我們可以檢查這些重複項的唯一位置是在數據庫中。
這意味著我正在使用此儲存過程來控制應用程序邏輯,只需將帶有雜湊的數據插入數據庫並忽略重複項不是一種選擇(應用程序可能會根據警報的內容向某人發送 SMS 消息,所以我們絕對不想要騙子)
為此,我對消息進行散列處理,然後在數據庫中呼叫一個儲存過程,檢查過去 10 秒內收到的消息是否具有相同的散列。我想 99% 確定儲存過程在競爭條件下是安全的。
下面是一些似乎有效的 SQL 程式碼:
DROP PROCEDURE IF EXISTS openduty_check_duplicate; CREATE PROCEDURE openduty_check_duplicate(IN new_hash CHAR(40)) BEGIN DECLARE now TIMESTAMP; DECLARE is_duplicate INT; SELECT CURRENT_TIMESTAMP INTO now; DO GET_LOCK('openduty_check_duplicate', 10); START TRANSACTION; DELETE FROM openduty_dedup WHERE DATE_SUB(now, INTERVAL 10 SECOND) > triggered; SELECT COUNT(*) FROM openduty_dedup WHERE request_hash = new_hash INTO is_duplicate; IF is_duplicate = 0 THEN INSERT INTO openduty_dedup (request_hash, triggered) VALUES (new_hash, now); END IF; COMMIT; DO RELEASE_LOCK('openduty_check_duplicate'); SELECT is_duplicate; END;
這會按預期工作嗎?我需要更改事務隔離級別嗎?在 MySQL 中有沒有更好的方法來做到這一點?
注意 我在一個環境中使用 MySQL 5.6,在另一個環境中使用 Amazon RDS,因此它應該盡可能兼容。
autocommit=1
用, 用做這些PRIMARY KEY(request_hash)
:DELETE FROM openduty_dedup WHERE triggered < NOW() - INTERVAL 10 SECOND AND request_hash = $hash; INSERT IGNORE INTO openduty_dedup (request_hash, triggered) VALUES ($new_hash, NOW());
如果
INSERT
失敗,要麼是最近 10 秒內有條目,要麼是其他人剛剛潛入。如果您需要了解成功,請檢查Rows_affected()
,它是特定於連接的,不需要在事務中。不需要
START TRANSACTION
。無需預先計算“現在”。不需要GET_LOCK()
。唯一的缺陷是硬編碼的 10 秒視窗。