国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > MySQL > 正文

mysql 隊列 實現并發讀

2024-07-24 12:49:17
字體:
來源:轉載
供稿:網友
一個 MySQL 表可以看作是一個隊列,每一行為一個元素。每次查詢得到滿足某個條件的最前面的一行,并將它從表中刪除或者改變它的狀態,使得下次查詢不會得到它。在沒有并發訪問的情況下,簡單地用 SELECT 得到一行,再用UPDATE(或者DELETE)語句修改之,就可以實現。
復制代碼 代碼如下:

SELECT * FROM targets WHERE status='C' LIMIT 1;
UPDATE targets SET status='D' WHERE id='id';

如果有并發訪問,在SELECT和UPDATE語句之間可能會存在其他地SELECT查詢,導致同一行被取出多次。為了保證在并發情況下仍然能正常工作,一種思路是使用數據庫地鎖來防止,就像在多線程環境下所做地一樣。總之,要是的查詢和修改為一個原子操作,不被其它的訪問干擾。MySQL 5 支持存儲過程,可以用它來實現。
單條 UPDATE 語句應該原子操作的,可以利用這個特性來保證并發訪問情況下隊列的正常工作。每次取元素時,先用 UPDATE 修改符合條件的第一行,然后再得到該行。可惜 UPDATE 語句沒有返回值,重新用普通的SELECT的話又很難找到剛被改過的那條記錄。
這里用到一個小技巧:在 UPDATE 時加上 id=LAST_INSERT_ID(id),再用 SELECT LAST_INSERT_ID() 即可得到剛修改的那條記錄的id。還有一個問題,當表中不存在符合條件的記錄,導致 UPDATE 失敗時,LAST_INSERT_ID() 會保留原來地值不變,因而不能區分隊列中是否還有元素。
ROW_COUNT() 返回上一個語句影響的行數,把它作為 SELECT 的一個條件,可以幫助解決這個問題。
最后,支持并發訪問的完整解決方案為:

復制代碼 代碼如下:

UPDATE targets SET status='D', id=LAST_INSERT_ID(id) WHERE status='C' LIMIT 1;
SELECT * FROM targets WHERE ROW_COUNT()>0 and id=LAST_INSERT_ID();

更新:在實現帶優先級的隊列時這種方法有問題,帶有 ORDER BY ... 條件的 UPDATE 語句非常慢,例如:

復制代碼 代碼如下:
UPDATE targets SET status='D' WHERE status='C' ORDER BY schedule ASC LIMIT 1;


而單獨查詢和更新則是很快的:
復制代碼 代碼如下:

SELECT id FROM targets WHERE status='C' ORDER BY schedule ASC LIMIT 1;
UPDATE targets SET status='D' WHERE id='id';


原來這是MySQL的Bug-12915,一年多以前提出來的,雖然關閉了,卻只解決了部分問題,尚不支持WHERE,見MySQL 5.0.15 的 Changlog。無奈,上面這種巧妙的方法也沒有實用價值了。
最后采用了一種折衷方案,如下:

復制代碼 代碼如下:

UPDATE targets, (SELECT id FROM targets WHERE status='C' AND schedule<CURRENT_TIMESTAMP ORDER BY schedule ASC LIMIT 1) tmp SET status='D' WHERE targets.id=LAST_INSERT_ID(tmp.id);
SELECT * FROM targets WHERE ROW_COUNT()>0 and id=LAST_INSERT_ID();
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 泰州市| 如东县| 建始县| 色达县| 乌兰浩特市| 吉安市| 贞丰县| 且末县| 承德县| 屯门区| 新河县| 达孜县| 县级市| 苍梧县| 东港市| 思茅市| 哈尔滨市| 广宗县| 图们市| 呼和浩特市| 通辽市| 炉霍县| 新乐市| 景宁| 同心县| 青阳县| 奉贤区| 土默特左旗| 固阳县| 多伦县| 香港 | 山阳县| 无为县| 高陵县| 洛阳市| 凤台县| 岢岚县| 镇巴县| 涟源市| 沅江市| 旅游|