Yêu cầu thg 1 24, 2018 7:44 SA 359 1 1
  • 359 1 1
+4

PHP - queue process

Chia sẻ
  • 359 1 1

Mình có bài toán như sau 1 ca chiếu phim hiện còn 10 vé Có 2 user cùng book ca đó. Mỗi user book 8 vé Expect: chỉ có 1 user được book thành công. 1 user sẽ được thông báo hết vé Code mình xử lí như sau:

  1. Kiểm tra vé còn trống có lớn hơn số vé mà user đó đặt hay không
  2. Nếu (1) thỏa mãn thì sẽ insert vào DB

Mình có test 2 user gửi request lần lượt thì kết quả như expect Nhưng mình dùng request của Nodejs. Gửi 2 request ĐỒNG THỜI thì 2 user đó đều book được vé thành công

Có cách nào xử lí được vấn đề này không nhỉ ?

Avatar Tran Duc Thang @thangtd90
thg 1 25, 2018 1:33 SA

Bạn có thể làm rõ cho mình 2 điểm này được không 😃

Kiểm tra vé còn trống có lớn hơn số vé mà user đó đặt hay không

Bạn nào thế nào để có thể Kiểm tra vé còn trống có lớn hơn số vé mà user đó đặt hay không? (cấu trúc DB thế nào, bạn dùng câu lệnh gì để check)

Nếu (1) thỏa mãn thì sẽ insert vào DB

Bạn insert thông tin gì, vào bảng gì?

tại cũng chưa biết cấu trúc DB, cũng như cách thức bạn query nên để suggest câu trả lời cụ thể cũng hơi khó 😂

Avatar Truong Bui @d10cn2btt
thg 1 25, 2018 2:21 SA

@thangtd90 Em có 2 bảng đơn giản như sau ạ order.status có các giá trị

    const STATUS_DRAFT = 0;
    const STATUS_PAYMENT_FAILURE = 1;
    const STATUS_PAYMENT_SUCCESS = 2;
    const STATUS_CANCEL = 3;

Kiểm tra vé còn trống có lớn hơn số vé mà user đó đặt hay không

Em lấy ticket.total_ticket - (sum(order.amount_ticket ( chỉ tính những order mà có status = 0, 2 thôi ))) = remain_ticket Sau đó so sánh remain_ticket vs số vé mà user muốn đặt

Nếu (1) thỏa mãn thì sẽ insert vào DB

Em insert số vé & status = 0 vào bảng order thôi ạ

Avatar Le Xuan Duy @telosma
thg 1 26, 2018 6:50 SA

Anh thử cách tạo luôn thêm trường remain_ticket bên trong ticket sau đó mình lock trường đó lại? Khi mình ghi mình sẽ check một biến dạng như "số lần update" của bản ghi này chẳng hạn. Khi đọc dữ liệu ra thì check lúc update và lúc ghi thì "số lần update" này có bằng nhau không? Nếu bằng nhau chứng tỏ quá trình đọc và ghi của tác vụ này không bị tác vụ nào khác ghi vào trước, để tránh trường hợp 2 tác vụ cùng đọc một lúc nhưng ghi chỉ được một mà thôi, còn nếu khác nhau thì nghĩa là đã có thằng ghi vào rồi, nếu mình ghi tiếp thì sẽ không đúng cần roll back lại và check remain_ticket Ý tưởng em nghĩ như vậy còn cái số lần support kia thì em nghĩ mỗi framework hay database sẽ có hỗ trợ sẵn, cụ thể thì em không rõ lắm ^^

1 CÂU TRẢ LỜI


Đã trả lời thg 1 26, 2018 7:41 SA
Đã được chấp nhận
+1

Em thử cách này xem sao 🤔

  • Thêm trường remain_ticket vào bảng ticket, lưu số lượng ticket còn lại. Trường này sẽ được cập nhật mỗi khi có một order thành công, và là kiểu UNSIGNED
  • Mỗi khi select bản ghi trong ticket ra để so sánh remain_ticket với amount_ticket trong oder, thay vì dùng câu lệnh select bình thường, em hãy dùng SELECT ... FOR UPDATE. Câu lệnh này sẽ lock bản ghi lại, không cho phép transaction khác đọc, cho đến khi em update dữ liệu rồi commit transaction của mình.
  • Ở trong code, em start transaction, select remain_ticket, so sánh rồi cập nhật remain_ticket sau đó commit transaction. Như vậy có thể là sẽ ổn 🤔 Để chắc chắn hơn thì lúc cập nhật remain_ticket, em try ... catch rồi rollback transaction nếu có lỗi luôn 😂 Bởi remain_ticket có kiểu dữ liệu unsigned, mysql sẽ báo lỗi khi nó bị trừ xuống còn âm mà.

Em có thể tham khảo https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html https://dev.mysql.com/doc/refman/5.7/en/select.html https://stackoverflow.com/questions/32827650/mysql-innodb-difference-between-for-update-and-lock-in-share-mode

Chia sẻ
Avatar Truong Bui @d10cn2btt
thg 1 26, 2018 8:09 SA

à. Em quên không note Nếu order có status là draft (đặt tạm) thì nó chỉ được tính trong vòng 10 phút thôi VD: Mình có 10 vé. 1 user đặt tạm 8 vé lúc 15:00 & không thanh toán Từ 15:00:00 - 15:09:59 thì user khác chỉ được đặt tối đa 2 vé Từ 15:10:00 thì số vé sẽ được khôi phục lại là 10 vé

Nên em nghĩ không lưu remain_ticket được ạ

À. Còn cái SELECT ... FOR UPDATE thì khi 1 user đặt vé thì những user khác sẽ không thể thao tác được với bảng ticket này (xem số vé, view....)

Avatar Tran Duc Thang @thangtd90
thg 1 26, 2018 9:19 SA

@d10cn2btt Em chỉ cần làm một cái batch, cứ mỗi một phút check danh sách các order có status là draft một lần, nếu thời gian quá 10 phút thì em chuyển sang expired, và tăng lại số lượng remain_ticket lên 🤔

À. Còn cái SELECT ... FOR UPDATE thì khi 1 user đặt vé thì những user khác sẽ không thể thao tác được với bảng ticket này (xem số vé, view....)

Nó chỉ lock trong khi cái transaction của em đang chạy thôi chứ em, em chạy 2, 3 câu lệnh SQL thì nó hết mấy mili giây đâu mà lo những user khác không thể thao tác được 😄

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í