Der Inhalt dieses Artikels besteht darin, Ihnen mitzuteilen, wie PHP die Redis-Dokumentsperre implementiert und das gleichzeitige wiederholte Schreiben verhindert. Es hat einen bestimmten Referenzwert.
1. Schreiben Sie im Vorherigen:
Im gesamten Lieferkettensystem wird es viele Arten von Dokumenten geben (Bestellungen, Lageraufträge, Ankunftsaufträge, Frachtbriefe usw.), wenn es um die Schnittstelle zum Schreiben von Dokumentdaten geht ( Vorgänge hinzufügen, löschen, ändern) Selbst wenn das Front-End entsprechende Einschränkungen vorgenommen hat, ist es dennoch möglich, dass aufgrund von Netzwerk- oder abnormalen Vorgängen gleichzeitige wiederholte Aufrufe auftreten, was zur gleichen Verarbeitung desselben Dokuments führt
2. Sperrmechanismus: Verlässt sich hauptsächlich auf die Redis-Setnx-Anweisung:
Aber es gibt ein Problem bei der Verwendung von Setnx, das heißt Die setnx-Anweisung unterstützt die Einstellung der Ablaufzeit nicht. Sie müssen den Befehl „expire“ verwenden, um eine Zeitüberschreitung für den Schlüssel festzulegen. Auf diese Weise ist der gesamte Sperrvorgang keine atomare Operation. Es ist möglich, dass die setnx-Sperre erfolgreich ist. Aufgrund des abnormalen Beendens des Programms wurde das Timeout jedoch nicht erfolgreich festgelegt und die Sperre wurde nicht rechtzeitig aufgehoben. In diesem Fall kann es zu einem Deadlock kommen (auch wenn im Geschäftsszenario kein Deadlock auftritt, ist dies kein Fall). gutes Design, damit nutzlose Schlüssel im Speicher verbleiben);
3. Sperrimplementierung (der vollständige Testcode wird am Ende veröffentlicht ):
/** * 加单据锁 * @param int $intOrderId 单据ID * @param int $intExpireTime 锁过期时间(秒) * @return bool|int 加锁成功返回唯一锁ID,加锁失败返回false */ public static function addLock($intOrderId, $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME) { //参数校验 if (empty($intOrderId) || $intExpireTime <= 0) { return false; } //获取Redis连接 $objRedisConn = self::getRedisConn(); //生成唯一锁ID,解锁需持有此ID $intUniqueLockId = self::generateUniqueLockId(); //根据模板,结合单据ID,生成唯一Redis key(一般来说,单据ID在业务中系统中唯一的) $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId); //加锁(通过Redis setnx指令实现,从Redis 2.6.12开始,通过set指令可选参数也可以实现setnx,同时可原子化地设置超时时间) $bolRes = $objRedisConn->set($strKey, $intUniqueLockId, ['nx', 'ex'=>$intExpireTime]); //加锁成功返回锁ID,加锁失败返回false return $bolRes ? $intUniqueLockId : $bolRes; }
4. Entsperrmechanismus: Entsperren bedeutet den Vergleich der eindeutigen Schloss-ID beim Sperren. Wenn der Vergleich erfolgreich ist, wird der Schlüssel gelöscht. Es ist zu beachten, dass der gesamte Entsperrvorgang auch die Atomizität gewährleisten muss.
Der WATCH-Befehl kann einen oder mehrere Schlüssel überwachen Schlüssel geändert (oder gelöscht) werden, werden nachfolgende Transaktionen nicht ausgeführt. Die Überwachung wird bis zum EXEC-Befehl fortgesetzt (Befehle in der Transaktion werden nach EXEC ausgeführt, sodass der Schlüsselwert der WATCH-Überwachung nach dem MULTI-Befehl geändert werden kann)Informationen zu Redis-Überwachung und -Transaktionen finden Sie im kurzen Buchartikel: https://www.jianshu.com/p/361cb9cd13d5
5. Entsperrungsimplementierung (der vollständige Testcode wird angezeigt). am Ende gepostet):
/** * 解单据锁 * @param int $intOrderId 单据ID * @param int $intLockId 锁唯一ID * @return bool */ public static function releaseLock($intOrderId, $intLockId) { //参数校验 if (empty($intOrderId) || empty($intLockId)) { return false; } //获取Redis连接 $objRedisConn = self::getRedisConn(); //生成Redis key $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId); //监听Redis key防止在【比对lock id】与【解锁事务执行过程中】被修改或删除,提交事务后会自动取消监控,其他情况需手动解除监控 $objRedisConn->watch($strKey); if ($intLockId == $objRedisConn->get($strKey)) { $objRedisConn->multi()->del($strKey)->exec(); return true; } $objRedisConn->unwatch(); return false; }
6. Im Anhang ist der Gesamttestcode (dieser Code ist nur eine einfache Version)
<?php/** * Class Lock_Service 单据锁服务 */class Lock_Service { /** * 单据锁redis key模板 */ const REDIS_LOCK_KEY_TEMPLATE = 'order_lock_%s'; /** * 单据锁默认超时时间(秒) */ const REDIS_LOCK_DEFAULT_EXPIRE_TIME = 86400; /** * 加单据锁 * @param int $intOrderId 单据ID * @param int $intExpireTime 锁过期时间(秒) * @return bool|int 加锁成功返回唯一锁ID,加锁失败返回false */ public static function addLock($intOrderId, $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME) { //参数校验 if (empty($intOrderId) || $intExpireTime <= 0) { return false; } //获取Redis连接 $objRedisConn = self::getRedisConn(); //生成唯一锁ID,解锁需持有此ID $intUniqueLockId = self::generateUniqueLockId(); //根据模板,结合单据ID,生成唯一Redis key(一般来说,单据ID在业务中系统中唯一的) $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId); //加锁(通过Redis setnx指令实现,从Redis 2.6.12开始,通过set指令可选参数也可以实现setnx,同时可原子化地设置超时时间) $bolRes = $objRedisConn->set($strKey, $intUniqueLockId, ['nx', 'ex'=>$intExpireTime]); //加锁成功返回锁ID,加锁失败返回false return $bolRes ? $intUniqueLockId : $bolRes; } /** * 解单据锁 * @param int $intOrderId 单据ID * @param int $intLockId 锁唯一ID * @return bool */ public static function releaseLock($intOrderId, $intLockId) { //参数校验 if (empty($intOrderId) || empty($intLockId)) { return false; } //获取Redis连接 $objRedisConn = self::getRedisConn(); //生成Redis key $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId); //监听Redis key防止在【比对lock id】与【解锁事务执行过程中】被修改或删除,提交事务后会自动取消监控,其他情况需手动解除监控 $objRedisConn->watch($strKey); if ($intLockId == $objRedisConn->get($strKey)) { $objRedisConn->multi()->del($strKey)->exec(); return true; } $objRedisConn->unwatch(); return false; } /** * Redis配置:IP */ const REDIS_CONFIG_HOST = '127.0.0.1'; /** * Redis配置:端口 */ const REDIS_CONFIG_PORT = 6379; /** * 获取Redis连接(简易版本,可用单例实现) * @param string $strIp IP * @param int $intPort 端口 * @return object Redis连接 */ public static function getRedisConn($strIp = self::REDIS_CONFIG_HOST, $intPort = self::REDIS_CONFIG_PORT) { $objRedis = new Redis(); $objRedis->connect($strIp, $intPort); return $objRedis; } /** * 用于生成唯一的锁ID的redis key */ const REDIS_LOCK_UNIQUE_ID_KEY = 'lock_unique_id'; /** * 生成锁唯一ID(通过Redis incr指令实现简易版本,可结合日期、时间戳、取余、字符串填充、随机数等函数,生成指定位数唯一ID) * @return mixed */ public static function generateUniqueLockId() { return self::getRedisConn()->incr(self::REDIS_LOCK_UNIQUE_ID_KEY); } }//test$res1 = Lock_Service::addLock('666666'); var_dump($res1);//返回lock id,加锁成功$res2 = Lock_Service::addLock('666666'); var_dump($res2);//false,加锁失败$res3 = Lock_Service::releaseLock('666666', $res1); var_dump($res3);//true,解锁成功$res4 = Lock_Service::releaseLock('666666', $res1); var_dump($res4);//false,解锁失败
PHP-Konfiguration und Anweisungen zum Erweitern von Redis unter Windows
Freigabe von PHP-Befehlen zum Betrieb von Redis
Detaillierte Erläuterung der Redis-Speichersperre und PHP, die gleichzeitige Vorgänge verhindert
Das obige ist der detaillierte Inhalt vonPHP implementiert die Redis-Dokumentsperre und verhindert gleichzeitiges wiederholtes Schreiben. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!