NhaLapTrinh.comNhà Lập Trình
Máy tính chạy thế nào

Hai người cùng bấm mua món hàng cuối cùng thì sao?

Khi hai người cùng mua món hàng cuối, nếu chương trình kiểm tra còn hàng rồi mới trừ kho thành hai bước riêng, cả hai đều có thể thấy còn hàng và cùng mua được. Lập trình viên xử lý bằng cách gộp kiểm tra và trừ kho thành một thao tác không thể chen ngang, hoặc khoá tạm món hàng.

🎙️
📑 Nội dung theo chương
  1. 0:39 Hai bước tưởng là một
  2. 1:30 Gộp lại thành một thao tác
  3. 2:18 Khoá tạm và giữ chỗ
  4. 3:09 Bấm nút hai lần cũng là tranh chấp
  5. 3:58 Vì sao lỗi này khó thấy khi thử?
  6. 4:44 Câu hỏi thường gặp
🔊 Bản audio (lồng nhạc nền)

Khi hai người cùng bấm mua món hàng cuối cùng, nếu chương trình kiểm tra còn hàng rồi mới trừ kho thành hai bước tách rời, cả hai có thể cùng thấy còn một cái và cùng mua thành công. Cửa hàng bán ra hai món trong khi chỉ có một. Lập trình viên phòng bằng cách gộp kiểm tra và trừ kho thành một thao tác không ai chen ngang được, hoặc khoá tạm món hàng khi có người đang mua.

Chào bạn, đây là Nhà Lập Trình. Loại lỗi này có tên trong nghề là tranh chấp, và nó là một trong những lỗi khó tìm nhất, vì khi bạn thử một mình trên máy thì không bao giờ gặp. Nó chỉ xuất hiện khi có nhiều người cùng lúc, thường là đúng vào ngày bán hàng quan trọng nhất.

Hai bước tưởng là một

Hãy hình dung cách một người mới viết chức năng mua hàng. Bước một, đọc số lượng còn trong kho. Bước hai, nếu còn lớn hơn không thì tạo đơn và ghi số lượng mới bằng số cũ trừ một. Nghe hoàn toàn hợp lý.

Giờ có hai người, một chị ở Hà Nội và một anh ở Cần Thơ, bấm mua cùng một lúc. Máy chủ xử lý hai yêu cầu song song. Yêu cầu của chị đọc kho, thấy còn một. Ngay lúc đó, trước khi nó kịp ghi, yêu cầu của anh cũng đọc kho, cũng thấy còn một. Cả hai đi tiếp, cả hai tạo đơn, cả hai ghi kho còn không.

Kết quả: hai đơn hàng, một món hàng. Kho ghi không, nhìn vào tưởng đúng. Chỉ khi nhân viên đi lấy hàng mới phát hiện thiếu.

Khoảng thời gian giữa đọc và ghi có khi chỉ vài phần nghìn giây. Với một cửa hàng ít khách, hai người rơi đúng vào khe đó là rất hiếm. Nhưng vào giờ mở bán vé ca nhạc hay đợt giảm giá lớn, hàng trăm người bấm cùng giây, cái hiếm trở thành chắc chắn.

Gộp lại thành một thao tác

Cách sửa gọn nhất là đừng để có khe hở. Thay vì đọc rồi mới ghi, hãy bảo cơ sở dữ liệu làm cả hai trong một lệnh: trừ số lượng đi một, nhưng chỉ khi số lượng đang lớn hơn không. Cơ sở dữ liệu bảo đảm lệnh này chạy trọn vẹn, không ai chen vào giữa.

Sau lệnh đó, chương trình hỏi lại: có dòng nào được cập nhật không. Nếu có, người này mua được. Nếu không, hàng đã hết, báo cho khách. Khi hai yêu cầu cùng tới, cơ sở dữ liệu xếp chúng lần lượt, người đến sau sẽ thấy lệnh không cập nhật được gì.

Người trong nghề gọi kiểu thao tác trọn vẹn này là nguyên tử, theo nghĩa cũ là không chia nhỏ được. Học nhận ra những chỗ trong chương trình cần thao tác nguyên tử là một bước lớn trong nghề.

Cái hay của cách này là nó không cần mã phức tạp. Nó chỉ cần bạn đổi cách nghĩ: đừng hỏi rồi mới làm, hãy làm có điều kiện rồi xem kết quả.

Khoá tạm và giữ chỗ

Có những trường hợp không gộp được thành một lệnh, vì giữa lúc kiểm tra và lúc chốt có nhiều việc phải làm. Đặt vé máy bay là ví dụ: khách chọn ghế, điền thông tin, thanh toán, mất vài phút. Không thể để ghế trống suốt vài phút đó cho người khác chọn.

Cách làm ở đây là giữ chỗ tạm. Khi khách chọn ghế, hệ thống đánh dấu ghế đang được giữ cho người này trong một khoảng thời gian, chẳng hạn mười phút. Người khác sẽ thấy ghế đó không chọn được. Hết mười phút mà chưa thanh toán thì ghế tự nhả ra.

Cách giữ chỗ cũng có mặt trái. Nếu thời gian giữ quá dài, người mua thật bị chặn bởi người chỉ đang xem. Nếu có kẻ cố tình giữ hết chỗ mà không mua, hệ thống cần giới hạn mỗi người giữ được bao nhiêu.

Việc chọn thời gian giữ bao lâu, giới hạn bao nhiêu là chuyện nghiệp vụ, cần hỏi người vận hành. Người lập trình chỉ cần chắc rằng cơ chế giữ chỗ bản thân nó cũng không bị tranh chấp.

Bấm nút hai lần cũng là tranh chấp

Tranh chấp không chỉ xảy ra giữa hai người. Nó xảy ra với chính một người. Mạng chậm, khách bấm nút thanh toán, không thấy gì, bấm lại lần nữa. Hai yêu cầu cùng đi tới máy chủ. Nếu không cẩn thận, khách bị trừ tiền hai lần.

Làm mờ nút sau khi bấm là cách chặn ở phía người dùng, nhưng không đủ. Mạng có thể tự gửi lại yêu cầu, ứng dụng có thể thử lại khi mất kết nối. Máy chủ phải tự bảo vệ.

Cách phổ biến là gắn cho mỗi lần thanh toán một mã duy nhất, được tạo từ lúc khách mở trang thanh toán. Máy chủ nhận yêu cầu, kiểm tra mã này đã xử lý chưa. Rồi thì trả lại kết quả cũ, chưa thì xử lý. Khách bấm bao nhiêu lần cũng chỉ một đơn.

Tính chất làm một lần hay làm nhiều lần đều ra cùng kết quả này rất quý với mọi chức năng liên quan tới tiền. Nó biến một hệ thống dễ vỡ thành một hệ thống chịu được mạng chập chờn.

Vì sao lỗi này khó thấy khi thử?

Khi bạn thử chương trình một mình, mọi thứ chạy lần lượt, không bao giờ có hai yêu cầu chen nhau. Lỗi tranh chấp nằm im. Nó chỉ lộ ra khi có tải thật, và khi lộ ra thì lại không tái hiện được, vì lần sau thời điểm đã khác.

Để tìm những lỗi này, người ta cố tình tạo tải: viết chương trình giả lập hàng trăm người cùng mua một món, rồi kiểm tra số đơn có khớp số hàng không. Hoặc đọc kỹ mã, tìm những chỗ đọc rồi mới ghi mà không có gì bảo vệ.

Một thói quen tốt là mỗi khi viết mã thay đổi dữ liệu dùng chung, như kho hàng, số dư, số chỗ, hãy tự hỏi: nếu hai người làm việc này cùng lúc thì sao. Câu hỏi đó tốn mười giây và cứu bạn khỏi những đêm gỡ lỗi dài.

Máy tính làm nhiều việc cùng lúc là sức mạnh của nó. Nhưng mỗi khi nhiều việc cùng chạm vào một thứ, bạn phải là người giữ trật tự.

Câu hỏi thường gặp

Bán vượt tồn kho trên trang thương mại điện tử là do đâu?

Thường do chương trình kiểm tra tồn kho và trừ kho thành hai bước tách rời, khiến nhiều người cùng mua được món cuối. Cũng có thể do tồn kho không đồng bộ giữa nhiều kênh bán.

Khoá dữ liệu có làm hệ thống chậm đi không?

Có thể, vì các yêu cầu phải chờ nhau. Nên khoá càng ít dữ liệu và càng ngắn thời gian càng tốt, và ưu tiên thao tác có điều kiện trọn vẹn khi có thể.

Làm sao tránh khách bị trừ tiền hai lần khi bấm thanh toán nhiều lần?

Gắn cho mỗi lần thanh toán một mã duy nhất và để máy chủ kiểm tra mã đó đã được xử lý chưa. Làm mờ nút sau khi bấm cũng giúp, nhưng không thay được việc kiểm tra ở máy chủ.

Lỗi tranh chấp có hay gặp ở ứng dụng nhỏ không?

Ít gặp hơn nhưng vẫn có, nhất là với nút bấm hai lần và các tác vụ chạy nền. Tập thói quen tự hỏi nếu hai việc chạy cùng lúc thì sao từ sớm sẽ đỡ rất nhiều về sau.

Thử nghiệm lỗi tranh chấp thế nào?

Viết chương trình giả lập nhiều người cùng thực hiện một thao tác trên cùng dữ liệu, rồi kiểm tra kết quả có đúng không. Đọc mã tìm những chỗ đọc rồi mới ghi dữ liệu dùng chung cũng rất hiệu quả.

Cần tư vấn cụ thể cho trường hợp của bạn?

Chúng tôi sẽ liên hệ trong vòng 24 giờ.

Đăng ký tư vấn ngay

Bài viết liên quan

Bạn cần hỗ trợ thêm?

Để lại thông tin, chúng tôi sẽ liên hệ trong 24 giờ.