**redis 锁的理解**
在`redis`作为缓存时,为了防止发生穿透、雪崩等问题,解决办法大多是在缓存过期后更新缓存时使用锁来防止穿透、雪崩的发生。而所谓的锁其实是手动设置的一个key值罢了。
举个例子:
缓存中存在一个key为'list'的缓存数据,这个list缓存长期处于高频请求下,在一定时间后,’list‘缓存过期,这时需要更新缓存。如果大量请求发现缓存过期都去读取数据库时,可能导致数据库崩溃。这时我们在第一个发现缓存过期的请求到达时,设置一个key为’lock‘的键并设置一定的过期时间,然后让这个请求去数据库请求到数据并更新缓存,然后删除’lock‘键,再返回数据。
在以上例子中有以下几点:
- 设置的’lock‘键就被称之为锁
- 给’lock'键添加过期时间是为了防止更新缓存的过程中程序意外退出,导致锁没有删除。防止形成死锁
- 大量请求在缓存过期后同时访问数据库导致数据库崩溃,被称之为“雪崩”
- 在请求发现缓存过期后,所有的请求都必须判断锁是否存在,不存在时应当设置锁并更新缓存。锁存在时,其他请求应该休眠后在尝试获取缓存,或是返回其他提示信息。
- 缓存的作用就是避免大量数据同时访问底层数据库,减小数据库压力,应当时刻避免直接对底层数据库查询访问
- 此例中还有其他的问题没有解决,只是为了理解锁的概念。例如:
- A请求B请求同时创建锁,发生抢锁(我不知道后果是什么)
- A请求更新缓存好事过长,导致锁时间过期,被B请求再次创建了锁,之后A请求更新完缓存删除了B创建的锁
在代码里的实现(基本逻辑,不保证运行)
```php
if(Redis::exists('list')) {
return Redis::get('list');
}
// EX:设置过期时间。NX:不存在时才可以设置
$lock = Redis::set('lock', '', 'EX', 20000, 'NX');
if($lock) {
// 访问数据库并更新 list 缓存
Redis::del($lock);
return Redis::get('list');
} else {
// 间隔2秒循环知道锁被删除
while(Redis::get('lock')) {
sleep(2);
continue;
}
return Redis::get('list');
}