Yêu cầu thg 8 10, 2018 2:01 SA 262 1 1
  • 262 1 1
0

Giải Pháp Cứ 16 Giây Thì Phải Tạo Hoặc Update 80.000 rows.

Chia sẻ
  • 262 1 1

Chào các bạn, mình gặp một vấn đề khi phát triển project trên nền laravel

Nói sơ qua về project này.

  • Đầu Tiên Người dùng tạo Jobs :
  • Cứ mỗi 16s giây lại phải làm 1 lần, làm xong sẽ lưu lại log bao gồm trạng thái và kết quả ,tổng cộng làm 5000 lần sẽ xong Jobs.
  • Jobs khi sử lý cần phải curl 3-5 lần rất tốn tài nguyên.
  • Jobs phải có chức năng stop nên mình cũng ko thể add hết 5000 lần vào hàng đợi mà phải tạo bảng chứa time_start cho từng jobs rồi sau đó.Mình sẽ phải dùng scheduling chạy mỗi phút 1 lần query đám jobs đã đến lúc chạy để thêm chúng vào hàng đợi.

Hiện có tầm 5000 jobs và đang tăng lên.

=> cứ 16 giây thì phải tạo hoặc update 80.000 rows.

=> Web đơ

Mình research vài ngày nhưng vẫn chưa ra giải pháp nào tốt. Hy vọng bạn nào đã từng làm project tương tự share mình ít kinh nghiệm

thg 8 10, 2018 2:56 SA

Theo mình hiểu thì jobs được chạy phía backend sao lại làm web (phần frontend phía trên) đơ được bạn nhỉ?

Trên frontend phải hiển thị được trạng thái đang chạy của jobs à?

Avatar Tran Duc Thang @thangtd90
thg 8 10, 2018 4:10 SA

Thực sự đọc giải thích của bạn khó hiểu quá 😂 Mình ngồi đọc đi đọc lại không dưới chục lần nhưng vẫn ... chưa hiểu gì cả 😦

Bạn giải thích thêm một chút được không

Cứ mỗi 16s giây lại phải làm 1 lần

"làm 1 lần" ở đây là làm cái gì? Bạn đang làm thế nào để căn 16 giây để thực hiện việc đó.

làm xong sẽ lưu lại log bao gồm trạng thái và kết quả

lưu trạng thái và kết quả của cái gì? bởi theo bạn nói là chạy 5000 lần mới xong 1 job, nên mình không rõ là lưu lại log thì là log của cái gì 🤔

Jobs khi sử lý cần phải curl 3-5 lần rất tốn tài nguyên

Tức là 1 job của bạn chạy 5000 lần một cái gì đó, và trong đó có 3-5 lần là curl, không biết mình hiểu thế có đúng không 🤔 Bạn đang curl cái gì mà lại tốn tài nguyên? Hiện quá trình đó đang xử lý mất bao lâu? Từ đó tính ra để chạy hết 1 job hiện đang cần bao nhiêu thời gian?

cứ 16 giây thì phải tạo hoặc update 80.000 rows

Schedulling hay crontab thì chỉ chạy được theo từng phút, hiện bạn đang xử lý thế nào để cứ 16 giây lại xử lý 1/5000 của job nhỉ? Và sao lại cần phải tạo hoặc update 80,000 rows vậy? Bạn tạo mới, hay update 80,000 rows này như thế nào, cần dùng đến khoảng bao nhiêu câu query?

=> Web đơ

Web đơ là do web cũng request đến bảng chứa 80,000 rows được tạo mới/update, mình hiểu thế đúng không nhỉ?

Bạn nên đưa thêm thông tin chi tiết thì mọi người sẽ có thể hiểu và support bạn được tốt hơn 😄

thg 8 10, 2018 6:28 SA

@thangtd90 Theo em hiểu thì vấn đề của bạn ấy là thế này

  • Có 1 job to phải thực hiện làm 5000 lần 1 job nhỏ, mỗi lần cách nhau 16s
  • Mỗi job nhỏ thì 1 lần curl 3 lần, tốn tài nguyên chắc là tốn bandwidth (?)
  • Cần phải log lại kết quả của từng job nhỏ

Schedulling hay crontab thì chỉ chạy được theo từng phút, hiện bạn đang xử lý thế nào để cứ 16 giây lại xử lý 1/5000 của job nhỉ?

  • Bạn ý có ghi rồi, là có time_start cho từng job, từng phút thì lấy các job đã đến giờ chạy ra để chạy (1 job to thì 1 phút có 3,4 job con được chạy)
  • Vụ web đơ em cũng đang hỏi 😄 Đợi bạn ấy vào confirm rồi nghĩ xem có cách nào không 😄
Avatar Duc @MinhDuc
thg 8 10, 2018 10:53 SA

@bs90 : Bác Nói đúng ý em rồi Đơ là do em sử lý bacnkend cả triệu reuqest một lúc 😦 Vì Khi người dùng tạo ra một tiến trình . Ngay lập tức trong backend của em sẽ tạo 5000 rows chứa Thông tin về Jobs của Tiến trình Vừa tạo. => em nói thêm là cứ mỗi lần request thành công em sẽ tính phí người dùng.

Giải pháp ném lên Redis : Gặp một ván đề là khi người dùng hủy tiến trình thì các Jobs của tiến trình đó phải hủy theo. 😦

1 CÂU TRẢ LỜI


Đã trả lời thg 8 10, 2018 6:31 SA
+2

@MinhDuc Với thông tin hiện tại mình có một ý kiến thế này

Bạn có thể thử dùng Redis để lưu 5000 kết quả trung gian. Redis đọc ghi rất nhanh, ưu việt hơn hẳn so với SQL nên có thể sẽ giải quyết tốt bài toán đọc ghi liên tục của bạn.

Sau khi hoàn thành job to bạn có thể ghi 1 lần các kết quả trung gian vào DB chính, hoặc nếu không cần thiết thì delete dữ liệu ở Redis đi là xong.

Chia sẻ
Avatar Duc @MinhDuc
thg 8 10, 2018 10:56 SA

Chào bạn. Các trên gặp một vấn đề là Khi Người dùng dừng tiến trình. Thì các Jobs buộc phải bị hủy. Nếu vậy. Trong jobs ta vẫn phải request tới database để kiểm tra trạng thái của tiếng trình 😦

thg 8 10, 2018 12:45 CH

@MinhDuc redis về cơ bản nó cũng là một dạng db, đọc ghi bình thường mà 😃

Giả sử user huỷ job lớn thì set một cái key “<job_id>_cancelled” value true.

Trước khi chạy job con nào thì check cái key đó, true thì return luôn không cho chạy nữa chẳng hạn.

thg 8 10, 2018 1:47 CH

Ngoài ra thay vì tạo một lúc 5000 rows, nếu có thể bạn có thể chỉ set các job con cho phút đầu tiên, sau mỗi mỗi job con sẽ có callback để init job con kế tiếp gối nhau như thế. Tuy nhiên làm thế này sẽ phải handle error tốt nếu không muốn đang chạy thì đứt gánh 😃

Avatar Duc @MinhDuc
thg 8 10, 2018 3:38 CH

1.Qua gợi ý của bạn. Mình nghĩ ko nên dùng queues vì sợ mất data , time out các kiểu . 2. Cách trên bị hạng chế do độ trễ của mỗi jobs => jobs A chạy tốn 5s thì jobs B sẽ bị call trễ 5 giây Mình nghĩ ra giải pháp sau bạn góp ý vs nha

  1. cron job chạy 1 phút 1 lần query lấy process nào active = true;
  2. Nếu nó đang được kích hoạt thì mình cho nó vào vòng lặp while với điều kiện bên dưới
  public function shedule(){
    $total = $process->total;
    $result = array(
        'completed ' => 0
        'success' => 0,
        'error' => 0,
    );
    while ( ){ 
      if( Redis::get('active'.$process_id) == flase || $result['completed '] > $total ){
         break;
         $this->jobCompletedCallback($result);         
      }
      try{
          $this->doMyJobs();
          $result['success']++;
       }catch(Exception $e){
          $result['error']++;
       }
       $result['completed ']++;
       $this->saveResult($result);
       sleep(16);         
    }
  }
  public function doMyJobs(){
      // nhiệm vụ con
  }
  public function saveResult($result){
      // lưu tạm kết quả lên redis
  }  
  public function jobCompletedCallback($result){
      // nhiệm vụ xong lưu kết quả vào database và xóa mọi thứ trên redis
  }   
thg 8 10, 2018 11:13 CH

@MinhDuc Có giải pháp thì ta cứ thử thôi 😄 Đáp ứng được yêu cầu là OK. Mình cũng không nắm được gì về yêu cầu của cái bạn đang làm.

Còn về vụ

jobs A chạy tốn 5s thì jobs B sẽ bị call trễ 5 giây

thì đơn giản mình assign job B vào đầu tiến trình chạy job A là xong mà 😄

P.S. Mình chỉ đóng góp ý tưởng thôi, còn không biết gì về PHP và Laravel nhé 😄

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí