PHP - queue process
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:
- Kiểm tra vé còn trống có lớn hơn số vé mà user đó đặt hay không
- 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ỉ ?
1 CÂU TRẢ LỜI
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ểuUNSIGNED
- Mỗi khi select bản ghi trong ticket ra để so sánh
remain_ticket
vớiamount_ticket
trong oder, thay vì dùng câu lệnh select bình thường, em hãy dùngSELECT ... 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ậtremain_ticket
sau đó commit transaction. Như vậy có thể là sẽ ổn Để chắc chắn hơn thì lúc cập nhậtremain_ticket
, emtry ... catch
rồi rollback transaction nếu có lỗi luôn Bởiremain_ticket
có kiểu dữ liệuunsigned
, 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
à. 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....)
@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
Bạn có thể làm rõ cho mình 2 điểm này được 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)
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ó
@thangtd90 Em có 2 bảng đơn giản như sau ạ
order.status
có các giá trị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ánhremain_ticket
vs số vé mà user muốn đặtEm insert số vé & status = 0 vào bảng
order
thôi ạAnh thử cách tạo luôn thêm trường
remain_ticket
bên trong ticket sau đó mìnhlock
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à checkremain_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 ^^