Redis
 sql >> Teknologi Basis Data >  >> NoSQL >> Redis

Redis didistribusikan kenaikan dengan penguncian

Memang, kode Anda tidak aman di sekitar batas rollover, karena Anda melakukan "get", (latensi dan pemikiran), "set" - tanpa memeriksa apakah kondisi di "get" Anda masih berlaku. Jika server sibuk di sekitar item 1000, akan mungkin untuk mendapatkan segala macam output gila, termasuk hal-hal seperti:

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

Opsi:

  1. gunakan API transaksi dan batasan untuk membuat logika Anda aman-konkurensi
  2. tulis ulang logika Anda sebagai skrip Lua melalui ScriptEvaluate

Sekarang, transaksi redis (per opsi 1) sulit dilakukan. Secara pribadi, saya akan menggunakan "2" - selain lebih sederhana untuk kode dan debug, itu berarti Anda hanya memiliki 1 perjalanan pulang pergi dan operasi, sebagai lawan dari "dapatkan, tonton, dapatkan, multi, incr/set, exec/ buang", dan "coba lagi dari awal" untuk memperhitungkan skenario pembatalan. Saya dapat mencoba menulisnya sebagai Lua untuk Anda jika Anda mau - harus sekitar 4 baris.

Berikut implementasi 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);
}

Catatan:jika Anda perlu membuat parameter maks, Anda akan menggunakan:

if result > tonumber(ARGV[1]) then

dan:

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

(jadi ARGV[1] mengambil nilai dari max )

Perlu dipahami bahwa eval /evalsha (yang merupakan ScriptEvaluate panggilan) tidak bersaing dengan permintaan server lain , jadi tidak ada yang berubah di antara incr dan kemungkinan set . Ini berarti kita tidak memerlukan watch yang rumit dll logika.

Ini sama (saya pikir!) melalui API transaksi / kendala:

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;
}

Rumit, ya? kasus sukses sederhana ini dia:

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*

yang... sedikit pekerjaan, dan melibatkan saluran pipa pada multiplexer. Kasus yang lebih rumit (kegagalan pernyataan, kegagalan arloji, penyelesaian) akan memiliki keluaran yang sedikit berbeda, tetapi seharusnya berhasil.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. apa itu pagecache, dentries, inode?

  2. Bagaimana Redis mencapai throughput dan kinerja yang tinggi?

  3. Koneksi Redis melalui soket di Node.js

  4. Docker menulis masalah koneksi spring boot redis

  5. Menghubungkan ke server redis jarak jauh