2012-05-25 24 views
24

thể bạn vui lòng giải thích cho tôi noi theo gương từ "The Little Redis Book":giao dịch và báo cáo đồng hồ trong Redis

Với đoạn mã trên, chúng ta sẽ không thể thực hiện lệnh riêng incr của chúng tôi kể từ khi họ tất cả được thực hiện cùng nhau khi exec được gọi. Từ mã, chúng ta không thể làm:

redis.multi() 
current = redis.get('powerlevel') 
redis.set('powerlevel', current + 1) 
redis.exec() 

Đó không phải là cách Redis giao dịch làm việc. Nhưng, nếu chúng ta thêm một chiếc đồng hồ để powerlevel, chúng tôi có thể làm:

redis.watch('powerlevel') 
current = redis.get('powerlevel') 
redis.multi() 
redis.set('powerlevel', current + 1) 
redis.exec() 

Nếu khách hàng khác thay đổi giá trị của powerlevel sau khi chúng tôi gọi là đồng hồ trên đó, giao dịch của chúng tôi sẽ thất bại. Nếu không có khách hàng nào thay đổi giá trị , bộ này sẽ hoạt động. Chúng ta có thể thực thi mã này trong một vòng lặp cho đến khi nó hoạt động.

Tại sao chúng tôi không thể thực hiện gia tăng trong giao dịch mà không thể bị gián đoạn bởi lệnh khác? Tại sao chúng ta cần lặp lại thay thế và đợi cho đến khi không ai thay đổi giá trị trước khi giao dịch bắt đầu?

+0

Bạn biết về trường hợp [incr] (http://redis.io/commands/incr) bị lỗi khi truy cập lại phải không? Nó thực hiện chính xác những gì bạn muốn trong ví dụ của bạn, mà không cần sử dụng một giao dịch. Tất nhiên đây không phải là câu trả lời cho chính câu hỏi đó, nhưng dù sao cũng đáng biết. – polvoazul

+2

@polvoazul, tôi biết lệnh này, cảm ơn. Đó là một câu hỏi phổ biến không phải do trường hợp thực sự gây ra. – Marboni

Trả lời

62

Có một số câu hỏi ở đây.

1) Tại sao chúng tôi không thể thực hiện gia tăng trong giao dịch mà không thể bị gián đoạn bởi lệnh khác?

Xin lưu ý rằng Redis "giao dịch" hoàn toàn khác so với hầu hết mọi người nghĩ rằng giao dịch nằm trong DBMS cổ điển.

# Does not work 
redis.multi() 
current = redis.get('powerlevel') 
redis.set('powerlevel', current + 1) 
redis.exec() 

Bạn cần hiểu những gì được thực thi trên phía máy chủ (trong Redis) và những gì được thực thi trên phía máy khách (trong tập lệnh của bạn). Trong đoạn mã trên, các lệnh GET và SET sẽ được thực thi trên Redis side, nhưng việc gán cho current và tính toán hiện tại + 1 được cho là được thực thi ở phía máy khách.

Để đảm bảo nguyên tử, khối MULTI/EXEC trì hoãn việc thực thi lệnh Redis cho đến khi thực hiện. Vì vậy, khách hàng sẽ chỉ chồng lên các lệnh GET và SET trong bộ nhớ, và thực thi chúng trong một lần chụp và cuối cùng là nguyên tử. Tất nhiên, nỗ lực gán dòng điện vào kết quả của GET và tăng dần sẽ xảy ra trước đó. Trên thực tế, phương thức redis.get sẽ chỉ trả về chuỗi "QUEUED" để báo hiệu lệnh bị trì hoãn và việc tăng lên sẽ không hoạt động.

Trong khối MULTI/EXEC, bạn chỉ có thể sử dụng các lệnh có tham số có thể được biết đầy đủ trước khi bắt đầu khối. Bạn có thể muốn đọc the documentation để biết thêm thông tin.

2) Tại sao chúng ta cần lặp lại và chờ cho đến khi không ai thay đổi giá trị trước khi giao dịch bắt đầu?

Đây là ví dụ về mô hình lạc quan đồng thời.

Nếu chúng ta sử dụng không XEM/MULTI/EXEC, chúng ta sẽ có một điều kiện chủng tộc tiềm năng:

# Initial arbitrary value 
powerlevel = 10 
session A: GET powerlevel -> 10 
session B: GET powerlevel -> 10 
session A: current = 10 + 1 
session B: current = 10 + 1 
session A: SET powerlevel 11 
session B: SET powerlevel 11 
# In the end we have 11 instead of 12 -> wrong 

Bây giờ chúng ta hãy thêm một chiếc đồng hồ/MULTI/EXEC khối. Với mệnh đề WATCH, các lệnh giữa MULTI và EXEC chỉ được thực thi nếu giá trị không thay đổi.

# Initial arbitrary value 
powerlevel = 10 
session A: WATCH powerlevel 
session B: WATCH powerlevel 
session A: GET powerlevel -> 10 
session B: GET powerlevel -> 10 
session A: current = 10 + 1 
session B: current = 10 + 1 
session A: MULTI 
session B: MULTI 
session A: SET powerlevel 11 -> QUEUED 
session B: SET powerlevel 11 -> QUEUED 
session A: EXEC -> success! powerlevel is now 11 
session B: EXEC -> failure, because powerlevel has changed and was watched 
# In the end, we have 11, and session B knows it has to attempt the transaction again 
# Hopefully, it will work fine this time. 

Vì vậy, bạn không phải lặp lại để đợi cho đến khi không ai thay đổi giá trị, cho đến khi Redis đảm bảo giá trị nhất quán và báo hiệu thành công.

Trong hầu hết các trường hợp, nếu "giao dịch" đủ nhanh và xác suất có ganh đua thấp, các bản cập nhật rất hiệu quả. Bây giờ, nếu có tranh chấp, một số hoạt động bổ sung sẽ phải được thực hiện đối với một số "giao dịch" (do lặp lại và thử lại). Nhưng dữ liệu sẽ luôn nhất quán và không cần khóa.

+1

Giải thích tuyệt vời, cảm ơn rất nhiều, cuối cùng tôi đã nhận được nó! Sau khi hiểu về mục 1, mục 2 trở nên hiển nhiên. – Marboni

+0

Vì vậy, có bất kỳ "công bằng" trong điều này? Nếu tôi đã làm "đồng hồ" nhưng nó đã thất bại thì sao? Sau đó, tôi phải thử lại. Điều gì sẽ xảy ra nếu có tranh chấp - tôi có thể thử mãi mãi mà không có "lấy khóa" không? – Brad

+0

Vì vậy, tôi nghĩ rằng tôi hiểu từ ví dụ của bạn - rằng nếu bạn đã cho thấy một ví dụ đã MULTI/EXEC nhưng không có WATCH, cả hai phiên có thể đọc giá trị 10, và có Redis viết lại 11. Nó sẽ làm cho writeback "nguyên tử", nhưng sẽ không bảo vệ chống lại khách hàng đọc và tăng cùng một giá trị tại địa phương, dẫn đến một câu trả lời sai? – Brad

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