前些日子的业务系统出现了并发现象, 我们的订单表由于对做对第三方开放的接口平台 而增加了 第三方的订单号
而这个订单号只能保证在该商户
下唯一, 整个列中并不唯一, 且我们之前的订单由于没 第三方订单号
而为空, 因此没法建组合唯一索引
利用 Redis 的 setnx 特性可以构建一个锁机制, 以下是实现的部分片段
public static function Block($key, $action = TRUE, $options = [])
{
if(!defined('MERCHANT_TOKEN')) return FALSE;
$res = FALSE;
$redis = Yii::$app->redis;
// 前缀
$prefix = isset($options['prefix']) ? (string)$options['prefix'] : 'MOSN';
// 过期时间
$expire = isset($options['expire']) ? (integer)$options['expire'] : 300;
$key = vsprintf('%s:%s:%s', [$prefix, MERCHANT_TOKEN, $key]);
if ($action === TRUE){
$res = $redis->executeCommand('SETNX', [$key, time() + $expire]);
if ($res){
$redis->executeCommand('EXPIRE', [$key, $expire]);
}
}else if ($action === FALSE){
$res = $redis->executeCommand('DEL', [$key]);
}
return $res ? TRUE : FALSE;
}
优化后
public static function Block($key, $action = TRUE, $options = [])
{
if(!defined('MERCHANT_TOKEN')) return FALSE;
$res = FALSE;
$redis = Yii::$app->redis;
// 前缀
$prefix = isset($options['prefix']) ? (string)$options['prefix'] : 'MOSN';
// 过期时间
$expire = isset($options['expire']) ? (integer)$options['expire'] : 300;
$key = vsprintf('%s:%s:%s', [$prefix, MERCHANT_TOKEN, $key]);
$timeNow = time();
if ($action === TRUE){
if ($redis->executeCommand('SETNX', [$key, $timeNow + $expire]) || $redis->executeCommand('GET', [$key]) && $redis->executeCommand('GETSET', [$key, $timeNow + $expire + 1]) < $timeNow){
$res = TRUE;
}else{
$res = FALSE;
}
}else if ($action === FALSE){
$redis->executeCommand('SET', [$key, $timeNow]);
$res = TRUE;
}
$redis->executeCommand('EXPIRE', [$key, $expire]);
return $res;
}