PHP implements Redis document lock to prevent concurrent write ____php

Source: Internet
Author: User
Tags delete key sprintf unique id

first, write in front:
In the entire supply chain system, there will be many kinds of documents (purchase orders, warehousing orders, delivery orders, waybill and so on, in connection with the writing of the document data interface (increase the deletion operation), even if the front-end to do the relevant restrictions, or because of the network or abnormal operation caused by concurrent repeated calls, resulting in the same document to do the same treatment;

To prevent this from causing an abnormal impact on the system, we have implemented a simple document lock through Redis, each request must acquire a lock to execute the business logic, the execution will not release the lock after completion; The concurrent duplicate operation of the same document is guaranteed only one request can acquire the lock (a single thread dependent on the Redis), is a kind of pessimistic lock design;

Note: Redis lock in our system is generally used only to solve the situation of concurrent duplicate requests, for the concurrency of repeated requests will generally go to the database or log check the state of the data, the two mechanisms combined to ensure the reliability of the entire link.

second, the lock mechanism:
Primarily relies on Redis setnx instruction implementations:

However, there is a problem with setnx, that is, the SETNX directive does not support setting the expiration time, you need to use the expire directive to set the timeout time for the key, so that the entire lock operation is not an atomic operation, there may be a setnx lock success, but due to the program exception exit caused the timeout , in the case of not unlocking in time, it may lead to deadlock (even if the business scene does not appear deadlock, useless key has been resident memory is not a good design);

This situation can be solved using Redis transactions, SETNX and expire two instructions as an atomic operation, but this is relatively cumbersome to do, but after Redis 2.6.12 version, Redis set instructions support NX, ex mode, and supports the atomization setting of expiration times:

Third, lock implementation (complete test code will be posted at the end):

/** * Plus document LOCK * @param int $intOrderId Document ID * @param int $intExpireTime Lock Expiration Time (sec) * @return Bool|int Lock successfully returns a unique lock ID, lock failure return FALSE */public static function Addlock ($intOrderId, $intExpireTime = sel
            F::redis_lock_default_expire_time) {//parameter check if (Empty ($intOrderId) | | | $intExpireTime <= 0) {
        return false;

        //Get Redis connection $objRedisConn = Self::getredisconn ();

        Generate a unique lock ID, unlock the need to hold this id $intUniqueLockId = Self::generateuniquelockid ();

        Based on the template, combined with the document ID, generates a unique Redis key (typically, the document ID is unique in the business system) $strKey = sprintf (self::redis_lock_key_template, $intOrderId); Lock (implemented by Redis SETNX instructions, starting with Redis 2.6.12, optional parameters via set directives can also be setnx, and the timeout can be set atomically) $bolRes = $objRedisConn->se

        T ($strKey, $intUniqueLockId, [' NX ', ' ex ' => $intExpireTime]); Lock successfully return lock ID, lock failure returns false return $bolRes?
    $intUniqueLockId: $bolRes; }

four, unlock mechanism:
Unlock is the only lock ID when lock, if successful, delete key; it should be noted that the entire process of unlocking the same need to ensure atomicity, which relies on Redis watch and transaction implementation;

The watch command can monitor one or more keys, and once a key is modified (or deleted), subsequent transactions are not executed. The monitor lasts until the EXEC command (the commands in the transaction are executed after exec, so you can modify the key values of watch monitoring after the multi command)
Redis Watch and Business can refer to Jane book article: Https://www.jianshu.com/p/361cb9cd13d5

Unlock implementation (full test code will be posted at the end):

/**
     * Solution document LOCK
     * @param int $intOrderId Document ID
     * @param int $intLockId lock Unique ID
     * @return bool
    /Public St atic function ReleaseLock ($intOrderId, $intLockId)
    {
        //parameter Check
        if (Empty ($intOrderId) | | empty ($INTLOCKID)) {return
            false;
        }

        Get the Redis connection
        $objRedisConn = Self::getredisconn ();

        Generate Redis key
        $strKey = sprintf (self::redis_lock_key_template, $intOrderId);

        Monitoring Redis key prevents modification or deletion in the "Lock ID" and "Unlock Transaction Execution", and automatically cancels the monitor after committing the transaction, and other situations need to be manually unblocked
        $objRedisConn->watch ($strKey);
        if ($intLockId = = $objRedisConn->get ($strKey)) {
            $objRedisConn->multi ()->del ($strKey)->exec ();
            return true;
        }
        $objRedisConn->unwatch ();
        return false;
    }

six, with the overall test code (this code is only a simple version)

<?php/** * Class Lock_service Document Lock Service/class Lock_service {/** * document lock Redis Key template */const redis_l

    ock_key_template = ' order_lock_%s ';

    /** * Document Lock Default timeout (sec)/Const REDIS_LOCK_DEFAULT_EXPIRE_TIME = 86400; /** * Plus document LOCK * @param int $intOrderId Document ID * @param int $intExpireTime Lock Expiration (sec) * @return Bool|int plus lock successful return Back to a unique lock ID, lock failure return FALSE */public static function Addlock ($intOrderId, $intExpireTime = Self::redis_lock_default_expir
        E_time) {//parameter check if (Empty ($intOrderId) | | | $intExpireTime <= 0) {return false;

        //Get Redis connection $objRedisConn = Self::getredisconn ();

        Generate a unique lock ID, unlock the need to hold this id $intUniqueLockId = Self::generateuniquelockid ();

        Based on the template, combined with the document ID, generates a unique Redis key (typically, the document ID is unique in the business system) $strKey = sprintf (self::redis_lock_key_template, $intOrderId); Lock (implemented by Redis SETNX instructions, starting with Redis 2.6.12, optional parameters through set directives can also be implemented SETNX, and the timeout time can be set atomically) $bolRes = $objRedisConn->set ($strKey, $intUniqueLockId, [' NX ', ' ex ' => $intExpireTime]); Lock successfully return lock ID, lock failure returns false return $bolRes?
    $intUniqueLockId: $bolRes;
    /** * Solution Document LOCK * @param int $intOrderId Document ID * @param int $intLockId lock Unique ID * @return BOOL/ public static function ReleaseLock ($intOrderId, $intLockId) {//parameter check if (Empty ($intOrderId) | | empt
        Y ($intLockId)) {return false;

        //Get Redis connection $objRedisConn = Self::getredisconn ();

        Generate Redis Key $strKey = sprintf (self::redis_lock_key_template, $intOrderId);
        Monitoring Redis key prevents modification or deletion in the "Lock ID" and "Unlock Transaction Execution", and automatically cancels the monitor after committing the transaction, and other situations need to be manually unblocked $objRedisConn->watch ($strKey);
            if ($intLockId = = $objRedisConn->get ($strKey)) {$objRedisConn->multi ()->del ($strKey)->exec ();
        return true;
        } $objRedisConn->unwatch ();
    return false; }

    /**
     *Redis configuration: IP/Const REDIS_CONFIG_HOST = ' 127.0.0.1 ';

    /** * Redis Configuration: Port * * Const REDIS_CONFIG_PORT = 6379; /** * Get Redis connection (simple version, available in single instance) * @param string $strIp IP * @param int $intPort port * @return Object Redis
    Connection */public static function Getredisconn ($strIp = self::redis_config_host, $intPort = self::redis_config_port)
        {$objRedis = new Redis ();
        $objRedis->connect ($strIp, $intPort);
    return $objRedis;

    /** * Redis key to generate a unique lock ID/const Redis_lock_unique_id_key = ' lock_unique_id '; /** * Generate lock Unique ID (Redis incr instructions to achieve a simple version, can be combined with date, time stamp, Fetch, string fill, random number functions, generate a specified number of digits unique ID) * @return mixed * * Public stat
    IC function Generateuniquelockid () {return self::getredisconn ()->incr (Self::redis_lock_unique_id_key);
}//test $res 1 = lock_service::addlock (' 666666 ');
Var_dump ($res 1);//return lock ID, lock success $res 2 = Lock_service::addlock (' 666666 '); Var_dump ($res 2);//falsE, lock failure $res 3 = Lock_service::releaselock (' 666666 ', $res 1);
Var_dump ($res 3);//true, unlock success $res 4 = Lock_service::releaselock (' 666666 ', $res 1); Var_dump ($res 4);//false, Unlock failed

Blog move: https://segmentfault.com/blog/leeonfancy

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.