2008-11-18 30 views
74

Tôi cần thực hiện các giao dịch (bắt đầu, cam kết hoặc quay lại), khóa (chọn để cập nhật). Làm thế nào tôi có thể làm điều đó trong một mô hình tài liệu db?Tôi có thể thực hiện các giao dịch và khóa trong CouchDB không?

Edit:

trường hợp là như thế này:

  • Tôi muốn chạy một trang web đấu giá.
  • Và tôi cũng nghĩ cách mua hàng trực tiếp.
  • Khi mua hàng trực tiếp, tôi phải giảm trường số lượng trong bản ghi mặt hàng, nhưng chỉ khi số lượng lớn hơn 0. Đó là lý do tại sao tôi cần khóa và giao dịch.
  • Tôi không biết cách giải quyết điều đó mà không có khóa và/hoặc giao dịch.

Tôi có thể giải quyết vấn đề này với CouchDB không?

Trả lời

132

No. CouchDB sử dụng mô hình "đồng thời lạc quan". Trong điều kiện đơn giản nhất, điều này chỉ có nghĩa là bạn gửi một phiên bản tài liệu cùng với bản cập nhật của bạn và CouchDB từ chối thay đổi nếu phiên bản tài liệu hiện tại không khớp với những gì bạn đã gửi.

Thật đơn giản, thực sự. Bạn có thể định lại nhiều kịch bản giao dịch bình thường dựa trên CouchDB. Tuy nhiên, bạn cần phải loại bỏ kiến ​​thức miền RDBMS của mình khi học CouchDB. Sẽ rất hữu ích khi tiếp cận các vấn đề từ cấp độ cao hơn, thay vì cố gắng đưa Couch vào một thế giới dựa trên SQL.

Theo dõi hàng tồn kho

Vấn đề bạn nêu chủ yếu là một vấn đề hàng tồn kho. Nếu bạn có một tài liệu mô tả một mục, và nó bao gồm một lĩnh vực cho "số lượng có sẵn", bạn có thể xử lý vấn đề đồng thời như thế này:

  1. Lấy tài liệu, hãy lưu ý những _rev tài sản đó CouchDB gửi cùng
  2. Decrement lĩnh vực số lượng, nếu nó lớn hơn không
  3. Gửi tài liệu được cập nhật trở lại, sử dụng tài sản _rev
  4. Nếu _rev phù hợp với số lượng hiện lưu trữ, được thực hiện!
  5. Nếu có một cuộc xung đột (khi _rev không phù hợp), lấy phiên bản tài liệu mới nhất

Trong trường hợp này, có hai kịch bản thất bại có thể nghĩ đến. Nếu phiên bản tài liệu gần đây nhất có số lượng là 0, bạn xử lý nó giống như bạn làm trong RDBMS và cảnh báo người dùng rằng họ không thể mua những gì họ muốn mua. Nếu phiên bản tài liệu gần đây nhất có số lượng lớn hơn 0, bạn chỉ cần lặp lại thao tác với dữ liệu đã cập nhật và bắt đầu lại từ đầu. Điều này buộc bạn phải làm công việc nhiều hơn một chút so với RDBMS, và có thể hơi khó chịu nếu có những cập nhật mâu thuẫn thường xuyên.

Bây giờ, câu trả lời tôi vừa đưa ra giả định rằng bạn sẽ làm mọi thứ trong CouchDB theo cùng cách mà bạn sẽ làm trong RDBMS.Tôi có thể tiếp cận vấn đề này hơi khác:

Tôi bắt đầu với tài liệu "sản phẩm chính" bao gồm tất cả dữ liệu mô tả (tên, hình ảnh, mô tả, giá, v.v ...). Sau đó, tôi sẽ thêm tài liệu "vé khoảng không quảng cáo" cho từng trường hợp cụ thể, với các trường cho product_keyclaimed_by. Nếu bạn đang bán một mô hình búa và có 20 mẫu để bán, bạn có thể có tài liệu có các phím như hammer-1, hammer-2, v.v., để đại diện cho mỗi cái búa có sẵn.

Sau đó, tôi sẽ tạo chế độ xem cung cấp cho tôi danh sách các búa có sẵn, với chức năng giảm cho phép tôi xem "tổng". Đây là hoàn toàn tắt cuff, nhưng nên cung cấp cho bạn một ý tưởng về một cái nhìn làm việc sẽ như thế nào.

Bản đồ

function(doc) 
{ 
    if (doc.type == 'inventory_ticket' && doc.claimed_by == null) { 
     emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev }); 
    } 
} 

này mang lại cho tôi một danh sách có sẵn "vé", bởi mã khóa sản phẩm. Tôi có thể lấy một nhóm này khi ai đó muốn mua một cái búa, sau đó lặp lại thông qua việc gửi các bản cập nhật (sử dụng id_rev) cho đến khi tôi xác nhận thành công một (vé được xác nhận quyền sở hữu trước đó sẽ dẫn đến lỗi cập nhật).

Giảm

function (keys, values, combine) { 
    return values.length; 
} 

này làm giảm chức năng chỉ đơn giản trả về tổng số người nhận inventory_ticket mục, vì vậy bạn có thể nói có bao nhiêu "búa" có sẵn để mua.

Hãy cẩn thận

Giải pháp này đại diện cho khoảng 3,5 phút trong tổng số suy nghĩ cho vấn đề cụ thể mà bạn đã trình bày. Có thể có cách tốt hơn để làm điều này! Điều đó nói rằng, nó làm giảm đáng kể các cập nhật xung đột và cắt giảm nhu cầu phản hồi xung đột với bản cập nhật mới. Trong mô hình này, bạn sẽ không có nhiều người dùng cố gắng thay đổi dữ liệu trong mục nhập sản phẩm chính. Ở mức rất tệ nhất, bạn sẽ có nhiều người dùng cố gắng yêu cầu một vé duy nhất và nếu bạn đã nắm lấy một số người trong số lượt xem của mình, bạn chỉ cần chuyển sang vé tiếp theo và thử lại.

Tham chiếu: https://wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F

+4

Nó không rõ ràng với tôi như thế nào có 'vé' mà bạn cố gắng yêu cầu bồi thường theo thứ tự là một cải tiến đáng kể hơn chỉ đơn giản là thử lại đọc/sửa đổi/ghi để cập nhật thực thể chủ. Chắc chắn nó không có vẻ giá trị thêm chi phí, đặc biệt là nếu bạn có một lượng lớn cổ phiếu. –

+4

Theo quan điểm của tôi, quy ước về vé là "đơn giản hơn" để xây dựng. Cập nhật không thành công trên mục nhập chính yêu cầu bạn phải tải lại tài liệu, thực hiện lại thao tác của bạn và sau đó lưu. Điều vé cho phép bạn thử và "yêu cầu" điều gì đó mà không phải yêu cầu thêm dữ liệu. – MrKurt

+0

Ngoài ra, nó phụ thuộc vào loại phí mà bạn đang lo lắng.Bạn sẽ chiến đấu với sự tranh cãi gia tăng hoặc có thêm yêu cầu bộ nhớ. Cho rằng một vé cũng có thể tăng gấp đôi như một bản ghi mua hàng, tôi không biết rằng sẽ có nhiều vấn đề về lưu trữ như bạn nghĩ. – MrKurt

24

Mở rộng câu trả lời của MrKurt. Đối với nhiều kịch bản bạn không cần phải mua lại cổ phiếu theo thứ tự. Thay vì chọn vé đầu tiên, bạn có thể chọn ngẫu nhiên từ các vé còn lại. Với số lượng vé lớn và một số lượng lớn các yêu cầu đồng thời, bạn sẽ nhận được nhiều tranh cãi về những vé đó, so với tất cả mọi người đang cố gắng để có được vé đầu tiên.

3

Thực ra, bạn có thể theo cách nào đó. Hãy xem HTTP Document API và cuộn xuống tiêu đề "Sửa đổi nhiều tài liệu với một yêu cầu duy nhất".

Về cơ bản, bạn có thể tạo/cập nhật/xóa một loạt tài liệu trong một yêu cầu gửi đến URI/{dbname}/_ bulk_docs và tất cả sẽ thành công hoặc không thành công. Tuy nhiên, tài liệu lưu ý rằng hành vi này có thể thay đổi trong tương lai.

EDIT: Như đã dự đoán, từ phiên bản 0.9, tài liệu hàng loạt không còn hoạt động theo cách này nữa.

+0

Điều đó sẽ không thực sự giúp đỡ trong tình huống đang được thảo luận, tức là tranh chấp trên các tài liệu đơn lẻ từ nhiều người dùng. – Kerr

+3

Bắt đầu với CouchDB 0.9, ngữ nghĩa của cập nhật hàng loạt đã thay đổi. –

19

Mẫu thiết kế cho các giao dịch còn lại là tạo ra "sự căng thẳng" trong hệ thống.Đối với trường hợp sử dụng ví dụ phổ biến của giao dịch tài khoản ngân hàng, bạn phải đảm bảo cập nhật tổng số cho cả hai tài khoản có liên quan:

  • Tạo tài liệu giao dịch "chuyển USD 10 từ tài khoản 11223 đến tài khoản 88733". Điều này tạo ra sự căng thẳng trong hệ thống.
  • Để giải quyết bất kỳ căng thẳng quét cho tất cả các tài liệu giao dịch và
    • Nếu tài khoản nguồn không được cập nhật chưa cập nhật các tài khoản nguồn (-10 USD)
    • Nếu tài khoản nguồn đã được cập nhật nhưng tài liệu giao dịch không hiển thị điều này sau đó cập nhật tài liệu giao dịch (ví dụ: đặt cờ "sourcedone" trong tài liệu)
    • Nếu tài khoản đích chưa được cập nhật, hãy cập nhật tài khoản đích (+10 USD)
    • Nếu tài khoản đích đã được cập nhật nhưng giao dịch tài liệu không hiển thị điều này, sau đó cập nhật tài liệu giao dịch
    • Nếu cả hai tài khoản đã được cập nhật, bạn có thể xóa tài liệu giao dịch hoặc giữ tài khoản để kiểm tra.

Quá trình quét cho căng thẳng nên được thực hiện trong một quá trình phụ trợ cho tất cả các "tài liệu căng thẳng" để giữ thời gian của sự căng thẳng trong hệ thống ngắn. Trong ví dụ trên, sẽ có một sự thiếu nhất quán trong thời gian ngắn khi tài khoản đầu tiên được cập nhật nhưng tài khoản thứ hai chưa được cập nhật. Điều này phải được đưa vào tài khoản giống như cách bạn sẽ đối phó với sự nhất quán cuối cùng nếu Couchdb của bạn được phân phối.

Một triển khai khác có thể tránh sự cần thiết phải giao dịch hoàn toàn: chỉ lưu trữ tài liệu căng thẳng và đánh giá trạng thái hệ thống của bạn bằng cách đánh giá mọi tài liệu liên quan đến độ căng. Trong ví dụ trên, điều này có nghĩa là tổng số tài khoản chỉ được xác định là tổng giá trị trong tài liệu giao dịch mà tài khoản này có liên quan. Trong Couchdb bạn có thể mô hình hóa điều này rất độc đáo như một khung nhìn bản đồ/giảm.

+4

Nhưng còn những trường hợp tài khoản bị ghi nợ nhưng tài liệu căng thẳng không bị thay đổi? Bất kỳ kịch bản thất bại nào giữa hai điểm đó, nếu chúng không phải là nguyên tử, sẽ gây ra sự mâu thuẫn vĩnh viễn, đúng không? Một cái gì đó về quá trình này phải là nguyên tử, đó là điểm của một giao dịch. –

+0

Có, bạn đúng, trong trường hợp này - trong khi căng thẳng không được giải quyết - sẽ có sự mâu thuẫn. Tuy nhiên sự mâu thuẫn này chỉ là tạm thời cho đến khi lần quét tiếp theo cho các tài liệu căng thẳng phát hiện ra điều này. Đó là thương mại của trường hợp này, một loại sự nhất quán cuối cùng về thời gian. Miễn là bạn đã suy giảm nguồn tài nguyên đầu tiên và sau đó tăng tài khoản đích, điều này có thể chấp nhận được. Nhưng hãy cẩn thận: tài liệu căng thẳng sẽ không cung cấp cho bạn các giao dịch ACID trên đầu trang của REST. Nhưng chúng có thể là một sự cân bằng tốt giữa REST thuần khiết và ACID. – ordnungswidrig

+3

Hãy tưởng tượng mọi tài liệu căng thẳng đều có dấu thời gian và tài liệu tài khoản có trường 'áp dụng cuối cùng' - hoặc danh sách các căng thẳng được áp dụng.Khi bạn ghi nợ tài khoản nguồn, bạn cũng cập nhật trường 'áp dụng cuối cùng'. Hai hoạt động này là nguyên tử vì chúng nằm trên cùng một tài liệu. Tài khoản đích cũng có một trường tương tự. Bằng cách đó, hệ thống luôn có thể cho biết tài liệu căng thẳng nào đã được áp dụng cho tài khoản nào. –

4

Không, CouchDB thường không thích hợp cho các ứng dụng giao dịch vì nó không hỗ trợ các hoạt động nguyên tử trong môi trường nhóm/nhân rộng.

CouchDB đã hy sinh khả năng giao dịch có lợi cho khả năng mở rộng. Để có các hoạt động nguyên tử, bạn cần một hệ thống điều phối trung tâm, điều này hạn chế khả năng mở rộng của bạn. Nếu bạn có thể đảm bảo bạn chỉ có một cá thể CouchDB hoặc mọi người sửa đổi một tài liệu cụ thể kết nối với cùng một cá thể CouchDB thì bạn có thể sử dụng hệ thống phát hiện xung đột để tạo ra một loại nguyên tử bằng các phương pháp được mô tả ở trên. lên đến một cụm hoặc sử dụng một dịch vụ được lưu trữ như Cloudant, nó sẽ bị hỏng và bạn sẽ phải làm lại phần đó của hệ thống.

Vì vậy, đề xuất của tôi sẽ là sử dụng một cái gì đó khác với CouchDB cho số dư tài khoản của bạn, nó sẽ dễ dàng hơn theo cách đó.

5

Để đáp lại sự cố của OP, Couch có lẽ không phải là lựa chọn tốt nhất ở đây. Sử dụng chế độ xem là một cách tuyệt vời để theo dõi khoảng không quảng cáo, nhưng việc kẹp tới 0 ít nhiều là không thể. Vấn đề là điều kiện chủng tộc khi bạn đọc kết quả của một khung nhìn, quyết định bạn đang sử dụng một mục "hammer-1", và sau đó viết một tài liệu để sử dụng nó. Vấn đề là không có cách nào nguyên tử để chỉ viết tài liệu để sử dụng búa nếu kết quả của khung nhìn là có> 0 hammer-1. Nếu 100 người dùng tất cả truy vấn xem cùng một lúc và xem 1 hammer-1, tất cả họ có thể viết một tài liệu để sử dụng một cái búa 1, kết quả là -99 hammer-1's. Trong thực tế, điều kiện chủng tộc sẽ khá nhỏ - thực sự nhỏ nếu DB của bạn đang chạy localhost.Nhưng khi bạn mở rộng quy mô và có một máy chủ hoặc cụm máy chủ DB ngoài trang web, sự cố sẽ trở nên đáng chú ý hơn nhiều. Bất kể, nó không thể chấp nhận để có một điều kiện chủng tộc của loại đó trong một hệ thống quan trọng - tiền liên quan.

Bản cập nhật để đáp ứng MrKurt của (nó có thể chỉ được ngày, hoặc anh ta có thể đã không biết về một số tính năng CouchDB)

Một quan điểm là một cách tốt để xử lý những thứ như số dư/hàng tồn kho trong CouchDB.

Bạn không cần phát ra tài liệu và xoay vòng trong chế độ xem. Bạn nhận được cả hai trong số đó miễn phí khi bạn truy xuất kết quả xem. Phát ra chúng - đặc biệt là ở định dạng tiết như từ điển - sẽ chỉ tăng lượt xem của bạn một cách không cần thiết lớn.

Một cái nhìn đơn giản để theo dõi số dư hàng tồn kho nên trông như thế này (cũng ra khỏi đỉnh đầu của tôi)

function(doc) 
{ 
    if(doc.InventoryChange != undefined) { 
     for(product_key in doc.InventoryChange) { 
      emit(product_key, 1); 
     } 
    } 
} 

Và chức năng giảm thậm chí còn đơn giản hơn

_sum 

này sử dụng một built in reduce function chỉ tổng hợp các giá trị của tất cả các hàng bằng các phím phù hợp.

Trong chế độ xem này, mọi tài liệu đều có thể có thành viên "InventoryChange" ánh xạ product_key thành thay đổi trong tổng số khoảng không quảng cáo của chúng. I E.

{ 
    "_id": "abc123", 
    "InventoryChange": { 
     "hammer_1234": 10, 
     "saw_4321": 25 
    } 
} 

Sẽ thêm 10 hammer_1234 và 25 saw_4321.

{ 
    "_id": "def456", 
    "InventoryChange": { 
     "hammer_1234": -5 
    } 
} 

Sẽ ghi 5 búa từ khoảng không quảng cáo.

Với mô hình này, bạn sẽ không bao giờ cập nhật bất kỳ dữ liệu nào, chỉ phụ thêm. Điều này có nghĩa là không có cơ hội để cập nhật xung đột. Tất cả các vấn đề giao dịch khi cập nhật dữ liệu biến mất :)

Một điều thú vị khác về mô hình này là bất kỳ tài liệu nào trong DB đều có thể thêm và trừ các mục từ khoảng không quảng cáo. Các tài liệu này có thể có tất cả các loại dữ liệu khác trong chúng. Bạn có thể có một tài liệu "Lô hàng" với một loạt dữ liệu về ngày và giờ nhận được, nhà kho, nhân viên tiếp nhận, v.v. và miễn là tài liệu đó định nghĩa một InventoryChange, nó sẽ cập nhật khoảng không quảng cáo. Như một tài liệu "Bán", và một tài liệu "DamagedItem" vv Nhìn vào mỗi tài liệu, họ đọc rất rõ ràng. Và chế độ xem xử lý tất cả công việc khó khăn.

+0

Chiến lược thú vị. Như là một newb CouchDB nó sẽ xuất hiện để tính toán số lượng hiện tại của búa, bạn cần phải thực hiện một bản đồ/giảm trên toàn bộ lịch sử của công ty * thay đổi hàng tồn kho cho búa. Đây có thể là những thay đổi đáng giá trong nhiều năm. Có một số tính năng tích hợp của CouchDB mà sẽ làm cho người biểu diễn này? – chadrik

+0

Có, các khung nhìn trong CouchDB giống như một bản đồ liên tục/liên tục/giảm. Bạn chính xác rằng để làm điều đó bắt đầu từ đầu trên một tập dữ liệu lớn sẽ mất độ tuổi, nhưng khi các tài liệu mới được thêm vào, chúng chỉ cập nhật chế độ xem hiện tại, nó không phải tính toán lại toàn bộ khung nhìn. Ghi nhớ có cả một không gian và yêu cầu CPU cho các khung nhìn. Ngoài ra, ít nhất là khi tôi làm việc với CouchDB một cách chuyên nghiệp (nó đã được một vài năm), nó đã được khá quan trọng để chỉ sử dụng được xây dựng trong giảm chức năng tức là. _sum. Các chức năng giảm Javascript tùy chỉnh cực kỳ chậm – wallacer

0

Chỉ cần sử dụng loại SQLite dung dịch nhẹ cho các giao dịch, và khi giao dịch hoàn tất tái tạo thành công nó, và đánh dấu nó lặp lại trong SQLite

bảng SQLite

txn_id , txn_attribute1, txn_attribute2,......,txn_status 
dhwdhwu$sg1 x     y    added/replicated 

Bạn cũng có thể xóa các giao dịch mà được nhân rộng thành công.

Các vấn đề liên quan