Business background: Locks are needed in room chess and card games to prevent dirty reading of redis data caused by concurrent operations; for example, adding the action of a user entering the room:
In the case of concurrency, get RoomUsers will have dirty reads;
Solution: lock the room to allow only one client to operate in a room at a time, and other concurrent clients The end is waiting; that is ----- blocking lock;
Lock: There are several redis locking methods: incr, set, setnx, hSetnx, you can refer to this article: Several redis locking methods A kind of implementation
Recommendation:PHP video tutorial
Here I use set this way
$roomId = $_GET['roomId']; $user = $_GET['user']; // '张三' $key = "LockRoom:{$roomId}"; $value = $roomId.uniqid(); $ex = 3; // 如果 $key 不存在的话,就设置 $key 的值为 $value,且有效期为 3s; // return TRUE / FALSE while(true){ $res = $this->redis->set($key, $value, ['nx', 'ex' => $ex]); if($res) { break; } usleep(5000); } // 将用户添加进房间 $roomUsers = $this->redis->get("Room:{$roomId}:Users"); // ['李四', '王五'] $roomUsers[] = $user; $this->redis->set("Room:{$roomId}:Users", $roomUsers); // ['李四', '王五', '张三']
Unlocking: Of course you have to unlock it after the operation, no You have to wait at least 3 seconds to unlock;
To unlock, use delete to delete the key; but there is a pitfall here. Delete cannot be used directly, because it is assumed that client01 obtains the lock and the time exceeds 3 seconds during the process of adding users to the room. , at this time client02 will also obtain the lock and set 3S, and then when client01 completes the operation and delete key, the lock set by client02 will be deleted;
It is recommended to use lua code to perform deletion, because lua execution is atomic sex.
// 将用户添加进房间 $roomUsers = $this->redis->get("Room:{$roomId}:Users"); // ['李四', '王五'] $roomUsers[] = $user; $this->redis->set("Room:{$roomId}:Users", $roomUsers); // ['李四', '王五', '张三'] // lua 脚本解锁 // 先判断 key的值是否为 value, TRUE 才会删除, 所以 $value 的设计要有随机唯一性 $script = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end '; $this->redis->eval($script, array($key , $value), 1);
For more related knowledge, please pay attention to redis introductory tutorialcolumn
The above is the detailed content of PHP+redis implements locking and unlocking operations. For more information, please follow other related articles on the PHP Chinese website!