Làm thế nào để ngăn chặn điều kiện chủng tộc trong một ứng dụng web?


31

Hãy xem xét một trang web thương mại điện tử, nơi Alice và Bob đều đang chỉnh sửa danh sách sản phẩm. Alice đang cải thiện mô tả, trong khi Bob đang cập nhật giá. Họ bắt đầu chỉnh sửa Acme Wonder Widget cùng một lúc. Bob hoàn thành đầu tiên và tiết kiệm sản phẩm với giá mới. Alice mất nhiều thời gian hơn để cập nhật mô tả và khi hoàn thành, cô lưu sản phẩm với mô tả mới. Thật không may, cô cũng ghi đè lên giá với giá cũ, không có ý định.

Theo kinh nghiệm của tôi, những vấn đề này cực kỳ phổ biến trong các ứng dụng web. Một số phần mềm (ví dụ phần mềm wiki) có bảo vệ chống lại điều này - thường thì lần lưu thứ hai không thành công với "trang đã được cập nhật trong khi bạn đang chỉnh sửa". Nhưng hầu hết các trang web không có sự bảo vệ này.

Điều đáng chú ý là bản thân các phương thức của bộ điều khiển đều an toàn cho luồng. Thông thường họ sử dụng các giao dịch cơ sở dữ liệu, điều này làm cho chúng an toàn theo nghĩa là nếu Alice và Bob cố gắng lưu vào cùng một thời điểm chính xác, điều đó sẽ không gây ra tham nhũng. Điều kiện cuộc đua phát sinh từ Alice hoặc Bob có dữ liệu cũ trong trình duyệt của họ.

Làm thế nào chúng ta có thể ngăn chặn các điều kiện chủng tộc như vậy? Đặc biệt, tôi muốn biết:

  • Những kỹ thuật có thể được sử dụng? ví dụ theo dõi thời gian thay đổi cuối cùng Những ưu và nhược điểm của mỗi.
  • Trải nghiệm người dùng hữu ích là gì?
  • Những khung nào có sự bảo vệ này được xây dựng trong?

Bạn đã đưa ra câu trả lời: bằng cách theo dõi ngày thay đổi của các đối tượng và so sánh nó với tuổi của dữ liệu mà các thay đổi khác cố gắng cập nhật. Bạn có muốn biết một cái gì đó khác, ví dụ như làm thế nào để làm điều đó một cách hiệu quả?
Kilian Foth

@KilianFoth - Tôi đã thêm một số thông tin về những gì tôi đặc biệt muốn biết
pyjama 25/11/14

1
Câu hỏi của bạn không có gì đặc biệt đối với các ứng dụng web, các ứng dụng máy tính để bàn có thể có cùng một vấn đề. Các chiến lược giải pháp điển hình được mô tả ở đây: stackoverflow.com/questions/129329/ cấp
Doc Brown

2
FYI, hình thức khóa mà bạn đề cập trong câu hỏi của bạn được gọi là " kiểm soát tương tranh lạc quan "
TehShrike

Một số cuộc thảo luận liên quan đến Django tại đây
pyjama 26/11/14

Câu trả lời:


17

Bạn cần "đọc bài viết của mình", có nghĩa là trước khi bạn viết ra một thay đổi, bạn cần đọc lại bản ghi và kiểm tra xem có bất kỳ thay đổi nào được thực hiện kể từ lần cuối bạn đọc nó không. Bạn có thể thực hiện trường này theo trường (hạt mịn) hoặc dựa trên dấu thời gian (hạt thô). Trong khi bạn thực hiện kiểm tra này, bạn cần một khóa độc quyền trong hồ sơ. Nếu không có thay đổi nào được thực hiện, bạn có thể ghi lại các thay đổi của mình và giải phóng khóa. Nếu hồ sơ đã thay đổi trong thời gian đó, bạn hủy bỏ giao dịch, mở khóa và thông báo cho người dùng.


Điều này nghe có vẻ như cách tiếp cận thực tế nhất. Bạn có biết bất kỳ khuôn khổ nào thực hiện điều này? Tôi nghĩ vấn đề lớn nhất với lược đồ này là một thông báo "chỉnh sửa xung đột" đơn giản sẽ khiến người dùng thất vọng, nhưng cố gắng hợp nhất các thay đổi (thủ công hoặc tự động) là khó khăn.
pyj28

Thật không may, tôi không biết bất kỳ khuôn khổ nào hỗ trợ điều này. Tôi không nghĩ rằng một thông báo lỗi chỉnh sửa sẽ được coi là gây khó chịu, miễn là nó không xuất hiện thường xuyên. Cuối cùng, tùy thuộc vào tải người dùng của hệ thống, bạn chỉ cần kiểm tra dấu thời gian hoặc thực hiện chức năng hợp nhất phức tạp hơn.
Phil

Tôi đã duy trì một sản phẩm cơ sở dữ liệu phân tán trên PC sử dụng cách tiếp cận chi tiết (so với bản sao cơ sở dữ liệu cục bộ của nó): nếu một người dùng thay đổi giá và người kia thay đổi mô tả - không vấn đề gì! Như là đời thật. Nếu hai người dùng thay đổi giá - Người dùng thứ 2 sẽ nhận được lời xin lỗi và thử lại thay đổi của họ. Không vấn đề gì! Điều này không yêu cầu khóa ngoại trừ trong thời điểm dữ liệu được ghi vào cơ sở dữ liệu. Sẽ không có vấn đề gì nếu một người dùng đi ăn trưa trong khi thay đổi của họ xuất hiện trên màn hình và gửi nó sau. Đối với thay đổi cơ sở dữ liệu từ xa, nó dựa trên dấu thời gian kỷ lục.

1
Dataflex có một hàm gọi là "đọc lại ()", thực hiện những gì bạn mô tả. Trong các phiên bản sau, nó an toàn trong môi trường nhiều người dùng. Và, thực sự, đó là cách duy nhất để các bản cập nhật xen kẽ như vậy hoạt động.

bạn có thể cho một ví dụ về làm thế nào để làm điều này với máy chủ sql \?
l --''''''--------- '' '' '' '' '' ''

10

Tôi đã thấy 2 cách chính:

  1. Thêm dấu thời gian của bản cập nhật cuối cùng của trang mà việc sử dụng đang chỉnh sửa trong một đầu vào ẩn. Khi cam kết dấu thời gian được kiểm tra so với dấu thời gian hiện tại và nếu chúng không khớp thì nó đã được người khác cập nhật và trả về lỗi.

    • pro: nhiều người dùng có thể chỉnh sửa các phần khác nhau của trang. Trang lỗi có thể dẫn đến một trang khác trong đó người dùng thứ hai có thể hợp nhất các thay đổi của anh ta trong trang mới.

    • con: đôi khi phần lớn nỗ lực bị lãng phí trong các chỉnh sửa đồng thời lớn.

  2. Khi người dùng bắt đầu chỉnh sửa khóa trang trong một khoảng thời gian hợp lý, khi một người dùng khác sau đó cố gắng chỉnh sửa, anh ta nhận được một trang lỗi và phải đợi cho đến khi khóa hết hạn hoặc người dùng đầu tiên đã cam kết.

    • pro: chỉnh sửa những nỗ lực không bị lãng phí.

    • con: một người dùng vô đạo đức có thể khóa một trang vô thời hạn. Một trang có khóa hết hạn vẫn có thể cam kết trừ khi được xử lý khác (sử dụng kỹ thuật 1)


7

Sử dụng Kiểm soát đồng thời lạc quan .

Thêm một cột versionNumber hoặc versionTimestamp vào bảng được đề cập (số nguyên là an toàn nhất).

Người dùng 1 đọc bản ghi:

{id:1, status:unimportant, version:5}

Người dùng 2 đọc bản ghi:

{id:1, status:unimportant, version:5}

Người dùng 1 lưu bản ghi, điều này làm tăng phiên bản:

save {id:1, status:important, version:5}
new value {id:1, status:important, version:6}

Người dùng 2 cố lưu bản ghi họ đọc:

save {id:1, status:unimportant, version:5}
ERROR

Hibernate / JPA có thể thực hiện việc này tự động với @Versionchú thích

Bạn cần duy trì trạng thái của bản ghi đã đọc ở đâu đó, thường là trong phiên (điều này an toàn hơn trong một biến dạng ẩn).


Cảm ơn ... đặc biệt hữu ích khi biết về @Version. Một câu hỏi: tại sao nó là két để lưu trữ trạng thái trong phiên? Trong trường hợp đó, tôi lo lắng rằng việc sử dụng nút quay lại có thể gây nhầm lẫn.
pyjama

Phiên an toàn hơn một yếu tố hình thức ẩn vì người dùng sẽ không thể thay đổi giá trị phiên bản. Nếu đó không phải là một mối quan tâm, thì hãy bỏ qua phần về phiên
Neil McGuigan

Kỹ thuật này được gọi là ẩn khóa lạc quan và nó là trong SQLAlchemy cũng
paj28

@ py28 - liên kết SQLAlchemyđó không chỉ ra bất cứ điều gì về các khóa ngoại tuyến lạc quan và tôi không thể tìm thấy nó trong các tài liệu. Bạn đã có một liên kết hữu ích hơn hay chỉ trỏ mọi người vào SQLAlchemy nói chung?
lùn

@dwanderson Ý tôi là phiên bản truy cập một phần của liên kết đó.
pyj28

1

Một số hệ thống Ánh xạ quan hệ đối tượng (ORM) sẽ phát hiện trường nào của đối tượng đã thay đổi kể từ khi được tải từ cơ sở dữ liệu và sẽ xây dựng câu lệnh cập nhật SQL để chỉ đặt các giá trị đó. ActiveRecord cho Ruby on Rails là một ORM như vậy.

Ảnh hưởng ròng là các trường mà người dùng không thay đổi không được bao gồm trong lệnh CẬP NHẬT được gửi đến cơ sở dữ liệu. Những người cập nhật các lĩnh vực khác nhau cùng một lúc không ghi đè lên các thay đổi của nhau.

Tùy thuộc vào ngôn ngữ lập trình bạn đang sử dụng, nghiên cứu các ORM nào khả dụng và xem liệu bất kỳ ngôn ngữ nào trong số chúng sẽ chỉ cập nhật các cột trong cơ sở dữ liệu được đánh dấu là "bẩn" trong ứng dụng của bạn.


Xin chào Greg. Thật không may, điều này không giúp với các loại điều kiện cuộc đua. Nếu bạn xem xét ví dụ ban đầu của tôi, khi Alice lưu, ORM sẽ thấy cột giá là bẩn và cập nhật nó - mặc dù bản cập nhật không mong muốn.
pyj28

1
@ py28 Điểm mấu chốt trong câu trả lời của Greg là " các trường người dùng không thay đổi ". Alice đã không thay đổi giá, vì vậy ORM sẽ không cố lưu giá trị "giá" vào cơ sở dữ liệu.
Ross Patterson

@RossPatterson - Làm thế nào để ORM biết sự khác biệt giữa các trường mà người dùng đã thay đổi và dữ liệu cũ từ trình duyệt? Nó không, ít nhất là không thực hiện thêm một số theo dõi. Nếu bạn muốn chỉnh sửa câu trả lời của Greg để bao gồm theo dõi như vậy hoặc gửi câu trả lời khác, điều đó sẽ hữu ích.
pyjama

@ Paj28 một số phần của hệ thống phải biết người dùng đã làm gì và chỉ lưu trữ những thay đổi mà người dùng đã thực hiện. Nếu người dùng thay đổi giá, sau đó thay đổi lại, sau đó gửi, thì điều này không được tính là "thứ gì đó mà người dùng đã thay đổi", vì họ đã không làm vậy. Nếu bạn có một hệ thống yêu cầu mức độ kiểm soát tương tranh này, bạn phải xây dựng nó theo cách này. Nếu không, không.

@nocomprende - Một số phần, chắc chắn - nhưng không phải ORM như câu trả lời này nói
pyj28
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.