2015-06-21 53 views
1

我想設計一個類似於stackoverflow的系統評論功能。也就是說:如何設計任務分配系統?

還有n任務,應該分配給用戶(用戶數量未知)。同一時間,一個任務應該分配給至多一個用戶,不應該爲不同的用戶分配相同的任務。

例如,n = 8,如果一個用戶進入系統默認分配給他3個任務。

  • 在17:00,湯姆進入系統,並獲得任務1,2,3
  • 在17:01,吉姆進入系統,並獲得任務4,5,6,
  • 在17 :02,Jerry進入系統並獲得任務7,8。
  • 在17:03,Bob進入系統並沒有得到任務。
  • 在17:05,湯姆完成任務1,2,離開系統。
  • 在17:06,鮑勃再次進入系統,並獲取任務3.

想我使用數據庫來存儲任務信息。

我的解決方案是,當任務1,2,3分配給Tom時,從DB刪除3條記錄並將它們存儲到內存中。然後其他人不會得到3條記錄。當Tom離開系統時,將他完成的任務和未完成的任務再次插入DB(任務狀態爲「已完成」或「未完成」)。

雖然缺點是存儲記錄不是100%安全的,但如果系統崩潰可能會導致數據丟失問題。

有人可以知道如何計算器設計審查功能?或分享其他解決方案?我想知道在這個用例中SELECT ... FOR UPDATE是否合適。

+0

這取決於你如何定義「進入/離開系統」。簡單的方法:在每次請求期間,您都假設用戶「進入系統」。如果用戶已經分配任務,則一切正常。如果存在未完成的任務,則將一些任務分配給該用戶(即更新數據庫中的任務並記住用戶和時間戳),除非用戶分配了足夠的任務。如果用戶註銷或者有一段時間沒有被看到,您可以從任務表中刪除用戶及其時間戳。因此,您現在需要做的是:在每個請求期間更新任務及其分配。 –

回答

1

你需要實現的是一個FIFO堆棧或簡單隊列。在Oracle中,最好的東西是SELECT ... FOR UPDATESKIP LOCKED子句,除非你想實現一個帶有AQ的實際隊列。 SKIP LOCKED允許我們輕鬆地操作多個用戶的堆棧。

這裏有一個簡單的接口:

create or replace package task_mgmt is 

    function get_next_task return tasks.id%type; 

    procedure complete_task (p_id in tasks.id%type); 

    procedure release_task (p_id in tasks.id%type); 

end task_mgmt; 
/

這是一個最基本的實現:

create or replace package body task_mgmt is 

    function get_next_task return tasks.id%type 
    is 
     return_value tasks.id%type; 
     cursor c_tsk is 
      select id 
      from tasks 
      where status = 'open' 
      order by date_created, id 
      for update skip locked; 

    begin 
     open c_tsk; 
     fetch c_tsk into return_value; 
     update tasks 
     set status = 'progress' 
      , assigned = user 
     where current of c_tsk; 
     close c_tsk; 
     return return_value; 
    end get_next_task; 

    procedure complete_task (p_id in tasks.id%type) 
    is 
    begin 
     update tasks 
     set status = 'complete' 
      , date_completed = sysdate 
     where id = p_id; 
     commit; 
    end complete_task; 

    procedure release_task (p_id in tasks.id%type) 
    is 
    begin 
     rollback; 
    end ; 

end task_mgmt; 
/

更新狀態時,用戶彈出堆棧創建一個鎖。由於SKIP LOCKED子句,下一個用戶將看不到該任務。這比刪除和重新插入記錄要乾淨得多。

這裏有一些數據:

create table tasks (
    id number not null 
    , descr varchar2(30) not null 
    , date_created date default sysdate not null 
    , status varchar2(10) default 'open' not null 
    , assigned varchar2(30) 
    , date_completed date 
    , constraint task_pk primary key (id) 
    ) 
/

insert into tasks (id, descr, date_created) values (1000, 'Do something', date '2015-05-28') 
/
insert into tasks (id, descr, date_created) values (1010, 'Look busy', date '2015-05-28') 
/
insert into tasks (id, descr, date_created) values (1020, 'Get coffee', date '2015-06-12') 
/

讓彈出!這裏的會議之一:

SQL> var tsk1 number; 
SQL> exec :tsk1 := task_mgmt.get_next_task ; 

PL/SQL procedure successfully completed. 

SQL> print :tsk1 

     TSK1 
---------- 
     1000 

SQL> 

同時會話二:

SQL> var tsk2 number; 
SQL> exec :tsk2 := task_mgmt.get_next_task ; 

PL/SQL procedure successfully completed. 

SQL> print :tsk2 

     TSK2 
---------- 
     1010 

SQL> 

早在會議之一:

SQL> exec task_mgmt.complete_task (:tsk1); 

PL/SQL procedure successfully completed. 

SQL> exec :tsk1 := task_mgmt.get_next_task ; 

PL/SQL procedure successfully completed. 

SQL> print :tsk1 

     TSK 
---------- 
     1020 

SQL> 

這種方法的主要缺點是它要求用戶在工作時保持有狀態的會話e任務。它不是這種情況,那麼你需要一個API,其中get_next_task()是一個離散事務,並忘記鎖定。


順便說一下,它可能會更好,讓用戶下載,而不是通過登錄觸發器爲它們分配一個任務(或任何你在心裏有「湯姆進入系統,並獲得任務1,2,3」) 。拉取任務是SO審覈隊列的工作方式。

此外,一次只分配一個任務。這樣你可以得到有效的工作分配。你想避免湯姆在盤子上有三項任務的情況,其中一項不會完成,而鮑勃無所事事。也就是說,除非你是鮑勃。

+0

謝謝,'select ... for update skip locked'在這種情況下真的很有用! – coderz