Redis-distributed lock (PHP implementation)

Redis implements distributed locks

Application scenarios (for example, solving the problem of inventory oversold)

How did inventory oversold occur?

Let's first take a look at what does the so-called e-commerce inventory oversold mean if distributed locks are not used? Take a look at the following picture:
Insert picture description here
This picture is actually very clear. Assuming that the order system is deployed on two machines, different users have to buy 10 iPhones at the same time, and they send a request to the order system.

Then every order system instance was checked in the database, and the current iphone inventory is 12 units.

The two big brothers looked at them and were happy. The inventory of 12 units is greater than the number of 10 units to buy!

Since then, each order system instance sends SQL to the database to place an order, and then deducts 10 inventory, one of which deducts the inventory from 12 to 2, and the other deducts the inventory from 2 to -8 station.

Now it's over, the inventory has a negative number! Tears rush, there are not 20 iPhones sent to two users! How can this be good.

How to solve the problem of inventory oversold with distributed locks?

For a lock key, only one client can obtain the lock at the same time, and other clients will wait infinitely to try to obtain that lock. Only the client that has obtained the lock can execute the following business logic.
Insert picture description here
You can follow the step number above and read it again, and you will immediately understand.

As you can see from the above figure, only one instance of the order system can successfully add a distributed lock, and then only one instance can check the inventory, determine whether the inventory is sufficient, place an order to deduct the inventory, and then release the lock.

After the lock is released, another order system instance can be locked, and then check the inventory. It is found that there are only 2 units in the inventory, and the inventory is insufficient to purchase, and the order fails. Will not deduct the inventory to -8.

Is there any other solution to solve the problem of oversold inventory?

Of course there is! For example, pessimistic locks, distributed locks, optimistic locks, queue serialization, asynchronous queue decentralization, Redis atomic operations, etc., for many solutions, we have our own set of optimization mechanisms for inventory oversold.

But as mentioned earlier, this article will talk about the concurrent optimization of distributed locks, not about the solution of inventory oversold, so inventory oversold is just a business scenario.

In the future, I have the opportunity to write an article about the solution to the problem of e-commerce inventory oversold. This article first focuses on the optimization of a distributed lock concurrency. I hope everyone understands the intention and background to avoid some brothers not reading it. Clear and vomit.

And I suggest that even if you have objections to the content of the article, leave a message on the backstage of the official account to discuss with me. The technology is to communicate more, open up ideas, and collide with thinking.

What conditions should a distributed lock have:

1. In a distributed system environment, a method can only be executed by one thread of one machine at the same time;
2. High-availability acquisition and release of locks;
3. High-performance acquisition and release of locks;
4. Available Re-entry feature;
5. With lock failure mechanism to prevent deadlock;
6. With non-blocking lock feature, that is, if the lock is not acquired, it will directly return to the failure of acquiring the lock.

PHP implements Redis distributed lock

class RedisMutexLock
{
    
    
    /**
     * 缓存 Redis 连接。
     *
     * @return void
     */
    public static function getRedis()
    {
    
    
        // 这行代码请根据自己项目替换为自己的获取 Redis 连接。
        return YCache::getRedisClient();
    }

    /**
     * 获得锁,如果锁被占用,阻塞,直到获得锁或者超时。
     * -- 1、如果 $timeout 参数为 0,则立即返回锁。
     * -- 2、建议 timeout 设置为 0,避免 redis 因为阻塞导致性能下降。请根据实际需求进行设置。
     *
     * @param  string  $key         缓存KEY。
     * @param  int     $timeout     取锁超时时间。单位(秒)。等于0,如果当前锁被占用,则立即返回失败。如果大于0,则反复尝试获取锁直到达到该超时时间。
     * @param  int     $lockSecond  锁定时间。单位(秒)。
     * @param  int     $sleep       取锁间隔时间。单位(微秒)。当锁为占用状态时。每隔多久尝试去取锁。默认 0.1 秒一次取锁。
     * @return bool 成功:true、失败:false
     */
    public static function lock($key, $timeout = 0, $lockSecond = 20, $sleep = 100000)
    {
    
    
        if (strlen($key) === 0) {
    
    
            // 请更换为自己项目抛异常的方法。
            YCore::exception(500, '缓存KEY没有设置');
        }
        if (!is_int($timeout) || $timeout < 0) {
    
    
            YCore::exception(500, "timeout 参数设置有误");
        }
        $start = self::getMicroTime();
        $redis = self::getRedis();
        do {
    
    
            // [1] 锁的 KEY 不存在时设置其值并把过期时间设置为指定的时间。锁的值并不重要。重要的是利用 Redis 的特性。
            $acquired = $redis->set("Lock:{
      
      $key}", 1, ['NX', 'EX' => $lockSecond]);
            if ($acquired) {
    
    
                break;
            }
            if ($timeout === 0) {
    
    
                break;
            }
            usleep($sleep);
        } while ((self::getMicroTime()) < ($start + ($timeout * 1000000)));
        return $acquired ? true : false;
    }

    /**
     * 释放锁
     *
     * @param  mixed  $key  被加锁的KEY。
     * @return void
     */
    public static function release($key)
    {
    
    
        if (strlen($key) === 0) {
    
    
            // 请更换为自己项目抛异常的方法。
            YCore::exception(500, '缓存KEY没有设置');
        }
        $redis = self::getRedis();
        $redis->del("Lock:{
      
      $key}");
    }

    /**
     * 获取当前微秒。
     *
     * @return bigint
     */
    protected static function getMicroTime()
    {
    
    
        return bcmul(microtime(true), 1000000);
    }
}

The content is organized by referring to online articles, only for personal learning use:

  1. https://zhuanlan.zhihu.com/p/95217001
  2. https://www.cnblogs.com/liuqingzheng/p/11080501.html
  3. https://blog.csdn.net/C18298182575/article/details/92786579

Guess you like

Origin blog.csdn.net/magentodaddy/article/details/108538672