常见的情况是更新某个数量字段。比如更新一篇文章的浏览数。如果并发地更新文章表的浏览数字段,会因为并发,使得多个更新请求得到的浏览数是一样的,导致更新结果一样。比如并发请求10次,他们都要给浏览数加1,但是其中可能有几个请求在获取当前数据库的浏览数都是同一个数量a,然后都将浏览数字段更新成a+1,这样就会造成更新浏览数错误的情况。
更新浏览数错误倒不会产生多大的不良影响,但是如果是变更用户资金,这种敏感操作。哪怕一次并发导致增减资金不正确都会造成实际的损失。这种情况下给程序加锁,变得尤为重要。
我们经常使用的mysql有锁的机制,但是mysql的锁基于事务,而且他是进程阻塞的。当Mysql没有得到锁时,会一直等待直到超时。超时就意味着PHP程序将得到一个Mysql的异常而导致程序退出。如果并发较高,会有很多请求因为超时而导致退出,这种情况同样不会是我们希望看到的。
Redis的setnx操作是原子性的,基于setnx可以很好的解决PHP程序需要锁机制的问题。他不会因为拿不到锁一直等待而超时的情况。Laytp
框架的第三方库extend\laytp\library\Redis.php
提供了Redis锁的获取和删除方法。源码如下:
<?php
namespace laytp\library;
use think\facade\Cache;
/**
* Redis锁
*/
class Redis
{
/**
* 得到一个redis锁
* @param $name string 锁名称
* @param $ttl int 锁存在的时间,单位秒,默认60秒
* @return bool
*/
public static function getLock($name, $ttl=60)
{
set_time_limit(0);
$redis = Cache::store('redis')->handler();
while(true){
if($redis->setnx($name, 1)){
$redis->expire($name, $ttl);
break;
}
}
return true;
}
/**
* 删除一个redis锁
* @param $name string 锁名称
* @return bool
*/
public static function delLock($name)
{
$redis = Cache::store('redis')->handler();
$redis->del($name);
return true;
}
}
使用示例:
use laytp\library\Redis;
// 循环获取Redis锁,直到获取到锁才执行后续程序
if(Redis::getLock('锁名称', 60)){
//这里写不允许进行并发请求的PHP程序块
Redis::delLock('锁名称');//程序执行完成后,将Redis锁删除
}