为什么需要加锁?
为了在高并发下能够保持数据的一致性,避免脏数据。在分布式的集群环境中,就需要使用分布式锁来保证不同节点的线程同步执行。
分布式锁的基本条件
为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下几个条件:
1、互斥性。在任意时刻,只有一个客户端能持有锁。
2、不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
3、解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了,即不能误解锁。
分布式锁实现
这里,我们使用Redis SET 命令的 NX 和 EX 参数来实现分布式锁!
参数介绍:
- EX second :设置键的过期时间为 second 秒。
- NX :只在键不存在时,才对键进行设置操作。
PHP代码实现:
Lock.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| <?php
class Lock {
private $scene;
private $config;
private $redis;
private $lockValue;
private $expire = 5;
public function __construct($scene = 'kill', $config = []) { $this->scene = $scene; $this->config = $config ? $config : ['host' => '127.0.0.1', 'port' => 6379]; $this->redis = $this->connect(); }
public function connect() { $redis = new Redis(); $redis->connect($this->config['host'], $this->config['port']); return $redis; }
public function lock() { $this->lockValue = md5(uniqid()); return $this->redis->set($this->scene, $this->lockValue , ['NX', 'EX' => $this->expire]); }
public function unLock() { $script = <<<LUA local key=KEYS[1] local value=ARGV[1] if(redis.call('get', key) == value) then return redis.call('del', key) end LUA; $this->redis->eval($script, [$this->scene, $this->lockValue], 1); } }
|
实例:
demo.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
include 'Lock.php';
use Swoole\Timer;
$Lock = new Lock();
Timer::tick(1000, function($timer_id) use ($Lock){ if ($Lock->lock()) { echo '['.date('Y-m-d H:i:s').'] ' . '请求成功!' . PHP_EOL; $Lock->unLock(); } else { echo '['.date('Y-m-d H:i:s').'] ' . '操作频繁,请稍候再试!' . PHP_EOL; } });
|
搞定!