Do you often get() and set() a shuttle when you use Redis? There are many useful data structures and methods in Redis. Today we’re going to test the eval() method.

Redis Eval Official Notes (Excerpts)

EVAL script numkeys key [key ...] arg [arg ...]

Starting with Redis version 2.6.0, Lua scripts can be evaluated using the EVAL command through the built-in Lua interpreter.

Redis uses a single Lua interpreter to run all scripts, and Redis also guarantees that scripts will be executed atomic: no other scripts or Redis commands will be executed while a script is running. This is similar to transactions surrounded by MULTI/EXEC. In the eyes of other clients, the script’s effects are either not visible or already completed.

On the other hand, this also means that executing a slow-running script is not a good idea. It’s not hard to write a script that runs very fast and smoothly because there is very little overhead, but be careful when you have to use a script that runs slow, because when the snail script runs slowly, other clients can’t execute because the server is busy.

The measured

As I understand it, the eval() method in Redis is generally used in situations where multiple Redis operations need to be performed to achieve a goal.

Below I have simulated a scenario where 100 redis operations are performed, although this may not be necessary, just to show the difference.

<? php $redis = new \Redis; $redis - > connect (127.0.0.1, 6379); $Redis ->flushDB(); Set $t = microtime(true); for($i = 0; $i < 100; ++$i) { $redis->set('key' . $i, $i); } echo 'php for set: ', microtime(true) - $t, PHP_EOL; $Redis ->flushDB(); $t = microtime(true); $keys = []; $values = []; for($i = 0; $i < 100; ++$i) { $keys[] = 'key' . $i; $values[] = $i; } $redis->eval(<<<SCRIPT for i=1,#KEYS do redis.call('set', KEYS[i], ARGV[i]) end SCRIPT , array_merge($keys, $values), count($keys)); echo 'eval:', microtime(true) - $t, PHP_EOL;Copy the code

Results:

PHP for set: 0.056596040725708

The eval: 0.00089216232299805

Eval is obviously faster than looping set in code.

You think this is the end?

No!

EvalSha?

EvalSha instructions

EVALSHA sha1 numkeys key [key …] arg [arg …]

The script cached in the server is evaluated according to the given SHA1 check code.

Caching scripts to the server can be done with the SCRIPT LOAD command.

Everything else about the command, such as how arguments are passed in, is the same as the EVAL command.

EvalSha measured

<? php function testEval($redis) { $keys = []; $values = []; for($i = 0; $i < 100; ++$i) { $keys[] = 'key' . $i; $values[] = $i; } $redis->eval(<<<SCRIPT for i=1,#KEYS do redis.call('set', KEYS[i], ARGV[i]) end SCRIPT , array_merge($keys, $values), count($keys)); } function testEvalSha($redis) { $keys = []; $values = []; for($i = 0; $i < 100; ++$i) { $keys[] = 'key' . $i; $values[] = $i; } $redis->evalSha(sha1(<<<SCRIPT for i=1,#KEYS do redis.call('set', KEYS[i], ARGV[i]) end SCRIPT ), array_merge($keys, $values), count($keys)); } $redis = new \Redis; $redis - > connect (127.0.0.1, 6379); $Redis ->flushDB(); $t = microtime(true); for($i = 0; $i < 100; ++$i) { testEval($redis); } echo 'eval:', microtime(true) - $t, PHP_EOL; $Redis ->flushDB(); $t = microtime(true); for($i = 0; $i < 100; ++$i) { testEvalSha($redis); } echo 'evalSha:', microtime(true) - $t, PHP_EOL;Copy the code

Results:

The eval: 0.081475973129272

EvalSha: 0.076005220413208

EvalSha is not significantly faster than Eval in my tests.

However, in a production environment, evalSha sends smaller packets than Eval and consumes fewer network resources.

Eval needs to send the script to Redis in its entirety each time, whereas evalSha only needs to pass a single SHA1.

EvalSha can be called only after an eval has been performed.

welfare

Here is a method I have encapsulated that uses evalSha in preference for trying and eval if it fails.

/** * the eval extension method combines eval and evalSha. * * @param \Redis $Redis * @param string $script * @param array $args * @param int $num_keys * @return mixed */ function evalEx($redis, $script, $args = null, $num_keys = null) { $sha1 = sha1($script); $redis->clearLastError(); $result = $redis->evalSha($sha1, $args, $num_keys); if('NOSCRIPT No matching script. Please use EVAL.' === $redis->getLastError()) { $result = $redis->eval($script, $args, $num_keys); } return $result; }Copy the code