How transactions work in Redis
I have been working with Redis relatively recently and now there is a need to change one key by several threads at the same time. To work with Redis in php, I use the Rediska client. Even when I read the Rediska manual, I saw a section about transactions , but today it's time to read more carefully.
I don’t know what I blame, or my poor knowledge of English, or my slow-wittedness, or the incomprehensibility of the documentation, but nevertheless, having read the transaction documentation on the Rediska website and then on the Redis website itself, I still did not understand if it’s blocked, mutable inside the transaction, the write key before execute () is executed or not.
Yes, in both documentation there is a description and examples of "Optimistic locking using check-and-set" when watch is used, but at the same time at the beginning of the official documentation on the Redis website it says:
All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served in the middle of the execution of a Redis transaction. This guarantees that the commands are executed as a single isolated operation.
After reading this, it seemed to me (I’ll cross over just in case) that it was just about the fact that no one in the middle of a transaction could change the values of the keys affected by it. However, I was mistaken and in order to understand this I had to write a script by analogy with that given in the dock on the Rediska client site.
This script ran in two threads from the command line, i.e. in theory, as a result, I had to get the value of the key "test_value" equal to 20 thousand, but in reality there was an average of about 12 thousand. Those. there is no lock.
Now I modify the loop a bit by adding watch:
Those. in fact, when an occurrence occurs, we try to repeat the transaction 5 times. As a result, with 5 repetitions, an average of 17 thousand came out, if we raise it to say 10, an average of 19 thousand will come out.
These are of course special cases, in practice it is unlikely that such a number of simultaneous changes and 5 repetitions should, in principle, be enough, but that is not the point. The fact is that, in fact, Redis does not yet have a mechanism (documented) for locking keys affected by changes.
It is not for me to decide whether it is good or bad to judge, I think it all depends on the task, I just wanted to show how it is.
I don’t know what I blame, or my poor knowledge of English, or my slow-wittedness, or the incomprehensibility of the documentation, but nevertheless, having read the transaction documentation on the Rediska website and then on the Redis website itself, I still did not understand if it’s blocked, mutable inside the transaction, the write key before execute () is executed or not.
Yes, in both documentation there is a description and examples of "Optimistic locking using check-and-set" when watch is used, but at the same time at the beginning of the official documentation on the Redis website it says:
All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served in the middle of the execution of a Redis transaction. This guarantees that the commands are executed as a single isolated operation.
After reading this, it seemed to me (I’ll cross over just in case) that it was just about the fact that no one in the middle of a transaction could change the values of the keys affected by it. However, I was mistaken and in order to understand this I had to write a script by analogy with that given in the dock on the Rediska client site.
$ options = array (
'namespace' => 'Application_',
'servers' => array (
array ('host' => '127.0.0.1', 'port' => 6379, 'db' => 5)
)
) ;
require_once 'Rediska.php';
// Get rediska entity
$ rediska = new Rediska ($ options);
for ($ i = 1; $ i <= 10000; $ i ++) {
// Start transaction
$ transaction = $ rediska-> transaction ();
// Get current value
$ value = $ rediska-> get ('test_value');
// Increment value
$ value ++;
// Store new value
$ transaction-> set ('test_value', $ value);
// Execute transaction
$ transaction-> execute ();
This script ran in two threads from the command line, i.e. in theory, as a result, I had to get the value of the key "test_value" equal to 20 thousand, but in reality there was an average of about 12 thousand. Those. there is no lock.
Now I modify the loop a bit by adding watch:
for ($ i = 1; $ i <= 10000; $ i ++) {
for ($ j = 1; $ j <= 5; $ j ++) {
// Start transaction
$ transaction = $ rediska-> transaction ();
// Watch
$ transaction-> watch ('test_value');
// Get current value
$ value = $ rediska-> get ('test_value');
// Increment value
$ value ++;
// Store new value
$ transaction-> set ('test_value', $ value);
// Execute transaction
try {
$ transaction-> execute ();
} catch (Rediska_Transaction_AbortedException $ e) {
continue;
}
break;
}
}
Those. in fact, when an occurrence occurs, we try to repeat the transaction 5 times. As a result, with 5 repetitions, an average of 17 thousand came out, if we raise it to say 10, an average of 19 thousand will come out.
These are of course special cases, in practice it is unlikely that such a number of simultaneous changes and 5 repetitions should, in principle, be enough, but that is not the point. The fact is that, in fact, Redis does not yet have a mechanism (documented) for locking keys affected by changes.
It is not for me to decide whether it is good or bad to judge, I think it all depends on the task, I just wanted to show how it is.