出现原因:对于一个并发很高的接口,缓存的key在失效的一瞬间会有机会将所有的请求都会打在数据库上面,造成数据库查询压力过大
一般解决思路:通常采用redis加锁(悲观锁/互斥锁)来解决,每次请求都会向redis进行拿锁操作,拿锁成功->写入缓存,否则进入等待时期(继续拿锁至成功为止)
/**
* 乐观锁
* @param string $key
* @param callable $function
* @param string $value
* @param int $ttl
* @return mixed
* @throws \Exception
*/
public function optimismLock(
string $key,
callable $function,
string $value = RedisFuncService::LOCK_VALUE,
int $ttl = RedisFuncService::LOCK_TTL
)
{
try {
$lock = $this->lock($key, $value, $ttl);
if (!$lock) {
logger("get lock fail return", compact('key'));
throw new \Exception("locking");
}
// 执行主程序
$action = $function();
// 执行完毕 ,使用lau脚本解锁
$this->unlock($key, $value);
} catch (\Exception $exception) {
$this->unlock($key, $value);
throw new \Exception($exception->getMessage());
}
return $action;
}
/**
* 悲观锁
* @param string $key
* @param callable $function
* @param string $value
* @param int $ttl
* @return mixed
* @throws \Exception
*/
public function pessimisticLock(
string $key,
callable $function,
string $value = RedisFuncService::LOCK_VALUE,
int $ttl = RedisFuncService::LOCK_TTL
)
{
try {
do {
$lock = $this->lock($key, $value, $ttl);
if (!$lock) {
// 继续等待拿锁成功
logger("get lock fail waiting", compact('key'));
usleep(5000); // 微妙
} else {
// 拿锁成功直接跳出执行主程序
break;
}
} while (!$lock);
// 执行主程序
$action = $function();
// 执行完毕,解锁
$this->unlock($key, $value);
} catch (\Exception $exception) {
$this->unlock($key, $value);
throw new \Exception($exception->getMessage());
}
return $action;
}
public function optimismLock(string $file_name, callable $func, array $option = [])
{
list($mode) = $this->serializationOptions($option);
$file_name = storage_path($file_name);
$fp = fopen($file_name, $mode);
try {
if (!flock($fp, LOCK_EX | LOCK_NB)) { // 强占锁,不阻塞,$function没执行完直接返回错误
throw new \Exception("file locking");
}
$func = $func();
flock($fp, LOCK_UN);
fclose($fp);
@unlink($file_name);
} catch (\Exception $exception) {
flock($fp, LOCK_UN);
fclose($fp);
@unlink($file_name);
throw new \Exception($exception->getMessage());
}
return $func;
}
public function pessimisticLock(string $file_name, callable $func, array $option = [])
{
list($mode) = $this->serializationOptions($option);
$file_name = storage_path($file_name);
$fp = fopen($file_name, $mode);
try {
if (flock($fp, LOCK_EX)) { // 强占锁 ,阻塞,执行完$function 才解锁
$func = $func();
flock($fp, LOCK_UN);
}
fclose($fp);
@unlink($file_name);
} catch (\Exception $exception) {
flock($fp, LOCK_UN);
fclose($fp);
@unlink($file_name);
throw new \Exception($exception->getMessage());
}
return $func;
}
-- END
写的不错,赞助一下主机费
PHPLAF v.1.0.10 正式发布了!开发文档:https://blog.junphp.com/apiword.jsp