甘肃省,教你怎样用分布式锁处理并发问题?,风暴

教你怎样用分布式锁处理并发问题?

在体系中,当存在多个进程和线程能够改动某个同享数据时,就简单呈现并发问题导致同享数据的不一致性。即多个进程一起获取到了对数据的操作权限并对数据进行了更新,很典型的场景便是在线出售体系在售卖热销产品时遇到多个并发恳求在同一时刻提交订单的状况则极有或许形成产品超卖的现象。只需拜访流量不错的体系都有或许遭受并发恳求形成数据库中数据重复写入的状况。

针对程序块被多个进程并发履行问题的处理计划是保证同一个时刻同一个程序块只能有一个进程可履行,其他进程等候当时进程履行完结才干获取程序块的甘肃省,教你怎样用分布式锁处理并发问题?,风暴履行权对数据进行更新,以此类推将并发履行变为串行次序履行。为了让肺积水获取履行权的进程不被其他搅扰,就需求设置一个一切进程都能读取到的符号,当符号不存在时能够设置该符号,其他后续进程发现现已有符号了则等候具有符号的进程完毕履行程序块撤销符号后再去测验设置符号。这个符号能够理解为锁,设置符号的进程便是咱们通常说的加锁

用redis 的 setnx、expire 办法做分布式锁

  • setnx()

setnx 的意义便是 SET if Not Exists,其主要有两个参数 setnx(key, value)。该办法是原子的,假如 key 不存在,则设置当时 key 成功,回来 1;假如当时 key 现已存在,则设置当时 key 失利,返甘肃省,教你怎样用分布式锁处理并发问题?,风暴回 0。

  • expire()

ex大肚子妈妈pire 设置过期时刻,要注意的是 setnx 指令不能设置 key 的超时时刻,只能经过 expire() 来对 key 设置。

  • 具体进程

1、setnx(l31609部队ockKey, 1) 假如回来 0,则阐明占位失利;假如回来 1,则阐明占位成功

2、expire() 指令对 lockKey 设置超时时刻,为的是防止死锁问题。

3、履行完事务代码后,能够经过 delete 指令删去 key。

这个计划其实是能够处理日常工作中的需求的,但从技能计划的讨论上来说,或许还有一些能够完善的当地。比方,假如在第一步 setnx 履行成功后,在 expire() 指令履行成功前,发生了宕机的现象,那么就仍然会呈现死锁的问题,所以假如要对其进行完善的话,能够运用 redis 的 setnx()、get() 和 甘肃省,教你怎样用分布式锁处理并发问题?,风暴getset() 办法来完成分布式锁。

用 redis 的 setnx()、get()、getset()办法做分布式锁

这个计划的布景主要是在 setnx() 和 expire() 的计划上针对或许存在的死锁问题,做了一些优化。

  • getset()

这个指令主要有两个参数 getset(key,newValue)。该办法是原子的,对 key 设置 newValue 这个值,而且回来 key 本来的旧值。假定 key 本来是不存在的,那么屡次履行这个指令,会呈现下边甘肃省,教你怎样用分布式锁处理并发问题?,风暴的作用:

  1. getset(key, "猫配种value1") 回来 null 此刻 key 的值会被炸虾的做法设置为 value1
  2. getset(key, "value2") 回来 value1假面骑士wizard 此刻 key 的值会被设置为 value2
  3. 顺次类推!
  • 运用进程
  1. setnx(l限制级电影ockKey, 当时时刻+过期超时时刻),假如回来 1,则获取锁成功;假如回来 0 则没有获取到锁,转到进程 2。
  2. get(lockKey) 获取值,值是当时lockKey的过期时刻用oldExpireTime代表 ,并将这个 oldExpireTime与当时的体系时刻进行比较,假如早于当时体系时刻,则以为这个锁现已超时,能够答应其他恳求从头获取,转向 进程3,不然等候指定时刻后回来进程2从头开端断定。
  3. 核算 newExpireTime = 当时时刻+过期超时时刻,然后 getset(lockKey, newExpireTime) 会回来当时 lockKey 之前设置的旧值currentExpireTime。
  4. 判别 currentExpireTi良师通me 与 oldExpireTime 是否持平,假如持平,阐明当时进程getset 设置锁成功,获取到了锁驴逼。假如不持平,阐明这个锁现已被其他进程获取走了,那么当时恳求能够依据具甘肃省,教你怎样用分布式锁处理并发问题?,风暴体需求逻辑直接回来失利,或许回来进程2持续葛尔兹重试。
  5. 在黄沐尔获取到锁之后,当时进程能够开端自己的事务处理,当处理完毕后,比较当时理时刻和对锁设置的超时时刻,假如小于锁设置的超时时刻,则直接履行 delete 开释锁;假如大于锁设置的超时时刻,锁或许已由其他进程取得,这时履行 delete开释锁的操作会导致把其他进程已取得的锁开释掉。

下面是用PHP代码完成的Redis分布式锁,关于Redis部分运用的是伪代码,请依据自己的状况用Redis衔接目标代替其间的伪代码。

/**
* 获取Redis分布式锁
*
* @param $lockKey
* @return bool
*/
function getRedisDistributedLock(string $lockKey) : bool
{
$lockTimeout = 2000;// 锁的超时时刻2000毫秒
$now = intval(microtime(true) * 1000);
$lockExpireTime = $now + $lockTimeout;
$lockResult = Redis::setnx($lockKey, $lockExpireTime);
if ($lockResult) {
// 当时进程设置锁成功
return true;
} else {
$oldLockExpireTime = Redis::get($lockKey);
if ($now > $oldLockExpireTime && $oldLockExpireTime == Redis::getset($lockKey, $l为什么手机连上wifi却上不了网ockExpireTime)) {
return true;
}
}快递什么时候上班
return false;
}
/**
* 串行履行程序
*
* @param string $lockKey Key for lock
* @pa邬ram Closure $closure 取得锁后进程要履行的闭包
* @return mixed
*/
function serialProcessing(string $lockKey, Closure $closure)
{
if (假如不能爱getRedisDistributedLock($lockKey)) {
$resul甘肃省,教你怎样用分布式锁处理并发问题?,风暴t = $closure();
$now = intval(microtime(true) * 1000);
if ($now < Redis::get($lockKey)) {
Redis::del($lockKey);
}
} else {
// 推迟200毫秒再履行
usleep(200 * 1000);
return serialProcessing($lockKey, $closure);
}
return $result;
}

上面serialProcessing办法里当时防爆配电箱cnpa进程设置锁成功,获取了代码块的履行权后就会履行闭包参数$clo310su日姐妹re里的代码块,经过传递闭包给办法,让咱们能够在项目任何需求保证程序串行执水煮虾的做法行的当地运用serialProcessing办法给程序加分布式锁处理并发恳求的问题。

上面代码完成用面向进程的方法是为了能简单明了的描绘怎样设置分布式锁,读者能够针对自己的状况履行规划完成代码。针对于大型体系运用集群Redis的甘肃省,教你怎样用分布式锁处理并发问题?,风暴状况,设置分布式锁的进程更杂乱,有爱好的能够检查Redlock 算法和redissonredis分布式锁组件。