Redis构建分布式锁

时间:2017-04-08

我们都知道当redis开启时,事务中的命令是不执行的,而是先将命令压入队列,然后当出现exec命令的时候,才会阻塞式的将所有的命令一个接一个的执行。

所以当使用PHP中的Redis类进行redis事务的时候,所有有关redis的命令都不会真正的执行,而仅仅是将命令发送到redis中进行存储起来。

因此下图中所圈到的$count实际上不是我们想要的数据,而是一个对象,因此test.php中11行出错。

查看对象count:

4.2、原子性操作incr解决

#更新test.php文件

<?php
header("content-type: text/html;charset=utf8;");
$start=time();
$redis=new Redis();
$redis->connect('192.168.95.11','6379');
for ($i=0; $i < 100000; $i++) 
{ 
 $count=$redis->incr('count');
}
$end=time();
echo "this OK<br/>";
echo "执行时间为:".($end-$start);
?>

两个浏览器同时执行,耗时14、15秒,count=200000,可以解决此问题。

缺点:

仅仅只是解决这里的取出加1的问题,本质上还是没能解决问题的,在实际环境中,我们需要做的是一系列操作,不仅仅只是取出加1,因此就很有必要构建一个万能锁了。

5、构建分布式锁  

我们构造锁的目的就是在高并发下消除选择竞争、保持数据一致性

构造锁的时候,我们需要注意几个问题:

1、预防处理持有锁在执行操作的时候进程奔溃,导致死锁,其他进程一直得不到此锁

2、持有锁进程因为操作时间长而导致锁自动释放,但本身进程并不知道,最后错误的释放其他进程的锁

3、一个进程锁过期后,其他多个进程同时尝试获取锁,并且都成功获得锁

我们将不对test.php文件修改了,而是直接建立一个相对比较规范的面向对象Lock.class.php类文件  

#建立Lock.class,php文件

<?php
#分布式锁
class Lock
{
 private $redis=''; #存储redis对象
 /**
 * @desc 构造函数
 * 
 * @param $host string | redis主机
 * @param $port int | 端口
 */
 public function __construct($host,$port=6379)
 {
  $this->redis=new Redis();
  $this->redis->connect($host,$port);
 } 
 /**
 * @desc 加锁方法
 *
 * @param $lockName string | 锁的名字
 * @param $timeout int | 锁的过期时间
 *
 * @return 成功返回identifier/失败返回false
 */
 public function getLock($lockName, $timeout=2)
 {
  $identifier=uniqid();  #获取唯一标识符
  $timeout=ceil($timeout); #确保是整数
  $end=time()+$timeout;
  while(time()<$end)   #循环获取锁
  {
   if($this->redis->setnx($lockName, $identifier)) #查看$lockName是否被上锁
   {
    $this->redis->expire($lockName, $timeout);  #为$lockName设置过期时间,防止死锁
    return $identifier;        #返回一维标识符
   }
   elseif ($this->redis->ttl($lockName)===-1) 
   {                        
    $this->redis->expire($lockName, $timeout);  #检测是否有设置过期时间,没有则加上(假设,客户端A上一步没能设置时间就进程奔溃了,客户端B就可检测出来,并设置时间)
   }
   usleep(0.001);   #停止0.001ms
  }
  return false;
 }
 /**
 * @desc 释放锁
 *
 * @param $lockName string | 锁名
 * @param $identifier string | 锁的唯一值
 *
 * @param bool
 */
 public function releaseLock($lockName,$identifier)
 {
  if($this->redis->get($lockName)==$identifier) #判断是锁有没有被其他客户端修改
  { 
   $this->redis->multi();
   $this->redis->del($lockName); #释放锁
   $this->redis->exec();
   return true;
  }
  else
  {
   return false; #其他客户端修改了锁,不能删除别人的锁
  }
 }
 /**
 * @desc 测试
 * 
 * @param $lockName string | 锁名
 */
 public function test($lockName)
 {
  $start=time();
  for ($i=0; $i < 10000; $i++) 
  { 
   $identifier=$this->getLock($lockName);
   if($identifier)
   {
    $count=$this->redis->get('count');
    $count=$count+1;
    $this->redis->set('count',$count);
    $this->releaseLock($lockName,$identifier);
   } 
  }
  $end=time();
  echo "this OK<br/>";
  echo "执行时间为:".($end-$start);
 }

}
header("content-type: text/html;charset=utf8;");
$obj=new Lock('192.168.95.11');
$obj->test('lock_count');
?>

测试结果:

  • 共3页:
  • 上一页
  • 2/3
  • 下一页
  • 上一篇:PHP获取当前日期及本周一是几月几号的方法 下一篇:谈谈从phpinfo中能获取哪些值得注意的信息

    相关文章

    最新文章