我有一個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日期?
謝謝,我注意到你從你一些其他的答案,並希望你在這裏回答爲好。我已經更新了我的初始文章,實施了您在其他文章中建議的例程。你能檢查一下它是否正確嗎? – maddo7
附加說明:我不明白你的觀點3.2:如何檢查一個條目是否不再無人認領? – maddo7
附加註釋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