mysql 隊(duì)列 實(shí)現(xiàn)并發(fā)讀
更新時(shí)間:2012年04月24日 20:39:01 作者:
隊(duì)列是常用的數(shù)據(jù)結(jié)構(gòu),基本特點(diǎn)就是先入先出,在事務(wù)處理等方面都要用到它,有的時(shí)候是帶有優(yōu)先級(jí)的隊(duì)列。當(dāng)隊(duì)列存在并發(fā)訪問(wèn)的時(shí)候,比如多線程情況下,就需要鎖機(jī)制來(lái)保證隊(duì)列中的同一個(gè)元素不被多次獲取
一個(gè) MySQL 表可以看作是一個(gè)隊(duì)列,每一行為一個(gè)元素。每次查詢得到滿足某個(gè)條件的最前面的一行,并將它從表中刪除或者改變它的狀態(tài),使得下次查詢不會(huì)得到它。在沒(méi)有并發(fā)訪問(wèn)的情況下,簡(jiǎn)單地用 SELECT 得到一行,再用UPDATE(或者DELETE)語(yǔ)句修改之,就可以實(shí)現(xiàn)。
SELECT * FROM targets WHERE status='C' LIMIT 1;
UPDATE targets SET status='D' WHERE id='id';
如果有并發(fā)訪問(wèn),在SELECT和UPDATE語(yǔ)句之間可能會(huì)存在其他地SELECT查詢,導(dǎo)致同一行被取出多次。為了保證在并發(fā)情況下仍然能正常工作,一種思路是使用數(shù)據(jù)庫(kù)地鎖來(lái)防止,就像在多線程環(huán)境下所做地一樣。總之,要是的查詢和修改為一個(gè)原子操作,不被其它的訪問(wèn)干擾。MySQL 5 支持存儲(chǔ)過(guò)程,可以用它來(lái)實(shí)現(xiàn)。
單條 UPDATE 語(yǔ)句應(yīng)該原子操作的,可以利用這個(gè)特性來(lái)保證并發(fā)訪問(wèn)情況下隊(duì)列的正常工作。每次取元素時(shí),先用 UPDATE 修改符合條件的第一行,然后再得到該行??上?UPDATE 語(yǔ)句沒(méi)有返回值,重新用普通的SELECT的話又很難找到剛被改過(guò)的那條記錄。
這里用到一個(gè)小技巧:在 UPDATE 時(shí)加上 id=LAST_INSERT_ID(id),再用 SELECT LAST_INSERT_ID() 即可得到剛修改的那條記錄的id。還有一個(gè)問(wèn)題,當(dāng)表中不存在符合條件的記錄,導(dǎo)致 UPDATE 失敗時(shí),LAST_INSERT_ID() 會(huì)保留原來(lái)地值不變,因而不能區(qū)分隊(duì)列中是否還有元素。
ROW_COUNT() 返回上一個(gè)語(yǔ)句影響的行數(shù),把它作為 SELECT 的一個(gè)條件,可以幫助解決這個(gè)問(wèn)題。
最后,支持并發(fā)訪問(wèn)的完整解決方案為:
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();
更新:在實(shí)現(xiàn)帶優(yōu)先級(jí)的隊(duì)列時(shí)這種方法有問(wèn)題,帶有 ORDER BY ... 條件的 UPDATE 語(yǔ)句非常慢,例如:
而單獨(dú)查詢和更新則是很快的:
SELECT id FROM targets WHERE status='C' ORDER BY schedule ASC LIMIT 1;
UPDATE targets SET status='D' WHERE id='id';
原來(lái)這是MySQL的Bug-12915,一年多以前提出來(lái)的,雖然關(guān)閉了,卻只解決了部分問(wèn)題,尚不支持WHERE,見(jiàn)MySQL 5.0.15 的 Changlog。無(wú)奈,上面這種巧妙的方法也沒(méi)有實(shí)用價(jià)值了。
最后采用了一種折衷方案,如下:
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();
復(fù)制代碼 代碼如下:
SELECT * FROM targets WHERE status='C' LIMIT 1;
UPDATE targets SET status='D' WHERE id='id';
如果有并發(fā)訪問(wèn),在SELECT和UPDATE語(yǔ)句之間可能會(huì)存在其他地SELECT查詢,導(dǎo)致同一行被取出多次。為了保證在并發(fā)情況下仍然能正常工作,一種思路是使用數(shù)據(jù)庫(kù)地鎖來(lái)防止,就像在多線程環(huán)境下所做地一樣。總之,要是的查詢和修改為一個(gè)原子操作,不被其它的訪問(wèn)干擾。MySQL 5 支持存儲(chǔ)過(guò)程,可以用它來(lái)實(shí)現(xiàn)。
單條 UPDATE 語(yǔ)句應(yīng)該原子操作的,可以利用這個(gè)特性來(lái)保證并發(fā)訪問(wèn)情況下隊(duì)列的正常工作。每次取元素時(shí),先用 UPDATE 修改符合條件的第一行,然后再得到該行??上?UPDATE 語(yǔ)句沒(méi)有返回值,重新用普通的SELECT的話又很難找到剛被改過(guò)的那條記錄。
這里用到一個(gè)小技巧:在 UPDATE 時(shí)加上 id=LAST_INSERT_ID(id),再用 SELECT LAST_INSERT_ID() 即可得到剛修改的那條記錄的id。還有一個(gè)問(wèn)題,當(dāng)表中不存在符合條件的記錄,導(dǎo)致 UPDATE 失敗時(shí),LAST_INSERT_ID() 會(huì)保留原來(lái)地值不變,因而不能區(qū)分隊(duì)列中是否還有元素。
ROW_COUNT() 返回上一個(gè)語(yǔ)句影響的行數(shù),把它作為 SELECT 的一個(gè)條件,可以幫助解決這個(gè)問(wèn)題。
最后,支持并發(fā)訪問(wèn)的完整解決方案為:
復(fù)制代碼 代碼如下:
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();
更新:在實(shí)現(xiàn)帶優(yōu)先級(jí)的隊(duì)列時(shí)這種方法有問(wèn)題,帶有 ORDER BY ... 條件的 UPDATE 語(yǔ)句非常慢,例如:
復(fù)制代碼 代碼如下:
UPDATE targets SET status='D' WHERE status='C' ORDER BY schedule ASC LIMIT 1;
而單獨(dú)查詢和更新則是很快的:
復(fù)制代碼 代碼如下:
SELECT id FROM targets WHERE status='C' ORDER BY schedule ASC LIMIT 1;
UPDATE targets SET status='D' WHERE id='id';
原來(lái)這是MySQL的Bug-12915,一年多以前提出來(lái)的,雖然關(guān)閉了,卻只解決了部分問(wèn)題,尚不支持WHERE,見(jiàn)MySQL 5.0.15 的 Changlog。無(wú)奈,上面這種巧妙的方法也沒(méi)有實(shí)用價(jià)值了。
最后采用了一種折衷方案,如下:
復(fù)制代碼 代碼如下:
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();
您可能感興趣的文章:
- 深入mysql并發(fā)插入優(yōu)化詳解
- MySQL主從復(fù)制的原理及配置方法(比較詳細(xì))
- mysql中復(fù)制表結(jié)構(gòu)的方法小結(jié)
- MySQL復(fù)制表結(jié)構(gòu)和內(nèi)容到另一張表中的SQL語(yǔ)句
- MySQL的主從復(fù)制步驟詳解及常見(jiàn)錯(cuò)誤解決方法
- mysql主從同步復(fù)制錯(cuò)誤解決一例
- MySQL的隱式類(lèi)型轉(zhuǎn)換整理總結(jié)
- MySQL隱式類(lèi)型的轉(zhuǎn)換陷阱和規(guī)則
- MySQL 5.7并發(fā)復(fù)制隱式bug實(shí)例分析
相關(guān)文章
MySQL數(shù)據(jù)庫(kù)主從復(fù)制原理及作用分析
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)主從復(fù)制原理并分析了主從復(fù)制的作用和使用方法,有需要的的朋友可以借鑒參考下,希望可以有所幫助,感謝閱讀2021-09-09在Linux環(huán)境下mysql的root密碼忘記解決方法(三種)
這篇文章主要介紹了在Linux環(huán)境下mysql的root密碼忘記解決方法,詳細(xì)的介紹了3種解決辦法,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12MySQL基于索引的壓力測(cè)試的實(shí)現(xiàn)
本文主要介紹了MySQL基于索引的壓力測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11Navicat Premium15連接云服務(wù)器中的數(shù)據(jù)庫(kù)問(wèn)題及遇到坑
這篇文章主要介紹了Navicat Premium15連接云服務(wù)器中的數(shù)據(jù)庫(kù)問(wèn)題及遇到坑,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03MYSQL大小寫(xiě)不敏感導(dǎo)致用戶登錄異常問(wèn)題
這篇文章主要介紹了MYSQL大小寫(xiě)不敏感導(dǎo)致用戶登錄異常問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04深入探索數(shù)據(jù)庫(kù)MySQL性能優(yōu)化與復(fù)雜查詢相關(guān)操作
數(shù)據(jù)庫(kù)MySQL 是一種開(kāi)源的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),在進(jìn)行 MySQL 數(shù)據(jù)庫(kù)開(kāi)發(fā)過(guò)程中,需要深入了解如何進(jìn)行性能優(yōu)化和復(fù)雜查詢,以提高系統(tǒng)的效率和可靠性,本文介紹的非常詳細(xì),需要的朋友可以參考一下2023-04-04MySQL5.6.40在CentOS7 64下安裝過(guò)程詳解
這篇文章主要介紹了MySQL5.6.40在CentOS7 64下安裝過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06