为什么我在 Redis 事务中获取不到缓存数据?
why I cannot get cached data in transaction of Redis?
我在PHP写了一个抽奖程序,因为这个程序的并发请求量很大,我限制了每个奖品的数量,在这个例子中是10个。我不想看到任何奖金超过股票。所以我把整个逻辑放在 Redis 的事务中(我使用 predis(https://github.com/nrk/predis) 作为我的 PHP redis 客户端),但是它不起作用,在对该程序请求超过 10 次之后,我在数据库中发现了 10 多条我无法理解的记录。有谁知道原因吗?非常感谢您的解释,谢谢!
这是我的 PHP 代码:
$this->load->model('Lottery_model');
$money = $this->_get_lottery();//which prize do you get
if($money > 0){
$key = $this->_get_sum_key($money);
$dbmodel = $this->Lottery_model;
// Executes a transaction inside the given callable block:
$responses = $redis->transaction(function ($tx) use ($key, $money, $dbmodel){
$qty = intval($tx->get($key));
if($qty < 10){
//not exceed the stock limit
$dbmodel->add($customer, $money); // insert record into db
$tx->incr($key);
}else{
log_message('debug', $money . ' dollar exceed the limit');
}
});
}else{
log_message('debug', 'you are fail');
}
看了Redis事务的文档后,我知道上面代码的用法是完全错误的。然后我将它修改为以下版本,使用乐观锁和检查并设置。
$options = array(
'cas' => true, // Initialize with support for CAS operations
'watch' => $key, // Key that needs to be WATCHed to detect changes
'retry' => 3,
);
try{
$responses = $redis->transaction($options, function ($tx) use ($key, $money, $username, $dbmodel, &$qty){
$qty = intval($tx->get($key));
if($qty < 10){
$tx->multi();
$tx->incr($key);
$dbmodel->add($username, $money);// insert into mysql db
}else{
log_message('debug', $money . ' dollar exceed the limit');
}
});
}catch(PredisException $e){
log_message('debug', 'redis transaction failed');
}
但问题是数据库中的记录数超过了奖品的限制,Redis中保存的总数不会。解决此类问题的常见解决方案是什么?在这种情况下我必须锁定 INNodb table 吗?
您需要了解 Redis 事务的工作原理 - 简而言之,所有进行事务的命令都由客户端(在您的情况下为 predis)进行缓冲,然后一次性发送到服务器。您的代码尝试在事务执行之前使用读取请求 (get
) 的结果。详情请参考文档:https://redis.io/topics/transactions
要么在事务外读取 qty
,并使用 WATCH
来防止竞争更新,要么将此逻辑整个移动到 Lua 脚本。
我在PHP写了一个抽奖程序,因为这个程序的并发请求量很大,我限制了每个奖品的数量,在这个例子中是10个。我不想看到任何奖金超过股票。所以我把整个逻辑放在 Redis 的事务中(我使用 predis(https://github.com/nrk/predis) 作为我的 PHP redis 客户端),但是它不起作用,在对该程序请求超过 10 次之后,我在数据库中发现了 10 多条我无法理解的记录。有谁知道原因吗?非常感谢您的解释,谢谢!
这是我的 PHP 代码:
$this->load->model('Lottery_model');
$money = $this->_get_lottery();//which prize do you get
if($money > 0){
$key = $this->_get_sum_key($money);
$dbmodel = $this->Lottery_model;
// Executes a transaction inside the given callable block:
$responses = $redis->transaction(function ($tx) use ($key, $money, $dbmodel){
$qty = intval($tx->get($key));
if($qty < 10){
//not exceed the stock limit
$dbmodel->add($customer, $money); // insert record into db
$tx->incr($key);
}else{
log_message('debug', $money . ' dollar exceed the limit');
}
});
}else{
log_message('debug', 'you are fail');
}
看了Redis事务的文档后,我知道上面代码的用法是完全错误的。然后我将它修改为以下版本,使用乐观锁和检查并设置。
$options = array(
'cas' => true, // Initialize with support for CAS operations
'watch' => $key, // Key that needs to be WATCHed to detect changes
'retry' => 3,
);
try{
$responses = $redis->transaction($options, function ($tx) use ($key, $money, $username, $dbmodel, &$qty){
$qty = intval($tx->get($key));
if($qty < 10){
$tx->multi();
$tx->incr($key);
$dbmodel->add($username, $money);// insert into mysql db
}else{
log_message('debug', $money . ' dollar exceed the limit');
}
});
}catch(PredisException $e){
log_message('debug', 'redis transaction failed');
}
但问题是数据库中的记录数超过了奖品的限制,Redis中保存的总数不会。解决此类问题的常见解决方案是什么?在这种情况下我必须锁定 INNodb table 吗?
您需要了解 Redis 事务的工作原理 - 简而言之,所有进行事务的命令都由客户端(在您的情况下为 predis)进行缓冲,然后一次性发送到服务器。您的代码尝试在事务执行之前使用读取请求 (get
) 的结果。详情请参考文档:https://redis.io/topics/transactions
要么在事务外读取 qty
,并使用 WATCH
来防止竞争更新,要么将此逻辑整个移动到 Lua 脚本。