Redis
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> Redis

Redis phân phối gia tăng với khóa

Thật vậy, mã của bạn không an toàn xung quanh ranh giới di chuột qua, bởi vì bạn đang thực hiện "lấy", (độ trễ và suy nghĩ), "đặt" - mà không kiểm tra xem các điều kiện trong "nhận" của bạn vẫn áp dụng. Nếu máy chủ bận xung quanh mục 1000, có thể nhận được tất cả các loại đầu ra điên rồ, bao gồm những thứ như:

1
2
...
999
1000 // when "get" returns 998, so you do an incr
1001 // ditto
1002 // ditto
0 // when "get" returns 999 or above, so you do a set
0 // ditto
0 // ditto
1

Tùy chọn:

  1. sử dụng các API giao dịch và ràng buộc để đảm bảo an toàn cho đồng thời logic của bạn
  2. viết lại logic của bạn dưới dạng tập lệnh Lua qua ScriptEvaluate

Bây giờ, các giao dịch redis (mỗi tùy chọn 1) rất khó. Cá nhân tôi sử dụng "2" - ngoài việc mã và gỡ lỗi đơn giản hơn, điều đó có nghĩa là bạn chỉ có 1 chuyến khứ hồi và hoạt động, trái ngược với "get, watch, get, multi, incr / set, executive / loại bỏ "và vòng lặp" thử lại từ đầu "để giải thích cho trường hợp hủy bỏ. Tôi có thể thử viết nó là Lua cho bạn nếu bạn thích - nó phải khoảng 4 dòng.

Đây là cách triển khai Lua:

string key = ...
for(int i = 0; i < 2000; i++) // just a test loop for me; you'd only do it once etc
{
    int result = (int) db.ScriptEvaluate(@"
local result = redis.call('incr', KEYS[1])
if result > 999 then
    result = 0
    redis.call('set', KEYS[1], result)
end
return result", new RedisKey[] { key });
    Console.WriteLine(result);
}

Lưu ý:nếu bạn cần tham số hóa giá trị tối đa, bạn sẽ sử dụng:

if result > tonumber(ARGV[1]) then

và:

int result = (int)db.ScriptEvaluate(...,
    new RedisKey[] { key }, new RedisValue[] { max });

(so ARGV[1] lấy giá trị từ max )

Cần phải hiểu rằng eval / evalsha (đó là những gì ScriptEvaluate cuộc gọi) không cạnh tranh với các yêu cầu máy chủ khác , vì vậy không có gì thay đổi giữa incrset có thể . Điều này có nghĩa là chúng ta không cần watch phức tạp vv logic.

Đây là điều tương tự (tôi nghĩ vậy!) Thông qua API giao dịch / ràng buộc:

static int IncrementAndLoopToZero(IDatabase db, RedisKey key, int max)
{
    int result;
    bool success;
    do
    {
        RedisValue current = db.StringGet(key);
        var tran = db.CreateTransaction();
        // assert hasn't changed - note this handles "not exists" correctly
        tran.AddCondition(Condition.StringEqual(key, current));
        if(((int)current) > max)
        {
            result = 0;
            tran.StringSetAsync(key, result, flags: CommandFlags.FireAndForget);
        }
        else
        {
            result = ((int)current) + 1;
            tran.StringIncrementAsync(key, flags: CommandFlags.FireAndForget);
        }
        success = tran.Execute(); // if assertion fails, returns false and aborts
    } while (!success); // and if it aborts, we need to redo
    return result;
}

Phức tạp hả? Trường hợp thành công đơn giản đây là sau đó:

GET {key}    # get the current value
WATCH {key}  # assertion stating that {key} should be guarded
GET {key}    # used by the assertion to check the value
MULTI        # begin a block
INCR {key}   # increment {key}
EXEC         # execute the block *if WATCH is happy*

đó là ... khá nhiều công việc, và liên quan đến sự cố đường ống trên bộ ghép kênh. Các trường hợp phức tạp hơn (lỗi xác nhận, lỗi đồng hồ, lỗi bao bọc) sẽ có kết quả hơi khác một chút, nhưng sẽ hoạt động.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Redis:Cách phân tích cú pháp kết quả danh sách

  2. Quy trình ngựa công việc bị chấm dứt bất ngờ RQ và Trị liệu

  3. json.loads và Redis trong python 3.5

  4. Điểm duy nhất cho bảng thành tích redis

  5. Máy chủ Redis không thể chạy hơn 1024 triệu bản đồ tối đa