Analisis masalah dalam kod berikut:
// 分布式锁服务 public interface RedisLockService { // 获取锁 public boolean getLock(String key); // 释放锁 public boolean releaseLock(String key); } // 业务服务 public class BizService { @Resource private RedisLockService redisLockService; public void bizMethod(String bizId) { try { // 获取锁 if(redisLockService.getLock(bizId)) { // 业务重复校验 if(!bizValidate(bizId)) { throw new BizException(ErrorBizCode.REPEATED); } // 执行业务 return doBusiness(); } // 获取锁失败 throw new BizException(ErrorBizCode.GET_LOCK_ERROR); } finally { // 释放锁 redisLockService.releaseLock(bizId); } } }
Kod di atas nampaknya sebenarnya, ia menyembunyikan masalah besar. Masalahnya ialah apabila melepaskan kunci, tiada pengesahan sama ada benang semasa telah memperoleh kunci:
Benang 1 dan utas 2 mengakses kaedah perniagaan pada masa yang sama
benang 2 Berjaya memperoleh kunci dan memulakan pemprosesan perniagaan
Benang 1 tidak memperoleh kunci, tetapi berjaya melepaskan kunci
Pada masa ini, utas 3 cuba memperoleh Kunci berjaya, tetapi perniagaan utas 2 belum diproses, jadi utas 3 tidak akan menyebabkan pengecualian pertindihan perniagaan
akhirnya menyebabkan utas 2 dan utas 3 berulang kali melaksanakan perniagaan
Penyelesaian adalah membenarkan kunci dilepaskan hanya selepas mengesahkan bahawa perolehan kunci berjaya:
public class BizService { @Resource private RedisLockService redisLockService; public void bizMethod(String bizId) { boolean getLockSuccess = false; try { // 尝试获取锁 getLockSuccess = redisLockService.getLock(bizId); // 获取锁成功 if(getLockSuccess) { // 业务重复校验 if(!bizValidate(bizId)) { throw new BizException(ErrorBizCode.REPEATED); } // 执行业务 return doBusiness(); } // 获取锁失败 throw new BizException(ErrorBizCode.GET_LOCK_ERROR); } finally { // 获取锁成功才允许释放锁 if(getLockSuccess) { redisLockService.releaseLock(bizId); } } } }
Tidak. Masalah kedua ialah Redis juga mempunyai mekanisme pembersihan memori, yang mungkin menyebabkan kunci yang diedarkan gagal .
(1) Pemadaman biasa
Redis kerap menyemak kunci mana yang telah tamat tempoh dan memadamnya jika didapati telah tamat tempoh
(2) Pemadaman malas
Jika terdapat terlalu banyak kunci, pemadaman biasa akan menggunakan banyak sumber, jadi strategi pemadaman malas diperkenalkan
Jika Redis mendapati bahawa kunci telah tamat tempoh semasa mengaksesnya, Pemadaman langsung
Apabila memori tidak mencukupi, Redis akan memilih beberapa elemen untuk pemadaman:
tiada enviction
melarang pengusiran data, baru Operasi tulis akan melaporkan ralat
volatile-lru
Pilih data yang paling kurang digunakan baru-baru ini daripada set data dengan masa tamat tempoh untuk dihapuskan
volatile-ttl
Pilih data yang akan tamat tempoh daripada set data dengan masa tamat tempoh ditetapkan untuk dihapuskan
volatile-random
Pilih mana-mana data untuk dihapuskan daripada set data dengan set masa tamat tempoh
kekunci semua- lru
Pilih data yang paling kurang digunakan baru-baru ini daripada set data untuk dihapuskan
allkeys-random
Pilih mana-mana data daripada set data untuk dihapuskan
Terdapat sekurang-kurangnya dua Senario yang menyebabkan kegagalan kunci teragih:
Senario 1: Redis mempunyai ingatan yang tidak mencukupi untuk kitar semula memori dan menggunakan strategi kitar semula allkeys-lru
atau allkeys-random
menyebabkan kegagalan kunci
Senario 2: Benang memperoleh kunci yang diedarkan dengan jayanya, tetapi masa pemprosesan terlalu lama. Pada masa ini, kunci tamat tempoh dan dikosongkan dengan kerap, menyebabkan urutan lain berjaya memperoleh kunci dan melaksanakan perniagaan berulang kali
Penyelesaian umum adalah untuk melindungi pada lapisan pangkalan data Sebagai contoh, perniagaan potongan inventori menggunakan penguncian optimistik pada lapisan pangkalan data.
rreeeeAtas ialah kandungan terperinci Apakah dua perangkap yang perlu dielakkan oleh kunci yang diedarkan oleh Redis?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!