diff --git a/FAQ.md b/FAQ.md index 87b664e6..12d78995 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,41 +1,39 @@ # Some frequently asked questions about Predis # -_________________________________________________ - +________________________________________________ ### What is the point of Predis? ### -The main point of Predis is about offering a highly customizable client for Redis that can be easily -extended by developers while still being reasonabily fast. With Predis you can swap almost any class -used internally with your own custom implementation: you can build connection classes, or new -distribution strategies for client-side sharding, or class handlers to replace existing commands or -add new ones. All of this can be achieved without messing with the source code of the library and -directly in your own application. Given the fast pace at which Redis is developed and adds new -features, this can be a great asset that allows you to add new and still missing features or commands, -or change the behaviour of the library without the need to break your dependencies in production code -(well, at least to some degree). +The main point of Predis is about offering a highly customizable and extensible client for Redis, +that can be easily extended by developers while still being reasonabily fast. With Predis you can +swap almost any class with your own custom implementation: you can have custom connection classes, +new distribution strategies for client-side sharding, or handlers to replace or add Redis commands. +All of this can be achieved without messing with the source code of the library and directly in your +own application. Given the fast pace at which Redis is developed and adds new features, this can be +a great asset since it allows developers to add new and still missing features or commands or change +the standard behaviour of the library without the need to break dependencies in production code (at +least to some degree). ### Does Predis support UNIX domain sockets and persistent connections? ### -Yes. Obviously, persistent connections actually work when using PHP configured as a persistent process that -gets recycled between requests (see [PHP-FPM](http://php-fpm.org/)). - +Yes. Obviously persistent connections actually work only when using PHP configured as a persistent +process reused by the web server (see [PHP-FPM](http://php-fpm.org)). ### Does Predis support transparent (de)serialization of values? ### -No, and it will not ever do that for you by default. The reason behind this decision is that serialization -is usually something that developers prefer to customize depending on their needs and can not be easily -generalized when using Redis because of the many possible access patterns for the data. This does not -mean that it is impossible to have such a feature, you can leverage Predis' extensibility to define your -own serialization-aware commands. See [here](http://github.com/nrk/predis/issues/29#issuecomment-1202624) -for more details on how to implement such a feature with a practical example. - +No and it will not ever do that by default. The reason behind this decision is that serialization is +usually something that developers prefer to customize depending on their needs and can not be easily +generalized when using Redis because of the many possible access patterns for your data. This does +not mean that it is impossible to have such a feature since you can leverage the extensibility of +this library to define your own serialization-aware commands. You can find more details about how to +do that [on this issue](http://github.com/nrk/predis/issues/29#issuecomment-1202624). ### How can I force Predis to connect to Redis before sending any command? ### -Explicitly connecting to Redis is usually not needed since the client library relies on lazily initialized -connections to the server, but this behavior can be inconvenient in certain scenarios when you absolutely -need to do an upfront check to detect if the server is up and running and eventually catch exceptions on -failures. In this case developers can use `Predis\Client::connect()` to explicitly connect to the server: +Explicitly connecting to Redis is usually not needed since the client initializes connections lazily +only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when +you absolutely need to perform an upfront check to determine if the server is up and running and +eventually catch exceptions on failures. Forcing the client to open the underlying connection can be +done by invoking `Predis\Client::connect()`: ```php $client = new Predis\Client(); @@ -49,27 +47,25 @@ try { $client->info(); ``` +### How Predis abstracts Redis commands? ### -### How Predis implements abstraction of Redis commands? ### - -The approach used in Predis to implement the abstraction of Redis commands is quite simple. By default -every command in the library follows exactly the same argument list as defined in the great online -[Redis documentation](http://redis.io/commands) which makes things pretty easy if you already know how -Redis works or if you need to look up how to use certain commands. Alternatively, variadic commands can -accept an array for keys or values (depending on the command) instead of a list of arguments. See for -example how [RPUSH](http://redis.io/commands/rpush) or [HMSET](http://redis.io/commands/hmset) work: +The approach used to implement Redis commands is quite simple: by default each command follows the +same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things +pretty easy if you already know how Redis works or you need to look up how to use certain commands. +Alternatively, variadic commands can accept an array for keys or values (depending on the command) +instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and +[`HMSET`](http://redis.io/commands/hmset) are great examples: ```php -$client->rpush('my:list', 'value1', 'value2', 'value3'); // values as arguments -$client->rpush('my:list', array('value1', 'value2', 'value3')); // values as single argument array +$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments +$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array -$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // values as arguments -$client->hmset('my:hash', array('field1'=>'value1', 'field2'=>'value2'); // values as single named array +$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments +$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array ``` -The only exception to this _rule_ is the [SORT](http://redis.io/commands/sort) command for which modifiers are -[passed using a named array](tests/Predis/Command/KeySortTest.php#L56-77). - +An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed +[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75). # Frequently asked questions about performances # @@ -78,83 +74,92 @@ _________________________________________________ ### Predis is a pure-PHP implementation: it can not be fast enough! ### -It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you -a couple of easy numbers using a single Predis client with PHP 5.4.7 (custom build) and Redis 2.2 -(localhost) under Ubuntu 12.04.1 (running on a Intel Q6600): +It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a +couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6 +against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600: - 21500 SET/sec using 12 bytes for both key and value - 21000 GET/sec while retrieving the very same values - 0.130 seconds to fetch 30000 keys using _KEYS *_. +``` +21000 SET/sec using 12 bytes for both key and value. +21000 GET/sec while retrieving the very same values. +0.130 seconds to fetch 30000 keys using _KEYS *_. +``` -How does it compare with a nice C-based extension such as [__phpredis__](http://github.com/nicolasff/phpredis)? +How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension +providing an efficient client for Redis? - 30100 SET/sec using 12 bytes for both key and value - 29400 GET/sec while retrieving the very same values - 0.035 seconds to fetch 30000 keys using "KEYS *"". +``` +30100 SET/sec using 12 bytes for both key and value +29400 GET/sec while retrieving the very same values +0.035 seconds to fetch 30000 keys using "KEYS *"". +``` -Wow, __phpredis__ looks so much faster! Well we are comparing a C extension with a pure-PHP library so -lower numbers are quite expected, but there is a fundamental flaw in them: is this really how you are -going to use Redis in your application? Are you really going to send thousands of commands in a for-loop -for each page request using a single client instance? If so, well I guess you are probably doing something -wrong. Also, if you need to SET or GET multiple keys you should definitely use commands such as MSET and -MGET. You can also use pipelining to get more performances when this technique can be used. +Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so +lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are +going to use Redis in your application? Are you really going to send thousands of commands using a +for-loop on each page request using a single client instance? If so... well I guess you are probably +doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use +commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this +technique can be used. -There is one more thing. We have tested the overhead of Predis by connecting on a localhost instance of -Redis, but how these numbers change when we hit the network by connecting to instances of Redis that -reside on other servers? +There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance +of Redis but how these numbers change when we hit the physical network by connecting to remote Redis +instances? - Using Predis: - 3200 SET/sec using 12 bytes for both key and value - 3200 GET/sec while retrieving the very same values - 0.132 seconds to fetch 30000 keys using "KEYS *". +``` +Using Predis: +3200 SET/sec using 12 bytes for both key and value +3200 GET/sec while retrieving the very same values +0.132 seconds to fetch 30000 keys using "KEYS *". - Using phpredis: - 3500 SET/sec using 12 bytes for both key and value - 3500 GET/sec while retrieving the very same values - 0.045 seconds to fetch 30000 keys using "KEYS *". +Using phpredis: +3500 SET/sec using 12 bytes for both key and value +3500 GET/sec while retrieving the very same values +0.045 seconds to fetch 30000 keys using "KEYS *". +``` -There you go, you get almost the same average numbers and the reason is quite simple: network latency -is a real performance killer and you cannot do (almost) anything about that. As a disclaimer, please -remember that we are measuring the overhead of client libraries implementations and the effects of the -network round-trip time, we are not really measuring how fast Redis is. Redis shines the best with -thousands of concurrent clients doing requests! Also, actual performances should be measured according -to how your application will use Redis. +There you go, you get almost the same average numbers and the reason is simple: network latency is a +real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember +that we are measuring the overhead of client libraries implementations and the effects of network +round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands +of concurrent clients doing requests! Also, actual performances should be measured according to how +your application will use Redis. +### I am convinced, but performances for multi-bulk responses are still worse ### -### I am convinced, but performances for multi-bulk responses (e.g. _KEYS *_) are still worse ### - -Fair enough, but there is actually an option for you if you need even more speed and it consists on -installing __[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) -and let Predis using it. __phpiredis__ is a C-based extension that wraps __hiredis__ (the official -Redis C client library) with a thin layer that exposes its features to PHP. You can choose between -two different connection backend classes: `Predis\Connection\PhpiredisConnection` (it depends on the -`socket` extension) and `Predis\Connection\PhpiredisStreamConnection` (it uses PHP's native streams). -You will now get the benefits of a faster protocol parser just by adding a couple of lines of code: +Fair enough, but there is an option available if you need even more speed and consists on installing +__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the +client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client +library for Redis) with a thin layer exposing its features to PHP. You can then choose between two +different connection classes: `Predis\Connection\PhpiredisStreamConnection` (native PHP streams) and +`Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`). You will now get the benefits +of a faster protocol serializer and parser just by adding a couple of lines of code: ```php $client = new Predis\Client('tcp://127.0.0.1', array( 'connections' => array( - 'tcp' => 'Predis\Connection\PhpiredisConnection', - 'unix' => 'Predis\Connection\PhpiredisConnection', + 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', + 'unix' => 'Predis\Connection\PhpiredisSocketConnection', ), )); ``` -As simple as it is, nothing will really change in the way you use the library in your application. So, -how fast is it now? There are not much improvements for inline or short bulk responses (e.g. _SET_ or -_GET_), but the speed for parsing multi-bulk responses is now on par with phpredis: +Dead simple. Nothing changes in the way you use the library in your application. So how fast is it +our basic benchmark script now? There are not much improvements for inline or short bulk responses +like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on +par with phpredis: - Using Predis with a phpiredis-based connection to fetch 30000 keys using _KEYS *_: +``` +Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis:: - 0.035 seconds from a local Redis instance - 0.047 seconds from a remote Redis instance +0.035 seconds from a local Redis instance +0.047 seconds from a remote Redis instance +``` +### Why not using phpredis if I need to install an extension to have better performances? ### -### If I need to install a C extension to get better performances, why not using phpredis? ### - -Good question. Generically speaking, if you need absolute uber-speed using localhost instances of Redis -and you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you -do not need any kind of extensibility or guaranteed backwards compatibility with different versions of -Redis (Predis currently supports from 1.2 up to 2.6, and even the current development version), then -using __phpredis__ can make sense for you. Otherwise, Predis is perfect for the job. __phpiredis__ -can give you a nice speed bump, but using it is not mandatory. +Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and +you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you +do not need any kind of extensibility or guaranteed backwards compatibility with different versions +of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then +using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding +__phpiredis__ you can get a nice speed bump almost for free.