8.1 ID giao dịch
Nó được chỉ định là XID hoặc TxID (nếu có sự khác biệt, hãy cho tôi biết). Dấu thời gian có thể được sử dụng làm TxID, có thể hữu ích nếu chúng ta muốn khôi phục tất cả các hành động về một thời điểm nào đó. Vấn đề có thể phát sinh nếu dấu thời gian không đủ chi tiết - khi đó các giao dịch có thể nhận được cùng một ID.
Do đó, tùy chọn đáng tin cậy nhất là tạo ID sản phẩm UUID duy nhất. Trong Python, điều này rất dễ dàng:
>>> import uuid
>>> str(uuid.uuid4())
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0'
>>> str(uuid.uuid4())
'd15bed89-c0a5-4a72-98d9-5507ea7bc0ba'
Ngoài ra còn có một tùy chọn để băm một tập hợp dữ liệu xác định giao dịch và sử dụng hàm băm này làm TxID.
8.2 Thử lại
Nếu chúng ta biết rằng một chức năng hoặc chương trình nhất định là bình thường, thì điều này có nghĩa là chúng ta có thể và nên cố gắng lặp lại lệnh gọi của nó trong trường hợp có lỗi. Và chúng ta chỉ cần chuẩn bị cho thực tế là một số hoạt động sẽ gây ra lỗi - do các ứng dụng hiện đại được phân phối qua mạng và phần cứng, lỗi không nên được coi là ngoại lệ mà là tiêu chuẩn. Lỗi có thể xảy ra do sự cố máy chủ, lỗi mạng, tắc nghẽn ứng dụng từ xa. Ứng dụng của chúng ta nên hoạt động như thế nào? Đúng vậy, hãy thử lặp lại thao tác.
Vì một đoạn mã có thể nói nhiều hơn cả một trang từ, nên chúng ta hãy sử dụng một ví dụ để hiểu cơ chế thử lại ngây thơ sẽ hoạt động như thế nào một cách lý tưởng. Tôi sẽ chứng minh điều này bằng thư viện Độ bền (nó được thiết kế tốt đến mức ngay cả khi bạn không định sử dụng nó, ví dụ này sẽ cho bạn thấy cách bạn có thể thiết kế cơ chế lặp lại):
import logging
import random
import sys
from tenacity import retry, stop_after_attempt, stop_after_delay, wait_exponential, retry_if_exception_type, before_log
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger(__name__)
@retry(
stop=(stop_after_delay(10) | stop_after_attempt(5)),
wait=wait_exponential(multiplier=1, min=4, max=10),
retry=retry_if_exception_type(IOError),
before=before_log(logger, logging.DEBUG)
)
def do_something_unreliable():
if random.randint(0, 10) > 1:
raise IOError("Broken sauce, everything is hosed!!!111one")
else:
return "Awesome sauce!"
print(do_something_unreliable.retry.statistics)
> Đề phòng, tôi sẽ nói: \@retry(...) là một cú pháp Python đặc biệt được gọi là "trình trang trí". Nó chỉ là một hàm retry(...) bao hàm một hàm khác và thực hiện điều gì đó trước hoặc sau khi nó được thực thi.
Như chúng ta có thể thấy, các lần thử lại có thể được thiết kế một cách sáng tạo:
- Bạn có thể giới hạn số lần thử theo thời gian (10 giây) hoặc số lần thử (5).
- Có thể theo cấp số nhân (nghĩa là 2 ** một số tăng dần n ). hoặc bằng cách nào đó khác (ví dụ: đã sửa) để tăng thời gian giữa các lần thử riêng biệt. Biến thể theo cấp số nhân được gọi là "sự sụp đổ tắc nghẽn".
- Bạn chỉ có thể thử lại đối với một số loại lỗi nhất định (IOError).
- Các lần thử lại có thể được thực hiện trước hoặc hoàn thành bởi một số mục nhập đặc biệt trong nhật ký.
Bây giờ chúng ta đã hoàn thành khóa học về chiến binh trẻ và biết các khối xây dựng cơ bản mà chúng ta cần để làm việc với các giao dịch ở phía ứng dụng, hãy làm quen với hai phương pháp cho phép chúng ta thực hiện các giao dịch trong các hệ thống phân tán.
8.3 Các công cụ nâng cao dành cho những người yêu thích giao dịch
Tôi sẽ chỉ đưa ra các định nghĩa khá chung chung, vì chủ đề này xứng đáng là một bài viết lớn riêng biệt.
Cam kết hai pha (2pc) . 2pc có hai giai đoạn: giai đoạn chuẩn bị và giai đoạn cam kết. Trong giai đoạn chuẩn bị, tất cả các dịch vụ siêu nhỏ sẽ được yêu cầu chuẩn bị cho một số thay đổi dữ liệu có thể được thực hiện nguyên tử. Khi tất cả đã sẵn sàng, giai đoạn cam kết sẽ thực hiện các thay đổi thực tế. Để điều phối quá trình, cần có một điều phối viên toàn cầu khóa các đối tượng cần thiết - nghĩa là chúng không thể truy cập được để thay đổi cho đến khi điều phối viên mở khóa chúng. Nếu một vi dịch vụ cụ thể chưa sẵn sàng cho các thay đổi (ví dụ: không phản hồi), điều phối viên sẽ hủy bỏ giao dịch và bắt đầu quá trình khôi phục.
Tại sao giao thức này tốt? Nó cung cấp tính nguyên tử. Ngoài ra, nó đảm bảo sự cô lập khi viết và đọc. Điều này có nghĩa là những thay đổi đối với một giao dịch sẽ không hiển thị với những người khác cho đến khi điều phối viên thực hiện các thay đổi. Nhưng các thuộc tính này cũng có một nhược điểm: vì giao thức này là đồng bộ (chặn), nó làm chậm hệ thống (mặc dù thực tế là bản thân cuộc gọi RPC khá chậm). Và một lần nữa, có nguy cơ ngăn chặn lẫn nhau.
câu chuyện . Trong mẫu này, một giao dịch phân tán được thực thi bởi các giao dịch cục bộ không đồng bộ trên tất cả các vi dịch vụ được liên kết. Các dịch vụ siêu nhỏ giao tiếp với nhau thông qua một xe buýt sự kiện. Nếu bất kỳ dịch vụ siêu nhỏ nào không hoàn thành giao dịch cục bộ của mình, thì các dịch vụ siêu nhỏ khác sẽ thực hiện các giao dịch bù để khôi phục các thay đổi.
Ưu điểm của Saga là không có đối tượng nào bị chặn. Nhưng có, tất nhiên, nhược điểm.
Saga khó gỡ lỗi, đặc biệt khi có nhiều dịch vụ siêu nhỏ liên quan. Một nhược điểm khác của mẫu Saga là nó thiếu khả năng cách ly đọc. Đó là, nếu các thuộc tính được chỉ ra trong ACID là quan trọng đối với chúng tôi, thì Saga không phù hợp lắm với chúng tôi.
Chúng ta thấy gì từ mô tả của hai kỹ thuật này? Thực tế là trong các hệ thống phân tán, trách nhiệm về tính nguyên tử và sự cô lập thuộc về ứng dụng. Điều tương tự cũng xảy ra khi sử dụng cơ sở dữ liệu không đảm bảo ACID. Nghĩa là, những thứ như giải quyết xung đột, khôi phục, cam kết và giải phóng dung lượng đều do nhà phát triển đảm nhận.
8.4 Làm cách nào để biết khi nào tôi cần bảo đảm ACID?
Khi có khả năng cao là một nhóm người dùng hoặc quy trình nhất định sẽ đồng thời làm việc trên cùng một dữ liệu .
Xin lỗi vì sự tầm thường, nhưng một ví dụ điển hình là các giao dịch tài chính.
Khi thứ tự thực hiện các giao dịch có vấn đề.
Hãy tưởng tượng rằng công ty của bạn sắp chuyển từ trình nhắn tin FunnyYellowChat sang trình nhắn tin FunnyRedChat, bởi vì FunnyRedChat cho phép bạn gửi ảnh gif nhưng FunnyYellowChat thì không. Nhưng bạn không chỉ thay đổi trình nhắn tin - bạn đang di chuyển thư từ của công ty bạn từ trình nhắn tin này sang trình nhắn tin khác. Bạn làm điều này bởi vì các lập trình viên của bạn quá lười ghi lại các chương trình và quy trình ở đâu đó tập trung và thay vào đó họ xuất bản mọi thứ ở các kênh khác nhau trong trình nhắn tin. Có, và nhân viên bán hàng của bạn đã công bố chi tiết về các cuộc đàm phán và thỏa thuận ở cùng một nơi. Nói tóm lại, toàn bộ cuộc sống của công ty bạn ở đó và vì không ai có thời gian để chuyển toàn bộ mọi thứ sang một dịch vụ làm tài liệu và việc tìm kiếm các trình nhắn tin tức thời hoạt động tốt, bạn đã quyết định thay vì dọn dẹp đống đổ nát chỉ sao chép tất cả tin nhắn đến một vị trí mới. Thứ tự của các tin nhắn là quan trọng
Nhân tiện, đối với thư từ trong một trình nhắn tin, thứ tự nói chung là quan trọng, nhưng khi hai người viết một cái gì đó trong cùng một cuộc trò chuyện cùng một lúc, thì nói chung, tin nhắn của ai sẽ xuất hiện trước không quá quan trọng. Vì vậy, đối với trường hợp cụ thể này, ACID sẽ không cần thiết.
Một ví dụ khác có thể là tin sinh học. Tôi hoàn toàn không hiểu điều này, nhưng tôi cho rằng thứ tự đó rất quan trọng khi giải mã bộ gen người. Tuy nhiên, tôi nghe nói rằng các nhà tin sinh học thường sử dụng một số công cụ của họ cho mọi thứ - có lẽ họ có cơ sở dữ liệu của riêng mình.
Khi bạn không thể cung cấp cho người dùng hoặc xử lý dữ liệu cũ.
Và một lần nữa - giao dịch tài chính. Thành thật mà nói, tôi không thể nghĩ ra bất kỳ ví dụ nào khác.
Khi các giao dịch đang chờ xử lý có liên quan đến chi phí đáng kể. Hãy tưởng tượng các vấn đề có thể phát sinh khi bác sĩ và y tá cùng cập nhật hồ sơ bệnh nhân và xóa các thay đổi của nhau cùng một lúc, vì cơ sở dữ liệu không thể tách biệt các giao dịch. Hệ thống chăm sóc sức khỏe là một lĩnh vực khác, bên cạnh tài chính, nơi các bảo đảm ACID có xu hướng rất quan trọng.
8.5 Khi nào tôi không cần ACID?
Khi người dùng chỉ cập nhật một số dữ liệu riêng tư của họ.
Ví dụ: người dùng để lại nhận xét hoặc ghi chú dán trên trang web. Hoặc chỉnh sửa dữ liệu cá nhân trong tài khoản cá nhân với nhà cung cấp dịch vụ bất kỳ.
Khi người dùng hoàn toàn không cập nhật dữ liệu mà chỉ bổ sung dữ liệu mới (chắp thêm).
Ví dụ: một ứng dụng đang chạy lưu dữ liệu về các lần chạy của bạn: bạn đã chạy bao nhiêu, trong thời gian nào, lộ trình, v.v. Mỗi lần chạy mới là dữ liệu mới và những cái cũ không được chỉnh sửa gì cả. Có lẽ, dựa trên dữ liệu, bạn sẽ có được các phân tích - và chỉ cơ sở dữ liệu NoSQL mới phù hợp với tình huống này.
Khi logic kinh doanh không xác định nhu cầu về một thứ tự nhất định trong đó các giao dịch được thực hiện.
Có lẽ, đối với một blogger Youtube, người quyên góp để sản xuất tài liệu mới trong buổi phát sóng trực tiếp tiếp theo, việc ai, khi nào và theo thứ tự nào, đã ném tiền cho anh ta không quá quan trọng.
Khi người dùng sẽ ở trên cùng một trang web hoặc cửa sổ ứng dụng trong vài giây hoặc thậm chí vài phút và do đó, bằng cách nào đó họ sẽ thấy dữ liệu cũ.
Về mặt lý thuyết, đây là bất kỳ phương tiện truyền thông tin tức trực tuyến nào hoặc cùng một Youtube. Hoặc "Ha-br". Khi việc các giao dịch chưa hoàn thành có thể được lưu trữ tạm thời trong hệ thống không quan trọng với bạn, thì bạn có thể bỏ qua chúng mà không có bất kỳ thiệt hại nào.
Nếu bạn đang tổng hợp dữ liệu từ nhiều nguồn và dữ liệu được cập nhật với tần suất cao - ví dụ: dữ liệu về tỷ lệ lấp đầy chỗ đậu xe trong thành phố thay đổi ít nhất 5 phút một lần, thì về lý thuyết, đó sẽ không phải là vấn đề lớn cho bạn nếu tại một thời điểm nào đó, giao dịch cho một trong các bãi đậu xe không được thực hiện. Mặc dù, tất nhiên, nó phụ thuộc vào chính xác những gì bạn muốn làm với dữ liệu này.
GO TO FULL VERSION