最后我是用redis锁解决问题的。锁的代码如下
/**
* redis操作
* ps. 如果没有安装redis,会直接返回false,不会报错!
*
* Created by PhpStorm.
* User: haua
* Date: 16/10/21
* Time: 23:02
*/
class hRedis{
public static $host = '';//127.0.0.1
public static $port = '';//6379
public static $timeout = '10';
public static $auth = '';//密码
public static $prefix = 'act_projects_';//redis用作缓存前缀跟其它缓存做区分
public static $errs = [];
public static $redises = [];
/**
* 连接redis,
* @param $db_index int|string 数据库下标
* @return redis|bool
*/
public static function RD($db_index='1'){
if(!empty(self::$redises[$db_index]))
return self::$redises[$db_index];
if(!class_exists('redis'))
return false;
try{
$redis = new redis();
$redis->connect(self::$host, self::$port,self::$timeout);
if(self::$auth)
$redis->auth(self::$auth);
if(is_numeric($db_index))
$redis->select($db_index);
self::$redises[$db_index] = $redis;
return $redis;
}catch(Exception $e){
self::$errs[] = $e;
return false;
}
}
/**
* redis锁机制
* @param $name string 锁名
* @param $ttl int 锁多长时间,如果超过这段时间还没解锁,就会自动解锁,0为永久,如果大于9位数,会被认为是时间戳。
* @return bool|string 锁成功还是失败,失败返回false,成功会返回一个随机数,随机数需要作为参数传入解锁函数。锁失败的原因是,同名的锁已经锁上了
*
* 锁用途:如果有一件事,只能等它上一次完成,才能再执行,那就要一个锁,
* 每次执行的时候检查是否有锁,如果没锁,则上一把锁,然后执行业务,如果锁住了,表示这个动作正在进行,不能并行执行,所以要提示前端:服务器正在执行任务,请稍后再试。
*/
public static function lock($name,$ttl){
$rd = self::RD();
if(!$rd)//如果无法链接redis,返回加锁失败
return false;
$ran = rand(8,9999);
$ttl = (int)$ttl;
$time = ['nx'];
if($ttl>0){
$now = time();
if($ttl<=$now)
$time['ex'] = $ttl;
else
$time['px'] = $ttl-$now;
}
$name = self::key($name);
$res = $rd->set($name, $ran, $time);
return $res?$ran:false;
}
/**
* 解锁
* @param $name string
* @param $rand string|bool
* @return bool 解锁成功还是失败
*/
public static function unlock($name,$rand){
$rd = self::RD();
if(!$rd)//如果无法链接redis,返回加锁失败
return false;
$name = self::key($name);
if ($rd->get($name) == $rand||$rand===true) {
$rd->del($name);
}else{
return false;
}
return true;
}
}