2014-03-03 58 views
0

我有一個mysql隊列,它管理幾個php工人的任務,這些工人通過cron工作每分鐘運行一次。 我會簡化一切,使其更容易理解。關於隊列系統的問題

對於mysql一部分,我有2個表:

worker_info 

worker_id | name | hash  | last_used 
1   | worker1 | d8f9zdf8z | 2014-03-03 13:00:01 
2   | worker2 | odfi9dfu8 | 2014-03-03 13:01:01 
3   | worker3 | sdz7std74 | 2014-03-03 13:02:03 
4   | worker4 | duf8s763z | 2014-03-03 13:02:01 
... 

tasks 

task_id | times_run | task_id | workers_used 
1  | 3   | 2932 | 1,6,3 
2  | 2   | 3232 | 6,8 
3  | 6   | 5321 | 3,2,6,10,5,20 
4  | 1   | 8321 | 3 
... 

任務是跟蹤的任務表:

TASK_ID標識每個任務,times_run是一個任務已次數成功執行。 task_id是php腳本程序需要的一個數字。 workers_used是一個文本字段,它包含爲此任務處理的所有worker_infos的id。我不希望每個任務多次使用同一個worker_info,只有一次。

worker_info是一張表,它包含php腳本需要與last_used一起完成工作的一些信息,last_used是此工作人員上次使用時的全局指示符。

幾個php腳本工作在相同的任務,我需要的值是精確的,因爲每個worker_info應該只用於每個任務1次。

的PHP cron作業包括所有相同的套路:

腳本執行MySQL查詢得到的任務。

1. SELECT * FROM tasks ORDER BY times_run ASC LIMIT 1我們一直在與1個作業在一個時間

腳本鎖定worker_info表的工作,以避免一個worker_info會從一個任務查詢

2. LOCK TABLES worker_info WRITE

然後獲取多次選擇所有未用於此任務的worker_infos的列表,按last_used排序

3. SELECT * FROM worker_info WHERE worker_id NOT IN($workers_used) ORDER BY last_used ASC LIMIT 1

然後更新last_used參數,以便同worker_info將不會在此期間選擇當任務仍然運行

4. UPDATE workder_info Set last_used = NOW() WHERE worker_id = $id

最後鎖定得到釋放

5. UNLOCK TABLES

的PHP腳本執行其例程,如果任務成功,它會得到更新

6. UPDATE tasks SET times_run = times_run + 1, workers_used = IF(workers_used = '', '$worker_id', CONCAT(workers_used,', $worker_id'))我知道這是非常糟糕的做法,執行workers_used這種方式不使用第二個表來聲明依賴關係,但我有點害怕它將採取的空間。 一個任務可以有幾千個worker_used,我自己有幾千個任務。這樣,表格很快就會超過100萬個條目,我擔心這會讓事情變得非常緩慢,所以我採用了這種存儲方式。

然後,腳本執行步驟2-6 10次,然後返回第1步選擇新任務並重新執行任務。

現在這個安裝程序已使我受益匪淺一年左右,但現在,我需要有50+此隊列系統上激活PHP腳本,我得到在性能方面的問題越來越多。 PHP查詢最多需要20秒,而且我無法像我需要的那樣擴展,如果我只運行更多的PHP腳本,mysql服務器就會崩潰。 如果系統崩潰,我不想丟失任何數據,因此我正在將每次更改寫入數據庫。另外,當我創建系統時,我遇到了workers_used問題,因爲當10個php腳本在1個任務上工作時,經常發生一個worker_info數據在我不想要的同一個任務中被多次使用。

所以我介紹了這個固定的鎖,但我懷疑它是系統的瓶頸。如果一名工作人員鎖定桌面來執行其操作,則所有其他49名php工作人員都需要等待這種情況。

現在我的問題是:

這個實現甚至好嗎?我應該堅持它還是把它扔掉,做一些其他的事情?

這是LOCK甚至我的問題或做別的事情可能會拖慢系統?

我怎樣才能改善這種設置,使之快了很多?

//編輯作爲建議的jeremycole:

我想我需要更新worker_info表,以實施更改:

worker_info 

worker_id | name | hash  | tasks_owner | last_used 
1   | worker1 | d8f9zdf8z | 1   | 2014-03-03 13:00:01 
2   | worker2 | odfi9dfu8 | NULL  | 2014-03-03 13:01:01 
3   | worker3 | sdz7std74 | NULL  | 2014-03-03 13:02:03 
4   | worker4 | duf8s763z | NULL  | 2014-03-03 13:02:01 
... 

,且程序更改爲:

SET autocommit=0將自動提交設置爲0,以便查詢不會自動獲取

1. SELECT * FROM tasks ORDER BY times_run ASC LIMIT 1選擇一個任務來處理

2. START TRANSACTION

3. SELECT * FROM worker_info WHERE worker_id NOT IN($workers_used) AND tasks_owner IS NULL ORDER BY last_used ASC LIMIT 1 FOR UPDATE

4. UPDATE worker_info SET last_used = NOW(), tasks_owner = $task_id WHERE worker_id = $worker_id

5. COMMIT

待辦事項PHP程序,如果成功:

6. UPDATE tasks SET times_run = times_run + 1, workers_used = IF(workers_used = '', '$worker_id', CONCAT(workers_used,', $worker_id'))

這應該是它還是我錯在某些時候? 是否真的需要tasks_owner還是足以改變last_used日期?

回答

1

閱讀我的回答有關如何實現MySQL中的作業隊列這裏的另一個問題可能是有用的:

MySQL deadlocking issue with InnoDB

總之,使用LOCK TABLES因爲這是完全沒有必要的,不可能產生良好的結果。您的回覆

+0

謝謝,我注意到你從你一些其他的答案,並希望你在這裏回答爲好。我已經更新了我的初始文章,實施了您在其他文章中建議的例程。你能檢查一下它是否正確嗎? – maddo7

+0

附加說明:我不明白你的觀點3.2:如何檢查一個條目是否不再無人認領? – maddo7

+0

附加註釋II:我只是用包含一個ID,一個數字和一個日期的表格進行測試,並用各種條目填充它。然後我在2個php文件中同時執行這些查詢:'SET autocommit = 0; BEGIN TRANSACTION; SELECT * FROM mysql_tests ORDER BY date date ASC FOR UPDATE; UPDATE mysql_tests SET count = count + 1 WHERE tid = 1; COMMIT;'I added提交前10秒延遲,所以我可以看到會發生什麼。結果是一個帶有查詢的腳本在執行查詢之前等待另一個腳本完成,因此它們都在同一行上工作。有沒有一種方法, – maddo7