почему я не могу получить кешированные данные в транзакции 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 в этом случае?


person Austin Kong    schedule 07.03.2017    source источник


Ответы (1)


Вам нужно понять, как работают транзакции Redis — в двух словах, все команды, выполняющие транзакцию, буферизуются клиентом (predis в вашем случае), а затем одновременно отправляются на сервер. Ваш код пытается использовать результат запроса на чтение (get) до выполнения транзакции. Дополнительные сведения см. в документации: https://redis.io/topics/transactions.

Либо прочитайте qty вне транзакции и используйте WATCH для защиты от конкурирующих обновлений, либо переместите эту логику в целиком в сценарий Lua.

person Itamar Haber    schedule 07.03.2017
comment
Большое тебе спасибо! Я прочитал документацию, которую вы упомянули. Теперь я знаю, как работают транзакции Redis. поэтому я изменил свой код на оптимистическую блокировку с помощью решения CAS, теперь значение qty правильное, наконец, оно достигает 10, но записей в базе данных все еще намного больше, чем 10. знаете, почему? - person Austin Kong; 07.03.2017
comment
Извините - я не понимаю вопроса. - person Itamar Haber; 07.03.2017
comment
извините за это, я обновил свой исходный вопрос второй версией исходного кода. новый вопрос есть. Спасибо! - person Austin Kong; 08.03.2017
comment
Если я правильно понимаю, вы пытаетесь реализовать в своем PHP-коде распределенную транзакцию между двумя базами данных - это нетривиальная задача. Я предлагаю вам вывести операцию dbmodel за рамки Redis tx и вызывать ее только после успешного завершения Redis tx. - person Itamar Haber; 09.03.2017
comment
Да, ты прав! Теперь он работает, когда я выношу операцию db за пределы транзакции Redis. большое спасибо! - person Austin Kong; 28.03.2017