1 |
27204
|
antonis.le
|
# Some frequently asked questions about Predis #
|
2 |
|
|
________________________________________________
|
3 |
|
|
|
4 |
|
|
### What is the point of Predis? ###
|
5 |
|
|
|
6 |
|
|
The main point of Predis is about offering a highly customizable and extensible client for Redis,
|
7 |
|
|
that can be easily extended by developers while still being reasonabily fast. With Predis you can
|
8 |
|
|
swap almost any class with your own custom implementation: you can have custom connection classes,
|
9 |
|
|
new distribution strategies for client-side sharding, or handlers to replace or add Redis commands.
|
10 |
|
|
All of this can be achieved without messing with the source code of the library and directly in your
|
11 |
|
|
own application. Given the fast pace at which Redis is developed and adds new features, this can be
|
12 |
|
|
a great asset since it allows developers to add new and still missing features or commands or change
|
13 |
|
|
the standard behaviour of the library without the need to break dependencies in production code (at
|
14 |
|
|
least to some degree).
|
15 |
|
|
|
16 |
|
|
### Does Predis support UNIX domain sockets and persistent connections? ###
|
17 |
|
|
|
18 |
|
|
Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
|
19 |
|
|
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).
|
20 |
|
|
|
21 |
|
|
### Does Predis support transparent (de)serialization of values? ###
|
22 |
|
|
|
23 |
|
|
No and it will not ever do that by default. The reason behind this decision is that serialization is
|
24 |
|
|
usually something that developers prefer to customize depending on their needs and can not be easily
|
25 |
|
|
generalized when using Redis because of the many possible access patterns for your data. This does
|
26 |
|
|
not mean that it is impossible to have such a feature since you can leverage the extensibility of
|
27 |
|
|
this library to define your own serialization-aware commands. You can find more details about how to
|
28 |
|
|
do that [on this issue](http://github.com/nrk/predis/issues/29#issuecomment-1202624).
|
29 |
|
|
|
30 |
|
|
### How can I force Predis to connect to Redis before sending any command? ###
|
31 |
|
|
|
32 |
|
|
Explicitly connecting to Redis is usually not needed since the client initializes connections lazily
|
33 |
|
|
only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when
|
34 |
|
|
you absolutely need to perform an upfront check to determine if the server is up and running and
|
35 |
|
|
eventually catch exceptions on failures. Forcing the client to open the underlying connection can be
|
36 |
|
|
done by invoking `Predis\Client::connect()`:
|
37 |
|
|
|
38 |
|
|
```php
|
39 |
|
|
$client = new Predis\Client();
|
40 |
|
|
|
41 |
|
|
try {
|
42 |
|
|
$client->connect();
|
43 |
|
|
} catch (Predis\Connection\ConnectionException $exception) {
|
44 |
|
|
// We could not connect to Redis! Your handling code goes here.
|
45 |
|
|
}
|
46 |
|
|
|
47 |
|
|
$client->info();
|
48 |
|
|
```
|
49 |
|
|
|
50 |
|
|
### How Predis abstracts Redis commands? ###
|
51 |
|
|
|
52 |
|
|
The approach used to implement Redis commands is quite simple: by default each command follows the
|
53 |
|
|
same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things
|
54 |
|
|
pretty easy if you already know how Redis works or you need to look up how to use certain commands.
|
55 |
|
|
Alternatively, variadic commands can accept an array for keys or values (depending on the command)
|
56 |
|
|
instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and
|
57 |
|
|
[`HMSET`](http://redis.io/commands/hmset) are great examples:
|
58 |
|
|
|
59 |
|
|
```php
|
60 |
|
|
$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments
|
61 |
|
|
$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array
|
62 |
|
|
|
63 |
|
|
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments
|
64 |
|
|
$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array
|
65 |
|
|
```
|
66 |
|
|
|
67 |
|
|
An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed
|
68 |
|
|
[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75).
|
69 |
|
|
|
70 |
|
|
|
71 |
|
|
# Speaking about performances... #
|
72 |
|
|
_________________________________________________
|
73 |
|
|
|
74 |
|
|
|
75 |
|
|
### Predis is a pure-PHP implementation: it can not be fast enough! ###
|
76 |
|
|
|
77 |
|
|
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a
|
78 |
|
|
couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6
|
79 |
|
|
against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600:
|
80 |
|
|
|
81 |
|
|
```
|
82 |
|
|
21000 SET/sec using 12 bytes for both key and value.
|
83 |
|
|
21000 GET/sec while retrieving the very same values.
|
84 |
|
|
0.130 seconds to fetch 30000 keys using _KEYS *_.
|
85 |
|
|
```
|
86 |
|
|
|
87 |
|
|
How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension
|
88 |
|
|
providing an efficient client for Redis?
|
89 |
|
|
|
90 |
|
|
```
|
91 |
|
|
30100 SET/sec using 12 bytes for both key and value
|
92 |
|
|
29400 GET/sec while retrieving the very same values
|
93 |
|
|
0.035 seconds to fetch 30000 keys using "KEYS *"".
|
94 |
|
|
```
|
95 |
|
|
|
96 |
|
|
Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
|
97 |
|
|
lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are
|
98 |
|
|
going to use Redis in your application? Are you really going to send thousands of commands using a
|
99 |
|
|
for-loop on each page request using a single client instance? If so... well I guess you are probably
|
100 |
|
|
doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use
|
101 |
|
|
commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this
|
102 |
|
|
technique can be used.
|
103 |
|
|
|
104 |
|
|
There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance
|
105 |
|
|
of Redis but how these numbers change when we hit the physical network by connecting to remote Redis
|
106 |
|
|
instances?
|
107 |
|
|
|
108 |
|
|
```
|
109 |
|
|
Using Predis:
|
110 |
|
|
3200 SET/sec using 12 bytes for both key and value
|
111 |
|
|
3200 GET/sec while retrieving the very same values
|
112 |
|
|
0.132 seconds to fetch 30000 keys using "KEYS *".
|
113 |
|
|
|
114 |
|
|
Using phpredis:
|
115 |
|
|
3500 SET/sec using 12 bytes for both key and value
|
116 |
|
|
3500 GET/sec while retrieving the very same values
|
117 |
|
|
0.045 seconds to fetch 30000 keys using "KEYS *".
|
118 |
|
|
```
|
119 |
|
|
|
120 |
|
|
There you go, you get almost the same average numbers and the reason is simple: network latency is a
|
121 |
|
|
real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember
|
122 |
|
|
that we are measuring the overhead of client libraries implementations and the effects of network
|
123 |
|
|
round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands
|
124 |
|
|
of concurrent clients doing requests! Also, actual performances should be measured according to how
|
125 |
|
|
your application will use Redis.
|
126 |
|
|
|
127 |
|
|
### I am convinced, but performances for multi-bulk responses are still worse ###
|
128 |
|
|
|
129 |
|
|
Fair enough, but there is an option available if you need even more speed and consists on installing
|
130 |
|
|
__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the
|
131 |
|
|
client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client
|
132 |
|
|
library for Redis) with a thin layer exposing its features to PHP. You can then choose between two
|
133 |
|
|
different connection classes:
|
134 |
|
|
|
135 |
|
|
- `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams).
|
136 |
|
|
- `Predis\Connection\PhpiredisConnection` (requires `ext-socket`).
|
137 |
|
|
|
138 |
|
|
You will now get the benefits of a faster protocol serializer and parser just by adding a couple of
|
139 |
|
|
lines of code:
|
140 |
|
|
|
141 |
|
|
```php
|
142 |
|
|
$client = new Predis\Client('tcp://127.0.0.1', array(
|
143 |
|
|
'connections' => array(
|
144 |
|
|
'tcp' => 'Predis\Connection\PhpiredisStreamConnection',
|
145 |
|
|
'unix' => 'Predis\Connection\PhpiredisConnection',
|
146 |
|
|
),
|
147 |
|
|
));
|
148 |
|
|
```
|
149 |
|
|
|
150 |
|
|
Dead simple. Nothing changes in the way you use the library in your application. So how fast is it
|
151 |
|
|
our basic benchmark script now? There are not much improvements for inline or short bulk responses
|
152 |
|
|
like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on
|
153 |
|
|
par with phpredis:
|
154 |
|
|
|
155 |
|
|
```
|
156 |
|
|
Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis::
|
157 |
|
|
|
158 |
|
|
0.035 seconds from a local Redis instance
|
159 |
|
|
0.047 seconds from a remote Redis instance
|
160 |
|
|
```
|
161 |
|
|
|
162 |
|
|
### If I need an extension to get better performances, why not using phpredis? ###
|
163 |
|
|
|
164 |
|
|
Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and
|
165 |
|
|
you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
|
166 |
|
|
do not need any kind of extensibility or guaranteed backwards compatibility with different versions
|
167 |
|
|
of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then
|
168 |
|
|
using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding
|
169 |
|
|
__phpiredis__ you can get a nice speed bump almost for free.
|