Kunci terdistribusi merupakan salah satu fitur yang paling sering digunakan dalam aplikasi berskala besar. Anda dapat mengimplementasikan kunci terdistribusi berbasis Redis melalui berbagai metode. Topik ini menjelaskan pendekatan umum untuk mengimplementasikan kunci terdistribusi serta praktik terbaik untuk menerapkannya menggunakan Tair (Enterprise Edition). Praktik terbaik ini dikembangkan dari pengalaman Alibaba Group dalam menggunakan Tair (Enterprise Edition).
Informasi latar belakang
Kunci terdistribusi dan skenario penggunaannya
Jika sumber daya tertentu diakses secara bersamaan oleh beberapa thread dalam proses yang sama selama pengembangan aplikasi, Anda dapat menggunakan mutex atau kunci baca/tulis. Jika akses terjadi antar proses pada host yang sama, gunakan primitif sinkronisasi seperti semaphore, pipeline, atau memori bersama. Namun, jika sumber daya diakses oleh beberapa host, Anda harus menggunakan kunci terdistribusi. Kunci terdistribusi adalah mekanisme mutual exclusion dengan keberadaan global yang mencegah kegagalan logis akibat persaingan sumber daya dalam sistem terdistribusi.

Fitur kunci terdistribusi
Saling Eksklusif
Pada saat tertentu, hanya satu klien yang dapat memegang kunci.
Tanpa Deadlock
Kunci terdistribusi menggunakan mekanisme sewa. Jika klien memperoleh kunci lalu mengalami pengecualian, kunci akan dilepaskan otomatis setelah periode tertentu, mencegah deadlock.
Konsisten
Alih bencana di ApsaraDB for Redis dapat dipicu oleh kesalahan eksternal atau internal. Kesalahan eksternal mencakup kegagalan perangkat keras dan pengecualian jaringan, sedangkan kesalahan internal mencakup query lambat dan cacat sistem. Setelah alih bencana dipicu, node replika dipromosikan menjadi node master baru untuk memastikan ketersediaan tinggi (HA). Dalam skenario ini, jika bisnis Anda memiliki persyaratan tinggi untuk mutual exclusion, kunci harus tetap sama setelah alih bencana.
Implementasikan kunci terdistribusi berbasis open source Redis
Metode yang dijelaskan dalam bagian ini juga berlaku untuk Redis Open-Source Edition.
Memperoleh Kunci
Di Redis, jalankan perintah SET untuk memperoleh kunci. Contoh perintah dan parameter disediakan di bawah:
SET resource_1 random_value NX EX 5Tabel 1. Parameter atau Opsi
Parameter/opsi
Deskripsi
resource_1
Kunci dari kunci terdistribusi. Jika kunci ada, sumber daya yang sesuai terkunci dan tidak dapat diakses oleh klien lain.
random_value
String acak. Nilai ini harus unik di seluruh klien.
EX
Periode validitas kunci. Unit: detik. Anda juga dapat menggunakan opsi PX untuk menetapkan periode validitas hingga milidetik.
NX
Jika kunci yang akan ditetapkan sudah ada di Redis, operasi set dibatalkan.
Dalam contoh kode, validitas kunci resource_1 diatur selama 5 detik. Jika klien tidak melepaskan kunci, kunci akan kedaluwarsa setelah 5 detik dan dapat diambil kembali oleh sistem untuk digunakan oleh klien lain.
Melepaskan Kunci
Secara umum, gunakan perintah DEL untuk melepaskan kunci. Namun, pendekatan ini dapat menyebabkan masalah berikut:

Pada waktu t1, kunci terdistribusi adalah resource_1 untuk Aplikasi 1, dengan validitas 3 detik.
Aplikasi 1 tetap terblokir selama lebih dari 3 detik karena alasan tertentu, seperti waktu respons yang lama. Kunci resource_1 kedaluwarsa dan kunci terdistribusi dilepaskan secara otomatis pada titik waktu t2.
Pada waktu t3, Aplikasi 2 memperoleh kunci terdistribusi.
Aplikasi 1 melanjutkan dari kondisi terblokir dan menjalankan perintah
DEL resource_1pada titik waktu t4 untuk melepaskan kunci terdistribusi yang dimiliki oleh aplikasi 2.
Contoh ini menunjukkan bahwa kunci hanya perlu dilepaskan oleh klien yang menetapkan kunci. Oleh karena itu, sebelum klien menjalankan perintah GET untuk memeriksa apakah kunci ditetapkan oleh klien itu sendiri. Kemudian, klien dapat menjalankan perintah DEL untuk melepaskan kunci. Dalam kebanyakan kasus, klien menggunakan skrip Lua berikut di Redis untuk melepaskan kunci yang ditetapkan oleh klien:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 endPerpanjangan
Jika klien tidak dapat menyelesaikan operasi dalam waktu sewa kunci, kunci harus diperpanjang. Hanya klien yang menetapkan kunci yang dapat memperpanjangnya. Di Redis, gunakan skrip Lua berikut untuk memperpanjang kunci:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("expire",KEYS[1], ARGV[2]) else return 0 end
Implementasikan kunci terdistribusi berbasis Tair
Untuk instans berbasis DRAM Tair atau instans optimasi memori persisten, Anda dapat menggunakan perintah string yang ditingkatkan untuk mengimplementasikan kunci terdistribusi tanpa skrip Lua tambahan.
Memperoleh Kunci
Metode untuk memperoleh kunci di Tair mirip dengan Redis open source, yaitu menggunakan perintah SET. Contoh perintah:
SET resource_1 random_value NX EX 5Melepaskan Kunci
Gunakan perintah CAD dari Tair (Enterprise Edition) untuk melepaskan kunci secara efisien. Contoh perintah:
/* if (GET(resource_1) == my_random_value) DEL(resource_1) */ CAD resource_1 my_random_valuePerpanjangan
Gunakan perintah CAS untuk memperpanjang kunci. Contoh perintah:
CAS resource_1 my_random_value my_random_value EX 10CatatanPerintah CAS tidak memeriksa apakah nilai baru sama dengan nilai aslinya.
Contoh kode berdasarkan Jedis
Definisikan Perintah CAS dan CAD
enum TairCommand implements ProtocolCommand { CAD("CAD"), CAS("CAS"); private final byte[] raw; TairCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } }Memperoleh Kunci
public boolean acquireDistributedLock(Jedis jedis,String resourceKey, String randomValue, int expireTime) { SetParams setParams = new SetParams(); setParams.nx().ex(expireTime); String result = jedis.set(resourceKey,randomValue,setParams); return "OK".equals(result); }Melepaskan Kunci
public boolean releaseDistributedLock(Jedis jedis,String resourceKey, String randomValue) { jedis.getClient().sendCommand(TairCommand.CAD,resourceKey,randomValue); Long ret = jedis.getClient().getIntegerReply(); return 1 == ret; }Perpanjangan
public boolean renewDistributedLock(Jedis jedis,String resourceKey, String randomValue, int expireTime) { jedis.getClient().sendCommand(TairCommand.CAS,resourceKey,randomValue,randomValue,"EX",String.valueOf(expireTime)); Long ret = jedis.getClient().getIntegerReply(); return 1 == ret; }
Metode untuk memastikan konsistensi kunci
Replikasi antara node master dan node replika bersifat asinkron. Jika node master gagal setelah perubahan data ditulis ke node master dan pergantian HA dipicu, perubahan dalam buffer mungkin tidak direplikasi ke node master baru, menyebabkan ketidaksesuaian data. Bagian ini menjelaskan tiga metode untuk memastikan konsistensi kunci.
Gunakan Algoritma Redlock
Algoritma Redlock diajukan oleh pendiri proyek open source Redis untuk memastikan konsistensi kunci. Algoritma Redlock sepenuhnya tentang perhitungan probabilitas. Sebuah instans Redis master-replika tunggal mungkin kehilangan kunci selama alih bencana HA dengan probabilitas
k%. Jika Anda menggunakan algoritma Redlock untuk mengimplementasikan kunci terdistribusi, Anda dapat menghitung probabilitas N instans Redis independen kehilangan kunci pada saat yang sama berdasarkan rumus berikut: Probabilitas kehilangan kunci =(k%)^N. Karena stabilitas tinggi Redis, kunci jarang hilang dan persyaratan layanan Anda dapat dengan mudah dipenuhi.CatatanSaat Anda mengimplementasikan algoritma Redlock, Anda tidak perlu memastikan bahwa semua kunci di N instans Redis berlaku pada saat yang sama. Dalam kebanyakan kasus, algoritma Redlock dapat memenuhi persyaratan bisnis Anda jika Anda memastikan bahwa kunci di
M(1<M=<N)node Redis berlaku pada saat yang sama.Algoritma Redlock memiliki beberapa kelemahan:
Klien membutuhkan waktu lama untuk memperoleh atau melepaskan kunci.
Algoritma Redlock tidak dapat digunakan dalam instans cluster atau master-replika standar.
Algoritma Redlock mengonsumsi jumlah sumber daya yang besar. Untuk menggunakan algoritma Redlock, Anda harus membuat beberapa instans ApsaraDB for Redis independen atau instans Redis yang dikelola sendiri.
Gunakan Perintah WAIT
Perintah WAIT dari Redis memblokir klien saat ini, sampai semua perintah tulis sebelumnya disinkronkan dari node master ke sejumlah node replika yang ditentukan. Dalam perintah WAIT, Anda dapat menentukan periode timeout yang diukur dalam milidetik. Perintah WAIT digunakan di Tair (Redis OSS-compatible) untuk memastikan konsistensi kunci terdistribusi. Contoh perintah:
SET resource_1 random_value NX EX 5 WAIT 1 5000Saat Anda menjalankan perintah WAIT, klien hanya melanjutkan untuk melakukan operasi lain dalam dua skenario setelah klien memperoleh kunci. Satu skenario adalah bahwa data disinkronkan ke node replika. Skenario lainnya adalah bahwa periode timeout tercapai. Dalam contoh ini, periode timeout adalah 5.000 milidetik. Jika output dari perintah WAIT adalah 1, data disinkronkan antara node master dan node replika. Dalam hal ini, konsistensi data terjamin. Perintah WAIT jauh lebih hemat biaya daripada algoritma Redlock.
Catatan:
Perintah WAIT hanya memblokir klien yang mengirim perintah dan tidak memengaruhi klien lain.
Jika perintah WAIT mengembalikan nilai valid, kunci telah disinkronkan ke node replika. Namun, jika pergantian HA dipicu sebelum respons sukses, data mungkin hilang. Output perintah WAIT hanya menunjukkan kemungkinan kegagalan sinkronisasi, dan integritas data tidak dapat dipastikan. Setelah perintah WAIT mengembalikan kesalahan, Anda dapat memperoleh kunci lagi atau memverifikasi data.
Anda tidak perlu menjalankan perintah WAIT untuk melepaskan kunci karena kunci terdistribusi saling eksklusif. Kegagalan logis tidak terjadi meskipun kunci dilepaskan setelah periode tertentu.
Gunakan Tair
Perintah CAS dan CAD membantu mengurangi biaya pengembangan dan manajemen kunci terdistribusi serta meningkatkan kinerja kunci.
Instans berbasis DRAM Tair memberikan performa tiga kali lebih tinggi dibandingkan Redis open source. Kontinuitas layanan terjamin bahkan untuk implementasi kunci terdistribusi konkurensi tinggi. Anda juga dapat mengonfigurasi replikasi semi-sinkron antara node master dan node replika di instans berbasis DRAM. Mode ini memastikan data ditulis ke node master dan disinkronkan ke node replika sebelum respons sukses dikembalikan ke klien, mencegah kehilangan data setelah pergantian HA. Mode replikasi semi-sinkron berubah menjadi mode asinkron jika kegagalan node replika atau pengecualian jaringan terjadi selama sinkronisasi data.