├── en
├── metadata.xml
├── redis.md
├── title.png
├── title.psd
└── xetex.template
├── readme.md
├── ru
├── generate.bat
├── metadata.xml
├── redis-ru.pdf
├── redis.md
├── title.png
├── title.psd
└── xetex.template
└── template
└── xetex.template
/en/metadata.xml:
--------------------------------------------------------------------------------
1 | The Little Redis Book
2 | Karl Seguin
3 | Perry Neal
4 |
--------------------------------------------------------------------------------
/en/redis.md:
--------------------------------------------------------------------------------
1 |
2 | \thispagestyle{empty}
3 | \changepage{}{}{}{-0.5cm}{}{2cm}{}{}{}
4 | \
5 |
6 | \clearpage
7 | \changepage{}{}{}{0.5cm}{}{-2cm}{}{}{}
8 |
9 | ## About This Book
10 |
11 | ### License
12 |
13 | The Little Redis Book is licensed under the Attribution-NonCommercial 3.0 Unported license. You should not have paid for this book.
14 |
15 | You are free to copy, distribute, modify or display the book. However, I ask that you always attribute the book to me, Karl Seguin, and do not use it for commercial purposes.
16 |
17 | You can see the *full text* of **the license at**:
18 |
19 |
20 |
21 | ### About The Author
22 |
23 | Karl Seguin is a developer with experience across various fields and technologies. He's an active contributor to Open-Source Software projects, a technical writer and an occasional speaker. He's written various articles, as well as a few tools, about Redis. Redis powers the ranking and statistics of his free service for casual game developers: [mogade.com](http://mogade.com/).
24 |
25 | Karl wrote [The Little MongoDB Book](http://openmymind.net/2011/3/28/The-Little-MongoDB-Book/), the free and popular book about MongoDB.
26 |
27 | His blog can be found at and he tweets via [@karlseguin](http://twitter.com/karlseguin)
28 |
29 | ### With Thanks To
30 |
31 | A special thanks to [Perry Neal](https://twitter.com/perryneal) for lending me his eyes, mind and passion. You provided me with invaluable help. Thank you.
32 |
33 | ### Latest Version
34 |
35 | The latest source of this book is available at:
36 |
37 |
38 | \clearpage
39 |
40 | ## Introduction
41 |
42 | Over the last couple years, the techniques and tools used for persisting and querying data have grown at an incredible pace. While it's safe to say that relational databases aren't going anywhere, we can also say that the ecosystem around data is never going to be the same.
43 |
44 | Of all the new tools and solutions, for me, Redis has been the most exciting. Why? First because it's unbelievably easy to learn. Hours is the right unit to use when talking about length of time it takes to get comfortable with Redis. Secondly, it solves a specific set of problems while at the same time being quite generic. What exactly does that mean? Redis doesn't try to be all things to all data. As you get to know Redis, it'll become increasingly evident what does and what does not belong in it. And when it does, as a developer, it's a great experience.
45 |
46 | While you can build a complete system using Redis only, I think most people will find that it supplements their more generic data solution - whether that be a traditional relational database, a document-oriented system, or something else. It's the kind of solution you use to implement specific features. In that way, it's similar to an indexing engine. You wouldn't build your entire application on Lucene. But when you need good search, it's a much better experience - for both you and your users. Of course, the similarities between Redis and indexing engines end there.
47 |
48 | The goal of this book is to build the foundation you'll need to master Redis. We'll focus on learning Redis' five data structures and look at various data modeling approaches. We'll also touch on some key administrative details and debugging techniques.
49 |
50 | ## Getting Started
51 |
52 | We all learn differently: some like to get their hands dirty, some like to watch videos, and some like to read. Nothing will help you understand Redis more than actually experiencing it. Redis is easy to install and comes with a simple shell that'll give us everything we need. Let's take a couple minutes and get it up and running on our machine.
53 |
54 | ### On Windows
55 |
56 | Redis doesn't officially support Windows, but there are options available. You wouldn't run these in production, but I've never experienced any limitations while doing development.
57 |
58 | First head over to and download the most up to date version (which should be at the top of the list).
59 |
60 | Extract the zip file and, based on your architecture, open either the `64bit` or `32bit` folder.
61 |
62 | ### On *nix and MacOSX
63 |
64 | For *nix and and Mac users, building it from source is your best option. The instructions, along with the latest version number, are available at . At the time of this writing the latest version is 2.4.6; to install this version we would execute:
65 |
66 | wget http://redis.googlecode.com/files/redis-2.4.6.tar.gz
67 | tar xzf redis-2.4.6.tar.gz
68 | cd redis-2.4.6
69 | make
70 |
71 | (Alternatively, Redis is available via various package managers. For example, MacOSX users with Homebrew installed can simply type `brew install redis`.)
72 |
73 | If you built it from source, the binary outputs have been placed in the `src` directory. Navigate to the `src` directory by executing `cd src`.
74 |
75 | ### Running and Connecting to Redis
76 |
77 | If everything worked, the Redis binaries should be available at your fingertips. Redis has a handful of executables. We'll focus on the Redis server and the Redis command line interface (a DOS-like client). Let's start the server. In Windows, double click `redis-server`. On *nix/MacOSX run `./redis-server`.
78 |
79 | If you read the start up message you'll see a warning that the `redis.conf` file couldn't be found. Redis will instead use built-in defaults, which is fine for what we'll be doing.
80 |
81 | Next start the Redis console by either double clicking `redis-cli` (Windows) or running `./redis-cli` (*nix/MacOSX). This will connect to the locally-running server on the default port (6379).
82 |
83 | You can test that everything is working by entering `info` into the command line interface. You'll hopefully see a bunch of key-value pairs which provide a great deal of insight into the server's status.
84 |
85 | If you are having problems with the above setup I suggest you seek help in the [official Redis support group](https://groups.google.com/forum/#!forum/redis-db).
86 |
87 | ## Redis Drivers
88 |
89 | As you'll soon learn, Redis' API is best described as an explicit set of functions. It has a very simple and procedural feel to it. This means that whether you are using the command line tool, or a driver for your favorite language, things are very similar. Therefore, you shouldn't have any problems following along if you prefer to work from a programming language. If you want, head over to the [client page](http://redis.io/clients) and download the appropriate driver.
90 |
91 | \clearpage
92 |
93 | ## Chapter 1 - The Basics
94 |
95 | What makes Redis special? What types of problems does it solve? What should developers watch out for when using it? Before we can answer any of these questions, we need to understand what Redis is.
96 |
97 | Redis is often described as an in-memory persistent key-value store. I don't think that's an accurate description. Redis does hold all the data in memory (more on this in a bit), and it does write that out to disk for persistence, but it's much more than a simple key-value store. It's important to step beyond this misconception otherwise your perspective of Redis and the problems it solves will be too narrow.
98 |
99 | The reality is that Redis exposes five different data structures, only one of which is a typical key-value structure. Understanding these five data structures, how they work, what methods they expose and what you can model with them is the key to understanding Redis. First though, let's wrap our heads around what it means to expose data structures.
100 |
101 | If we were to apply this data structure concept to the relational world, we could say that databases expose a single data structure - tables. Tables are both complex and flexible. There isn't much you can't model, store or manipulate with tables. However, their generic nature isn't without drawbacks. Specifically, not everything is as simple, or as fast, as it ought to be. What if, rather than having a one-size-fits-all structure, we used more specialized structures? There might be some things we can't do (or at least, can't do very well), but surely we'd gain in simplicity and speed?
102 |
103 | Using specific data structures for specific problems? Isn't that how we code? You don't use a hashtable for every piece of data, nor do you use a scalar variable. To me, that defines Redis' approach. If you are dealing with scalars, lists, hashes, or sets, why not store them as scalars, lists, hashes and sets? Why should checking for the existence of a value be any more complex than calling `exists(key)` or slower than O(1) (constant time lookup which won't slow down regardless of how many items there are)?
104 |
105 | ## The Building Blocks
106 |
107 | ### Databases
108 |
109 | Redis has the same basic concept of a database that you are already familiar with. A database contains a set of data. The typical use-case for a database is to group all of an application's data together and to keep it separate from another application's.
110 |
111 | In Redis, databases are simply identified by a number with the default database being number `0`. If you want to change to a different database you can do so via the `select` command. In the command line interface, type `select 1`. Redis should reply with an `OK` message and your prompt should change to something like `redis 127.0.0.1:6379[1]>`. If you want to switch back to the default database, just enter `select 0` in the command line interface..
112 |
113 | ### Commands, Keys and Values
114 |
115 | While Redis is more than just a key-value store, at its core, every one of Redis' five data structures has at least a key and a value. It's imperative that we understand keys and values before moving on to other available pieces of information.
116 |
117 | Keys are how you identify pieces of data. We'll be dealing with keys a lot, but for now, it's good enough to know that a key might look like `users:leto`. One could reasonably expect such a key to contain information about a user named `leto`. The colon doesn't have any special meaning, as far as Redis is concerned, but using a separator is a common approach people use to organize their keys.
118 |
119 | Values represent the actual data associated with the key. They can be anything. Sometimes you'll store strings, sometimes integers, sometimes you'll store serialized objects (in JSON, XML or some other format). For the most part, Redis treats values as a byte array and doesn't care what they are. Note that different drivers handle serialization differently (some leave it up to you) so in this book we'll only talk about string, integer and JSON.
120 |
121 | Let's get our hands a little dirty. Enter the following command:
122 |
123 | set users:leto "{name: leto, planet: dune, likes: [spice]}"
124 |
125 | This is the basic anatomy of a Redis command. First we have the actual command, in this case `set`. Next we have its parameters. The `set` command takes two parameters: the key we are setting and the value we are setting it to. Many, but not all, commands take a key (and when they do, it's often the first parameter). Can you guess how to retrieve this value? Hopefully you said (but don't worry if you weren't sure!):
126 |
127 | get users:leto
128 |
129 | Go ahead and play with some other combinations. Keys and values are fundamental concepts, and the `get` and `set` commands are the simplest way to play with them. Create more users, try different types of keys, try different values.
130 |
131 | ### Querying
132 |
133 | As we move forward, two things will become clear. As far as Redis is concerned, keys are everything and values are nothing. Or, put another way, Redis doesn't allow you to query an object's values. Given the above, we can't find the user(s) which live on planet `dune`.
134 |
135 | For many, this is will cause some concern. We've lived in a world where data querying is so flexible and powerful that Redis' approach seems primitive and unpragmatic. Don't let it unsettle you too much. Remember, Redis isn't a one-size-fits-all solution. There'll be things that just don't belong in there (because of the querying limitations). Also, consider that in some cases you'll find new ways to model your data.
136 |
137 | We'll look at more concrete examples as we move on, but it's important that we understand this basic reality of Redis. It helps us understand why values can be anything - Redis never needs to read or understand them. Also, it helps us get our minds thinking about modeling in this new world.
138 |
139 | ### Memory and Persistence
140 |
141 | We mentioned before that Redis is an in-memory persistent store. With respect to persistence, by default, Redis snapshots the database to disk based on how many keys have changed. You configure it so that if X number of keys change, then save the database every Y seconds. By default, Redis will save the database every 60 seconds if 1000 or more keys have changed all the way to 15 minutes if 9 or less keys has changed.
142 |
143 | Alternatively (or in addition to snapshotting), Redis can run in append mode. Any time a key changes, an append-only file is updated on disk. In some cases it's acceptable to lose 60 seconds worth of data, in exchange for performance, should there be some hardware or software failure. In some cases such a loss is not acceptable. Redis gives you the option. In chapter 5 we'll see a third option, which is offloading persistence to a slave.
144 |
145 | With respect to memory, Redis keeps all your data in memory. The obvious implication of this is the cost of running Redis: RAM is still the most expensive part of server hardware.
146 |
147 | I do feel that some developers have lost touch with how little space data can take. The Complete Works of William Shakespeare takes roughly 5.5MB of storage. As for scaling, other solutions tend to be IO- or CPU-bound. Which limitation (RAM or IO) will require you to scale out to more machines really depends on the type of data and how you are storing and querying it. Unless you're storing large multimedia files in Redis, the in-memory aspect is probably a non-issue. For apps where it is an issue you'll likely be trading being IO-bound for being memory bound.
148 |
149 | Redis did add support for virtual memory. However, this feature has been seen as a failure (by Redis' own developers) and its use has been deprecated.
150 |
151 | (On a side note, that 5.5MB file of Shakespeare's complete works can be compressed down to roughly 2MB. Redis doesn't do auto-compression but, since it treats values as bytes, there's no reason you can't trade processing time for RAM by compressing/decompressing the data yourself.)
152 |
153 | ### Putting It Together
154 |
155 | We've touched on a number of high level topics. The last thing I want to do before diving into Redis is bring some of those topics together. Specifically, query limitations, data structures and Redis' way to store data in memory.
156 |
157 | When you add those three things together you end up with something wonderful: speed. Some people think "Of course Redis is fast, everything's in memory." But that's only part of it. The real reason Redis shines versus other solutions is its specialized data structures.
158 |
159 | How fast? It depends on a lot of things - which commands you are using, the type of data, and so on. But Redis' performance tends to be measured in tens of thousands, or hundreds of thousands of operations **per second**. You can run `redis-benchmark` (which is in the same folder as the `redis-server` and `redis-cli`) to test it out yourself.
160 |
161 | I once changed code which used a traditional model to using Redis. A load test I wrote took over 5 minutes to finish using the relational model. It took about 150ms to complete in Redis. You won't always get that sort of massive gain, but it hopefully gives you an idea of what we are talking about
162 |
163 | It's important to understand this aspect of Redis because it impacts how you interact with it. Developers with an SQL background often work at minimizing the number of round trips they make to the database. That's good advice for any system, including Redis. However, given that we are dealing with simpler data structures, we'll sometimes need to hit the Redis server multiple times to achieve our goal. Such data access patterns can feel unnatural at first, but in reality it tends to be an insignificant cost compared to the raw performance we gain.
164 |
165 | ### In This Chapter
166 |
167 | Although we barely got to play with Redis, we did cover a wide range of topics. Don't worry if something isn't crystal clear - like querying. In the next chapter we'll go hands-on and any questions you have will hopefully answer themselves.
168 |
169 | The important takeaways from this chapter are:
170 |
171 | * Keys are strings which identify pieces of data (values)
172 |
173 | * Values are arbitrary byte arrays that Redis doesn't care about
174 |
175 | * Redis exposes (and is implemented as) five specialized data structures
176 |
177 | * Combined, the above make Redis fast and easy to use, but not suitable for every scenario
178 |
179 | \clearpage
180 |
181 | ## Chapter 2 - The Data Structures
182 |
183 | It's time to look at Redis' five data structures. We'll explain what each data structure is, what methods are available and what type of feature/data you'd use it for.
184 |
185 | The only Redis constructs we've seen so far are commands, keys and values. So far, nothing about data structures has been concrete. When we used the `set` command, how did Redis know what data structure to use? It turns out that every command is specific to a data structure. For example when you use `set` you are storing the value in a string data structure. When you use `hset` you are storing it in a hash. Given the small size of Redis' vocabulary, it's quite manageable.
186 |
187 | **[Redis' website](http://redis.io/commands) has great reference documentation. There's no point in repeating the work they've already done. We'll only cover the most important commands needed to understand the purpose of a data structure.**
188 |
189 | There's nothing more important than having fun and trying things out. You can always erase all the values in your database by entering `flushdb`, so don't be shy and try doing crazy things!
190 |
191 | ### Strings
192 |
193 | Strings are the most basic data structures available in Redis. When you think of a key-value pair, you are thinking of strings. Don't get mixed up by the name, as always, your value can be anything. I prefer to call them "scalars", but maybe that's just me.
194 |
195 | We already saw a common use-case for strings, storing instances of objects by key. This is something that you'll make heavy use of:
196 |
197 | set users:leto "{name: leto, planet: dune, likes: [spice]}"
198 |
199 | Additionally, Redis lets you do some common operations. For example `strlen ` can be used to get the length of a key's value; `getrange ` returns the specified range of a value; `append ` appends the value to the existing value (or creates it if it doesn't exist already). Go ahead and try those out. This is what I get:
200 |
201 | > strlen users:leto
202 | (integer) 42
203 |
204 | > getrange users:leto 27 40
205 | "likes: [spice]"
206 |
207 | > append users:leto " OVER 9000!!"
208 | (integer) 54
209 |
210 | Now, you might be thinking, that's great, but it doesn't make sense. You can't meaningfully pull a range out of JSON or append a value. You are right, the lesson here is that some of the commands, especially with the string data structure, only make sense given specific type of data.
211 |
212 | Earlier we learnt that Redis doesn't care about your values. Most of the time that's true. However, a few string commands are specific to some types or structure of values. As a vague example, I could see the above `append` and `getrange` commands being useful in some custom space-efficient serialization. As a more concrete example I give you the `incr`, `incrby`, `decr` and `decrby` commands. These increment or decrement the value of a string:
213 |
214 | > incr stats:page:about
215 | (integer) 1
216 | > incr stats:page:about
217 | (integer) 2
218 |
219 | > incrby ratings:video:12333 5
220 | (integer) 5
221 | > incrby ratings:video:12333 3
222 | (integer) 8
223 |
224 | As you can imagine, Redis strings are great for analytics. Try incrementing `users:leto` (a non-integer value) and see what happens (you should get an error).
225 |
226 | A more advanced example is the `setbit` and `getbit` commands. There's a [wonderful post](http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/) on how Spool uses these two commands to efficiently answer the question "how many unique visitors did we have today". For 128 million users a laptop generates the answer in less than 50ms and takes only 16MB of memory.
227 |
228 | It isn't important that you understand how bitmaps work, or how Spool uses them, but rather to understand that Redis strings are more powerful than they initially seem. Still, the most common cases are the ones we gave above: storing objects (complex or not) and counters. Also, since getting a value by key is so fast, strings are often used to cache data.
229 |
230 | ### Hashes
231 |
232 | Hashes are a good example of why calling Redis a key-value store isn't quite accurate. You see, in a lot of ways, hashes are like strings. The important difference is that they provide an extra level of indirection: a field. Therefore, the hash equivalents of `set` and `get` are:
233 |
234 | hset users:goku powerlevel 9000
235 | hget users:goku powerlevel
236 |
237 | We can also set multiple fields at once, get multiple fields at once, get all fields and values, list all the fields or delete a specific field:
238 |
239 | hmset users:goku race saiyan age 737
240 | hmget users:goku race powerlevel
241 | hgetall users:goku
242 | hkeys users:goku
243 | hdel users:goku age
244 |
245 | As you can see, hashes give us a bit more control over plain strings. Rather than storing a user as a single serialized value, we could use a hash to get a more accurate representation. The benefit would be the ability to pull and update/delete specific pieces of data, without having to get or write the entire value.
246 |
247 | Looking at hashes from the perspective of a well-defined object, such as a user, is key to understanding how they work. And it's true that, for performance reasons, more granular control might be useful. However, in the next chapter we'll look at how hashes can be used to organize your data and make querying more practical. In my opinion, this is where hashes really shine.
248 |
249 | ### Lists
250 |
251 | Lists let you store and manipulate an array of values for a given key. You can add values to the list, get the first or last value and manipulate values at a given index. Lists maintain their order and have efficient index-based operations. We could have a `newusers` list which tracks the newest registered users to our site:
252 |
253 | rpush newusers goku
254 | ltrim newusers 0 50
255 |
256 | First we push a new user at the front of the list, then we trim it so that it only contains the last 50 users. This is a common pattern. `ltrim` is an O(N) operation, where N is the number of values we are removing. In this case, where we always trim after a single insert, it'll actually have a constant performance of O(1) (because N will always be equal to 1).
257 |
258 | This is also the first time that we are seeing a value in one key referencing a value in another. If we wanted to get the details of the last 10 users, we'd do the following combination:
259 |
260 | keys = redis.lrange('newusers', 0, 10)
261 | redis.mget(*keys.map {|u| "users:#{u}"})
262 |
263 | The above is a bit of Ruby which shows the type of multiple roundtrips we talked about before.
264 |
265 | Of course, lists aren't only good for storing references to other keys. The values can be anything. You could use lists to store logs or track the path a user is taking through a site. If you were building a game, you might use it to track a queued user actions.
266 |
267 | ### Sets
268 |
269 | Set are used to store unique values and provide a number of set-based operations, like unions. Sets aren't ordered but they provide efficient value-based operations. A friend's list is the classic example of using a set:
270 |
271 | sadd friends:leto ghanima paul chani jessica
272 | sadd friends:duncan paul jessica alia
273 |
274 | Regardless of how many friends a user has, we can efficiently tell (O(1)) whether userX is a friend of userY or not
275 |
276 | sismember friends:leto jessica
277 | sismember friends:leto vladimir
278 |
279 | Furthermore we can see what two or more people share the same friends:
280 |
281 | sinter friends:leto friends:duncan
282 |
283 | and even store the result at a new key:
284 |
285 | sinterstore friends:leto_duncan friends:leto friends:duncan
286 |
287 | Sets are great for tagging or tracking any other properties of a value for which duplicates don't make any sense (or where we want to apply set operations such as intersections and unions).
288 |
289 | ### Sorted Sets
290 |
291 | The last and most powerful data structure are sorted sets. If hashes are like strings but with fields, then sorted sets are like sets but with a score. The score provides sorting and ranking capabilities. If we wanted a ranked list of friends, we might do:
292 |
293 | zadd friends:leto 100 ghanima 95 paul 95 chani 75 jessica 1 vladimir
294 |
295 | Want to find out how many friends `leto` has with a rank of 90 or over?
296 |
297 | zcount friends:leto 90 100
298 |
299 | How about figuring out `chani`'s rank?
300 |
301 | zrevrank friends:leto chani
302 |
303 | We use `zrevrank` instead of `zrank` since Redis' default sort is from low to high (but in this case we are ranking from high to low). The most obvious use-case for sorted sets is a leaderboard system. In reality though, anything you want sorted by an some integer, and be able to efficiently manipulate based on that score, might be a good fit for a sorted set.
304 |
305 | ### In This Chapter
306 |
307 | That's a high level overview of Redis' five data structures. One of the neat things about Redis is that you can often do more than you first realize. There are probably ways to use string and sorted sets that no one has thought of yet. As long as you understand the normal use-case though, you'll find Redis ideal for all types of problems. Also, just because Redis exposes five data structures and various methods, don't think you need to use all of them. It isn't uncommon to build a feature while only using a handful of commands.
308 |
309 | \clearpage
310 |
311 | ## Chapter 3 - Leveraging Data Structures
312 |
313 | In the previous chapter we talked about the five data structures and gave some examples of what problems they might solve. Now it's time to look at a few more advanced, yet common, topics and design patterns.
314 |
315 | ### Big O Notation
316 |
317 | Throughout this book we've made references to the Big O notation in the form of O(n) or O(1). Big O notation is used to explain how something behaves given a certain number of elements. In Redis, it's used to tell us how fast a command is based on the number of items we are dealing with.
318 |
319 | Redis documentation tells us the Big O notation for each of its commands. It also tells us what the factors are that influence the performance. Let's look at some examples.
320 |
321 | The fastest anything can be is O(1) which is a constant. Whether we are dealing with 5 items or 5 million, you'll get the same performance. The `sismember` command, which tells us if a value belongs to a set, is O(1). `sismember` is a powerful command, and its performance characteristics are a big reason for that. A number of Redis commands are O(1).
322 |
323 | Logarithmic, or O(log(N)), is the next fastest possibility because it needs to scan through smaller and smaller partitions. Using this type of divide and conquer approach, a very large number of items quickly gets broken down in a few iterations. `zadd` is a O(log(N)) command, where N is the number of elements already in the set.
324 |
325 | Next we have linear commands, or O(N). Looking for a non-indexed row in a table is an O(N) operation. So is using the `ltrim` command. However, in the case of `ltrim`, N isn't the number of elements in the list, but rather the elements being removed. Using `ltrim` to remove 1 item from a list of millions will be faster than using `ltrim` to remove 10 items from a list of thousands. (Though they'll probably both be so fast that you wouldn't be able to time it.)
326 |
327 | `zremrangebyscore` which removes elements from a sorted set with a score between a minimum and a maximum value has a complexity of O(log(N)+M). This makes it a mix. By reading the documentation we see that N is the number of total elements in the set and M is the number of elements to be removed. In other words, the number of elements that'll get removed is probably going to be more significant, in terms of performance, than the total number of elements in the list.
328 |
329 | The `sort` command, which we'll discuss in greater detail in the next chapter has a complexity of O(N+M*log(M)). From its performance characteristic, you can probably tell that this is one of Redis' most complex commands.
330 |
331 | There are a number of other complexities, the two remaining common ones are O(N^2) and O(C^N). The larger N is, the worse these perform relative to a smaller N. None of Redis' commands have this type of complexity.
332 |
333 | It's worth pointing out that the Big O notation deals with the worst case. When we say that something takes O(N), we might actually find it right away or it might be the last possible element.
334 |
335 |
336 | ### Pseudo Multi Key Queries
337 |
338 | A common situation you'll run into is wanting to query the same value by different keys. For example, you might want to get a user by email (for when they first log in) and also by id (after they've logged in). One horrible solution is to duplicate your user object into two string values:
339 |
340 | set users:leto@dune.gov "{id: 9001, email: 'leto@dune.gov', ...}"
341 | set users:9001 "{id: 9001, email: 'leto@dune.gov', ...}"
342 |
343 | This is bad because it's a nightmare to manage and it takes twice the amount of memory.
344 |
345 | It would be nice if Redis let you link one key to another, but it doesn't (and it probably never will). A major driver in Redis' development is to keep the code and API clean and simple. The internal implementation of linking keys (there's a lot we can do with keys that we haven't talked about yet) isn't worth it when you consider that Redis already provides a solution: hashes.
346 |
347 | Using a hash, we can remove the need for duplication:
348 |
349 | set users:9001 "{id: 9001, email: leto@dune.gov, ...}"
350 | hset users:lookup:email leto@dune.gov 9001
351 |
352 | What we are doing is using the field as a pseudo secondary index and referencing the single user object. To get a user by id, we issue a normal `get`:
353 |
354 | get users:9001
355 |
356 | To get a user by email, we issue an `hget` followed by a `get` (in Ruby):
357 |
358 | id = redis.hget('users:lookup:email', 'leto@dune.gov')
359 | user = redis.get("users:#{id}")
360 |
361 | This is something that you'll likely end up doing often. To me, this is where hashes really shine, but it isn't an obvious use-case until you see it.
362 |
363 | ### References and Indexes
364 |
365 | We've seen a couple examples of having one value reference another. We saw it when we looked at our list example, and we saw it in the section above when using hashes to make querying a little easier. What this comes down to is essentially having to manually manage your indexes and references between values. Being honest, I think we can say that's a bit of a downer, especially when you consider having to manage/update/delete these references manually. There is no magic solution to solving this problem in Redis.
366 |
367 | We already saw how sets are often used to implement this type of manual index:
368 |
369 | sadd friends:leto ghanima paul chani jessica
370 |
371 | Each member of this set is a reference to a Redis string value containing details on the actual user. What if `chani` changes her name, or deletes her account? Maybe it would make sense to also track the inverse relationships:
372 |
373 | sadd friends_of:chani leto paul
374 |
375 | Maintenance cost aside, if you are anything like me, you might cringe at the processing and memory cost of having these extra indexed values. In the next section we'll talk about ways to reduce the performance cost of having to do extra round trips (we briefly talked about it in the first chapter).
376 |
377 | If you actually think about it though, relational databases have the same overhead. Indexes take memory, must be scanned or ideally seeked and then the corresponding records must be looked up. The overhead is neatly abstracted away (and they do a lot of optimizations in terms of the processing to make it very efficient).
378 |
379 | Again, having to manually deal with references in Redis is unfortunate. But any initial concerns you have about the performance or memory implications of this should be tested. I think you'll find it a non-issue.
380 |
381 | ### Round Trips and Pipelining
382 |
383 | We already mentioned that making frequent trips to the server is a common pattern in Redis. Since it is something you'll do often, it's worth taking a closer look at what features we can leverage to get the most out of it.
384 |
385 | First, many commands either accept one or more set of parameters or have a sister-command which takes multiple parameters. We saw `mget` earlier, which takes multiple keys and returns the values:
386 |
387 | keys = redis.lrange('newusers', 0, 10)
388 | redis.mget(*keys.map {|u| "users:#{u}"})
389 |
390 | Or the `sadd` command which adds 1 or more members to a set:
391 |
392 | sadd friends:vladimir piter
393 | sadd friends:paul jessica leto "leto II" chani
394 |
395 | Redis also supports pipelining. Normally when a client sends a request to Redis it waits for the reply before sending the next request. With pipelining you can send a number of requests without waiting for their responses. This reduces the networking overhead and can result in significant performance gains.
396 |
397 | It's worth noting that Redis will use memory to queue up the commands, so it's a good idea to batch them. How large a batch you use will depend on what commands you are using, and more specifically, how large the parameters are. But, if you are issuing commands against ~50 character keys, you can probably batch them in thousands or tens of thousands.
398 |
399 | Exactly how you execute commands within a pipeline will vary from driver to driver. In Ruby you pass a block to the `pipelined` method:
400 |
401 | redis.pipelined do
402 | 9001.times do
403 | redis.incr('powerlevel')
404 | end
405 | end
406 |
407 | As you can probably guess, pipelining can really speed up a batch import!
408 |
409 | ### Transactions
410 |
411 | Every Redis command is atomic, including the ones that do multiple things. Additionally, Redis has support for transactions when using multiple commands.
412 |
413 | You might not know it, but Redis is actually single-threaded, which is how every command is guaranteed to be atomic. While one command is executing, no other command will run. (We'll briefly talk about scaling in a later chapter.) This is particularly useful when you consider that some commands do multiple things. For example:
414 |
415 | `incr` is essentially a `get` followed by a `set`
416 |
417 | `getset` sets a new value and returns the original
418 |
419 | `setnx` first checks if the key exists, and only sets the value if it does not
420 |
421 | Although these commands are useful, you'll inevitably need to run multiple commands as an atomic group. You do so by first issuing the `multi` command, followed by all the commands you want to execute as part of the transaction, and finally executing `exec` to actually execute the commands or `discard` to throw away, and not execute the commands. What guarantee does Redis make about transactions?
422 |
423 | * The commands will be executed in order
424 |
425 | * The commands will be executed as a single atomic operation (without another client's command being executed halfway through)
426 |
427 | * That either all or none of the commands in the transaction will be executed
428 |
429 | You can, and should, test this in the command line interface. Also note that there's no reason why you can't combine pipelining and transactions.
430 |
431 | multi
432 | hincrby groups:1percent balance -9000000000
433 | hincrby groups:99percent balance 9000000000
434 | exec
435 |
436 | Finally, Redis lets you specify a key (or keys) to watch and conditionally apply a transaction if the key(s) changed. This is used when you need to get values and execute code based on those values, all in a transaction. With the code above, we wouldn't be able to implement our own `incr` command since they are all executed together once `exec` is called. From code, we can't do:
437 |
438 | redis.multi()
439 | current = redis.get('powerlevel')
440 | redis.set('powerlevel', current + 1)
441 | redis.exec()
442 |
443 | That isn't how Redis transactions work. But, if we add a `watch` to `powerlevel`, we can do:
444 |
445 | redis.watch('powerlevel')
446 | current = redis.get('powerlevel')
447 | redis.multi()
448 | redis.set('powerlevel', current + 1)
449 | redis.exec()
450 |
451 | If another client changes the value of `powerlevel` after we've called `watch` on it, our transaction will fail. If no client changes the value, the set will work. We can execute this code in a loop until it works.
452 |
453 | ### Keys Anti-Pattern
454 |
455 | In the next chapter we'll talk about commands that aren't specifically related to data structures. Some of these are administrative or debugging tools. But there's one I'd like to talk about in particular: the `keys` command. This command takes a pattern and finds all the matching keys. This command seems like it's well suited for a number of tasks, but it should never be used in production code. Why? Because it does a linear scan through all the keys looking for matches. Or, put simply, it's slow.
456 |
457 | How do people try and use it? Say you are building a hosted bug tracking service. Each account will have an `id` and you might decide to store each bug into a string value with a key that looks like `bug:account_id:bug_id`. If you ever need to find all of an account's bugs (to display them, or maybe delete them if they delete their account), you might be tempted (as I was!) to use the `keys` command:
458 |
459 | keys bug:1233:*
460 |
461 | The better solution is to use a hash. Much like we can use hashes to provide a way to expose secondary indexes, so too can we use them to organize our data:
462 |
463 | hset bugs:1233 1 "{id:1, account: 1233, subject: '...'}"
464 | hset bugs:1233 2 "{id:2, account: 1233, subject: '...'}"
465 |
466 | To get all the bug ids for an account we simply call `hkeys bugs:1233`. To delete a specific bug we can do `hdel bugs:1233 2` and to delete an account we can delete the key via `del bugs:1233`.
467 |
468 |
469 | ### In This Chapter
470 |
471 | This chapter, combined with the previous one, has hopefully given you some insight on how to use Redis to power real features. There are a number of other patterns you can use to build all types of things, but the real key is to understand the fundamental data structures and to get a sense for how they can be used to achieve things beyond your initial perspective.
472 |
473 | \clearpage
474 |
475 | ## Chapter 4 - Beyond The Data Structures
476 |
477 | While the five data structures form the foundation of Redis, there are other commands which aren't data structure specific. We've already seen a handful of these: `info`, `select`, `flushdb`, `multi`, `exec`, `discard`, `watch` and `keys`. This chapter will look at some of the other important ones.
478 |
479 | ### Expiration
480 |
481 | Redis allows you to mark a key for expiration. You can give it an absolute time in the form of a Unix timestamp (seconds since January 1, 1970) or a time to live in seconds. This is a key-based command, so it doesn't matter what type of data structure the key represents.
482 |
483 | expire pages:about 30
484 | expireat pages:about 1356933600
485 |
486 | The first command will delete the key (and associated value) after 30 seconds. The second will do the same at 12:00 a.m. December 31st, 2012.
487 |
488 | This makes Redis an ideal caching engine. You can find out how long an item has to live until via the `ttl` command and you can remove the expiration on a key via the `persist` command:
489 |
490 | ttl pages:about
491 | persist pages:about
492 |
493 | Finally, there's a special string command, `setex` which lets you set a string and specify a time to live in a single atomic command (this is more for convenience than anything else):
494 |
495 | setex pages:about 30 'about us
....'
496 |
497 | ### Publication and Subscriptions
498 |
499 | Redis lists have an `blpop` and `brpop` command which returns and removes the first (or last) element from the list or blocks until one is available. These can be used to power a simple queue.
500 |
501 | Beyond this, Redis has first-class support for publishing messages and subscribing to channels. You can try this out yourself by opening a second `redis-cli` window. In the first window subscribe to a channel (we'll call it `warnings`):
502 |
503 | subscribe warnings
504 |
505 | The reply is the information of your subscription. Now, in the other window, publish a message to the `warnings` channel:
506 |
507 | publish warnings "it's over 9000!"
508 |
509 | If you go back to your first window you should have received the message to the `warnings` channel.
510 |
511 | You can subscribe to multiple channels (`subscribe channel1 channel2 ...`), subscribe to a pattern of channels (`psubscribe warnings:*`) and use the `unsubscribe` and `punsubscribe` commands to stop listening to one or more channels, or a channel pattern.
512 |
513 | Finally, notice that the `publish` command returned the value 1. This indicates the number of clients that received the message.
514 |
515 |
516 | ### Monitor and Slow Log
517 |
518 | The `monitor` command lets you see what Redis is up to. It's a great debugging tool that gives you insight into how your application is interacting with Redis. In one of your two redis-cli windows (if one is still subscribed, you can either use the `unsubscribe` command or close the window down and re-open a new one) enter the `monitor` command. In the other, execute any other type of command (like `get` or `set`). You should see those commands, along with their parameters, in the first window.
519 |
520 | You should be wary of running monitor in production, it really is a debugging and development tool. Aside from that, there isn't much more to say about it. It's just a really useful tool.
521 |
522 | Along with `monitor`, Redis has a `slowlog` which acts as a great profiling tool. It logs any command which takes longer than a specified number of **micro**seconds. In the next section we'll briefly cover how to configure Redis, for now you can configure Redis to log all commands by entering:
523 |
524 | config set slowlog-log-slower-than 0
525 |
526 | Next, issue a few commands. Finally you can retrieve all of the logs, or the most recent logs via:
527 |
528 | slowlog get
529 | slowlog get 10
530 |
531 | You can also get the number of items in the slow log by entering `slowlog len`
532 |
533 | For each command you entered you should see four parameters:
534 |
535 | * An auto-incrementing id
536 |
537 | * A Unix timestamp for when the command happened
538 |
539 | * The time, in microseconds, it took to run the command
540 |
541 | * The command and its parameters
542 |
543 | The slow log is maintained in memory, so running it in production, even with a low threshold, shouldn't be a problem. By default it will track the last 1024 logs.
544 |
545 | ### Sort
546 |
547 | One of Redis' most powerful commands is `sort`. It lets you sort the values within a list, set or sorted set (sorted sets are ordered by score, not the members within the set). In its simplest form, it allows us to do:
548 |
549 | rpush users:leto:guesses 5 9 10 2 4 10 19 2
550 | sort users:leto:guesses
551 |
552 | Which will return the values sorted from lowest to highest. Here's a more advanced example:
553 |
554 | sadd friends:ghanima leto paul chani jessica alia duncan
555 | sort friends:ghanima limit 0 3 desc alpha
556 |
557 | The above command shows us how to page the sorted records (via `limit`), how to return the results in descending order (via `desc`) and how to sort lexicographically instead of numerically (via `alpha`).
558 |
559 | The real power of `sort` is its ability to sort based on a referenced object. Earlier we showed how lists, sets and sorted sets are often used to reference other Redis objects. The `sort` command can dereference those relations and sort by the underlying value. For example, say we have a bug tracker which lets users watch issues. We might use a set to track the issues being watched:
560 |
561 | sadd watch:leto 12339 1382 338 9338
562 |
563 | It might make perfect sense to sort these by id (which the default sort will do), but we'd also like to have these sorted by severity. To do so, we tell Redis what pattern to sort by. First, let's add some more data so we can actually see a meaningful result:
564 |
565 | set severity:12339 3
566 | set severity:1382 2
567 | set severity:338 5
568 | set severity:9338 4
569 |
570 | To sort the bugs by severity, from highest to lowest, you'd do:
571 |
572 | sort watch:leto by severity:* desc
573 |
574 | Redis will substitute the `*` in our pattern (identified via `by`) with the values in our list/set/sorted set. This will create the key name that Redis will query for the actual values to sort by.
575 |
576 | Although you can have millions of keys within Redis, I think the above can get a little messy. Thankfully `sort` can also work on hashes and their fields. Instead of having a bunch of top-level keys you can leverage hashes:
577 |
578 | hset bug:12339 severity 3
579 | hset bug:12339 priority 1
580 | hset bug:12339 details "{id: 12339, ....}"
581 |
582 | hset bug:1382 severity 2
583 | hset bug:1382 priority 2
584 | hset bug:1382 details "{id: 1382, ....}"
585 |
586 | hset bug:338 severity 5
587 | hset bug:338 priority 3
588 | hset bug:338 details "{id: 338, ....}"
589 |
590 | hset bug:9338 severity 4
591 | hset bug:9338 priority 2
592 | hset bug:9338 details "{id: 9338, ....}"
593 |
594 | Not only is everything better organized, and we can sort by `severity` or `priority`, but we can also tell `sort` what field to retrieve:
595 |
596 | sort watch:leto by bug:*->priority get bug:*->details
597 |
598 | The same value substitution occurs, but Redis also recognizes the `->` sequence and uses it to look into the specified field of our hash. We've also included the `get` parameter, which also does the substitution and field lookup, to retrieve bug details.
599 |
600 | Over large sets, `sort` can be slow. The good news is that the output of a `sort` can be stored:
601 |
602 | sort watch:leto by bug:*->priority get bug:*->details store watch_by_priority:leto
603 |
604 | Combining the `store` capabilities of `sort` with the expiration commands we've already seen makes for a nice combo.
605 |
606 |
607 | ### In This Chapter
608 |
609 | This chapter focused on non-data structure-specific commands. Like everything else, their use is situational. It isn't uncommon to build an app or feature that won't make use of expiration, publication/subscription and/or sorting. But it's good to know that they are there. Also, we only touched on some of the commands. There are more, and once you've digested the material in this book it's worth going through the [full list](http://redis.io/commands).
610 |
611 | \clearpage
612 |
613 | ## Chapter 5 - Administration
614 |
615 | Our last chapter is dedicated to some of the administrative aspects of running Redis. In no way is this a comprehensive guide on Redis administration. At best we'll answer some of the more basic questions new users to Redis are most likely to have.
616 |
617 | ### Configuration
618 |
619 | When you first launched the Redis server, it warned you that the `redis.conf` file could not be found. This file can be used to configure various aspects of Redis. A well-documented `redis.conf` file is available for each release of Redis. The sample file contains the default configuration options, so it's useful to both understand what the settings do and what their defaults are. You can find it at .
620 |
621 | **This is the config file for Redis 2.4.6. You should replace "2.4.6" in the above URL with your version. You can find your version by running the `info` command and looking at the first value.**
622 |
623 | Since the file is well-documented, we won't be going over the settings.
624 |
625 | In addition to configuring Redis via the `redis.conf` file, the `config set` command can be used to set individual values. In fact, we already used it when setting the `slowlog-log-slower-than` setting to 0.
626 |
627 | There's also the `config get` command which displays the value of a setting. This command supports pattern matching. So if we want to display everything related to logging, we can do:
628 |
629 | config get *log*
630 |
631 | ### Authentication
632 |
633 | Redis can be configured to require a password. This is done via the `requirepass` setting (set through either the `redis.conf` file or the `config set` command). When `requirepass` is set to a value (which is the password to use), clients will need to issue an `auth password` command.
634 |
635 | Once a client is authenticated, they can issue any command against any database. This includes the `flushall` command which erases every key from every database. Through the configuration, you can rename commands to achieve some security through obfuscation:
636 |
637 | rename-command CONFIG 5ec4db169f9d4dddacbfb0c26ea7e5ef
638 | rename-command FLUSHALL 1041285018a942a4922cbf76623b741e
639 |
640 | Or you can disable a command by setting the new name to an empty string.
641 |
642 | ### Size Limitations
643 |
644 | As you start using Redis, you might wonder "how many keys can I have?" You might also wonder how many fields can a hash have (especially when you use it to organize your data), or how many elements can lists and sets have? Per instance, the practical limits for all of these is in the hundreds of millions.
645 |
646 |
647 | ### Replication
648 |
649 | Redis supports replication, which means that as you write to one Redis instance (the master), one or more other instances (the slaves) are kept up-to-date by the master. To configure a slave you use either the `slaveof` configuration setting or the `slaveof` command (instances running without this configuration are or can be masters).
650 |
651 | Replication helps protect your data by copying to different servers. Replication can also be used to improve performance since reads can be sent to slaves. They might respond with slightly out of date data, but for most apps that's a worthwhile tradeoff.
652 |
653 | Unfortunately, Redis replication doesn't yet provide automated failover. If the master dies, a slave needs to be manually promoted. Traditional high-availability tools that use heartbeat monitoring and scripts to automate the switch are currently a necessary headache if you want to achieve some sort of high availability with Redis.
654 |
655 | ### Backups
656 |
657 | Backing up Redis is simply a matter of copying Redis' snapshot to whatever location you want (S3, FTP, ...). By default Redis saves its snapshop to a file named `dump.rdb`. At any point in time, you can simply `scp`, `ftp` or `cp` (or anything else) this file.
658 |
659 | It isn't uncommon to disable both snapshotting and the append-only file (aof) on the master and let a slave take care of this. This helps reduce the load on the master and lets you set more aggressive saving parameters on the slave without hurting overall system responsiveness.
660 |
661 | ### Scaling and Redis Cluster
662 |
663 | Replication is the first tool a growing site can leverage. Some commands are more expensive than others (`sort` for example) and offloading their execution to a slave can keep the overall system responsive to incoming queries.
664 |
665 | Beyond this, truly scaling Redis comes down to distributing your keys across multiple Redis instances (which could be running on the same box, remember, Redis is single-threaded). For the time being, this is something you'll need to take care of (although a number of Redis drivers do provide consistent-hashing algorithms). Thinking about your data in terms of horizontal distribution isn't something we can cover in this book. It's also something you probably won't have to worry about for a while, but it's something you'll need to be aware of regardless of what solution you use.
666 |
667 | The good news is that work is under way on Redis Cluster. Not only will this offer horizontal scaling, including rebalancing, but it'll also provide automated failover for high availability.
668 |
669 | High availability and scaling is something that can be achieved today, as long as you are willing to put the time and effort into it. Moving forward, Redis Cluster should make things much easier.
670 |
671 | ### In This Chapter
672 |
673 | Given the number of projects and sites using Redis already, there can be no doubt that Redis is production-ready, and has been for a while. However, some of the tooling, especially around security and availability is still young. Redis Cluster, which we'll hopefully see soon, should help address some of the current management challenges.
674 |
675 | \clearpage
676 |
677 | ## Conclusion
678 |
679 | In a lot of ways, Redis represents a simplification in the way we deal with data. It peels away much of the complexity and abstraction available in other systems. In many cases this makes Redis the wrong choice. In others it can feel like Redis was custom-built for your data.
680 |
681 | Ultimately it comes back to something I said at the very start: Redis is easy to learn. There are many new technologies and it can be hard to figure out what's worth investing time into learning. When you consider the real benefits Redis has to offer with its simplicity, I sincerely believe that it's one of the best investments, in terms of learning, that you and your team can make.
682 |
--------------------------------------------------------------------------------
/en/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akandratovich/the-little-redis-book/13a2f0979fffcb03c43bf8945539b2e9036a43c8/en/title.png
--------------------------------------------------------------------------------
/en/title.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akandratovich/the-little-redis-book/13a2f0979fffcb03c43bf8945539b2e9036a43c8/en/title.psd
--------------------------------------------------------------------------------
/en/xetex.template:
--------------------------------------------------------------------------------
1 | \documentclass{book}
2 | \usepackage[dvipsnames,usenames]{color}
3 | \usepackage{fullpage}
4 | \usepackage{changepage}
5 | \usepackage{fontspec,xltxtra,xunicode}
6 | \setmainfont{$mainfont$}
7 | \setsansfont{$sansfont$}
8 | \setmonofont{$monofont$}
9 | \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase}
10 |
11 | \setlength{\parindent}{0pt}
12 | \setlength{\parskip}{12pt plus 2pt minus 1pt}
13 | \linespread{1.2}
14 |
15 |
16 | $if(fancy-enums)$
17 | \usepackage{enumerate}
18 | $endif$
19 | \setcounter{secnumdepth}{-1}
20 |
21 | \usepackage{hyperref}
22 | \hypersetup{
23 | colorlinks=true,%
24 | citecolor=YellowOrange,%
25 | filecolor=YellowOrange,%
26 | linkcolor=YellowOrange,%
27 | urlcolor=YellowOrange
28 | }
29 |
30 | \usepackage[compact]{titlesec}
31 | \titlespacing{\section}{0pt}{*0}{*-2}
32 | \titlespacing{\subsection}{0pt}{*0}{*-2}
33 | \titlespacing{\subsubsection}{0pt}{*1}{*-2}
34 |
35 | \begin{document}
36 | $body$
37 | \end{document}
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | This repository is a fork of "The Little Redis Book" for translation to russian.
2 |
3 | ## About ##
4 | The Little Redis Book is a free book introducing Redis.
5 |
6 | The book was written by [Karl Seguin](http://openmymind.net), with [Perry Neal](http://twitter.com/perryneal)'s assistance.
7 |
8 | If you liked this book, maybe you'll also like [The Little MongoDB Book](http://openmymind.net/2011/3/28/The-Little-MongoDB-Book/).
9 |
10 | ## License ##
11 | The book is freely distributed under the [Attribution-NonCommercial 3.0 Unported license]().
12 |
13 | ## Russian Info ##
14 |
15 | В папке ru лежит копия английской версии для редактирования. Желающие помочь — присылайте пулл реквесты. Гитхаб позволяет редактировать документ прямо с веб-страницы.
16 |
17 | ## О Книге ##
18 | The Little Redis Book - это бесплатная книга про Redis.
19 |
20 | Книга была написана [Karl Seguin](http://openmymind.net), при поддержке [Perry Neal](http://twitter.com/perryneal).
21 | Если вам нравится эта книга, вам также стоит обратить внимание на [The Little MongoDB Book](http://openmymind.net/2011/3/28/The-Little-MongoDB-Book/).
22 |
23 | ## Лицензия ##
24 | Книга свободно роспространяется под [Attribution-NonCommercial 3.0 Unported license]().
25 |
26 | ## Форматы ##
27 | Книга написана с пощью [markdown](http://daringfireball.net/projects/markdown/) и сконвертирована в PDF при помощи [PanDoc](http://johnmacfarlane.net/pandoc/). Несколько комманд LaTeX были добавлены для генерации PDF (для титульной страницы и для создания разрывов страниц между главами).
28 |
29 | ## Generating the PDF ##
30 |
31 | В связи с тем, что генерация исходных текстов, содержащих кирилицу вызвала некоторые проблемы, в папке 'ru' лежит скрипт генерации для Windows и готовая версия книги.
32 |
--------------------------------------------------------------------------------
/ru/generate.bat:
--------------------------------------------------------------------------------
1 | markdown2pdf --xetex --template=xetex.template -V paper=a4paper -V hmargin=3cm -V vmargin=3cm -V mainfont="Verdana" -V sansfont="Tahoma" -V language:russian -V monofont="Consolas" -V geometry=portrait -V columns=onecolumn -V fontsize=11pt -V nohyphenation=true redis.md -o redis_ru.pdf
2 |
--------------------------------------------------------------------------------
/ru/metadata.xml:
--------------------------------------------------------------------------------
1 | The Little Redis Book (Russian)
2 | Andrew Kondratovich
3 |
--------------------------------------------------------------------------------
/ru/redis-ru.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akandratovich/the-little-redis-book/13a2f0979fffcb03c43bf8945539b2e9036a43c8/ru/redis-ru.pdf
--------------------------------------------------------------------------------
/ru/redis.md:
--------------------------------------------------------------------------------
1 | \thispagestyle{empty}
2 | \changepage{}{}{}{-0.5cm}{}{2cm}{}{}{}
3 | \
4 |
5 | \clearpage
6 | \changepage{}{}{}{0.5cm}{}{-2cm}{}{}{}
7 |
8 | ## Об этой книге
9 |
10 | ### Лицензия
11 |
12 | The Little Redis Book (Маленькая книга о Redis) распространяется под лицензией Attribution-NonCommercial 3.0 Unported. Вы не обязаны платить за эту книгу.
13 |
14 | Вы можете свободно копировать, распространять, изменять и публиковать книгу. Тем не менее, я прошу, чтобы вы всегда указывали мое, Карла Сегуина (Karl Seguin), авторство и не использовали книгу в коммерческих целях.
15 |
16 | Полный текст лицензии вы можете найти по [ссылке](http://creativecommons.org/licenses/by-nc/3.0/legalcode).
17 |
18 | ### Об авторе
19 |
20 | Карл Сегуин - разработчик, имеющий опыт работы в разных областях и с разными \linebreak технологиями. Активный участник проектов Открытого/Свободного ПО, технический \linebreak писатель и участник различных конференций. Является автором различных статей о Redis и нескольких утилит для работы с этой системой. На Redis работает система ранжирования и статистики на его бесплатном сервисе для разработчиков казуальных игр - [mogade.com](http://mogade.com/).
21 |
22 | Карл написал [The Little MongoDB Book](http://openmymind.net/2011/3/28/The-Little-MongoDB-Book/), популярную бесплатную книгу о MongoDB.
23 |
24 | Вы можете найти его блог по адресу , а также следить за ним в Twitter: [@karlseguin](http://twitter.com/karlseguin).
25 |
26 | ### Благодарности
27 |
28 | Особая благодарность выражается [Перри Нилу](https://twitter.com/perryneal) за его зоркий взгляд, ум и страсть. Ты оказал мне неоценимую помощь. Спасибо.
29 |
30 | ### Перевод
31 |
32 |
33 |
34 | Данный перевод выполнен с целью популяризации Redis среди русскоговорящих \linebreak разработчиков. Книга является очень удобным и компактным руководством.
35 |
36 | Я благодарен следующим людям за помощь в переводе оригинального текста и вычитке результата:
37 |
38 | \begin{list}
39 | {}
40 | {
41 | \setstretch{0.8}
42 | \setlength\parsep{0ex}
43 | \setlength\topsep{-1ex}
44 | }
45 | \item - \href{https://github.com/scriptin}{Dmitry Scriptin}
46 | \item - \href{https://github.com/saippuakauppias}{Денис Веселов}
47 | \item - \href{https://github.com/polinom}{polinom}
48 | \end{list}
49 |
50 | ### Актуальная версия
51 |
52 | Актуальная версия английского варианта книги доступна в [репозитории Карла](http://github.com/karlseguin/the-little-redis-book).
53 |
54 | Последнюю версию перевода на русский язык можно найти по [адресу](https://github.com/kondratovich/the-little-redis-book).
55 |
56 | \clearpage
57 |
58 | ## Введение
59 |
60 | За последние пару лет технологии и средства хранения и доступа к данным развивались невероятными темпами. Можно с уверенностью сказать, что реляционные базы данных не собираются исчезать, но в то же время мы видим, что экосистема вокруг данных уже никогда не будет прежней.
61 |
62 | Из всех новых инструментов и решений, для меня, Redis оказался наиболее интересным. Почему? Во-первых, по причине невероятной простоты изучения. Час является самой подходящей единицей измерения, когда мы говорим о времени, необходимом для ознакомления с Redis. Во-вторых, эта система решает специфический класс задач, будучи в то же время достаточно универсальной. Что это значит? Redis не пытается охватить весь спектр задач, касающихся работы с данными. По мере знакомства с Redis будет все более очевидно, что может и чего не может эта система. И, если Redis может что-либо, для вас, как для разработчика, это будет приятным опытом.
63 |
64 | Вы можете построить целое приложение, используя только Redis. Однако, я думаю, что большинство людей обнаружит, что Redis дополняет их классическое решение для работы с даными, будь то реляционная СУБД, документо-ориентированная система или что-то еще. Это тот тип систем, которые вы используете для решения специфических задач. В этом смысле Redis близок к индексирующему движку. Вы не будете писать ваше приложение полностью на Lucene, но если вам нужна хорошая система поиска, она подарит вам полезный опыт. Конечно, сходства между Redis и поисковыми движками на этом заканчиваются.
65 |
66 | Цель этой книги - построить базис, необходимый для работы с Redis. Мы сфокусируем наши усилия на изучении пяти структур данных Redis и рассмотрим различные подходы к моделированию. Также, затронем некоторые ключевые вопросы администрирования и техники отладки.
67 |
68 | ## Начало работы
69 |
70 | Мы все учимся по-разному: кто-то предпочитает собственный опыт, кто-то смотрит видео, кто-то читает. Ничто не поможет вам понять Redis лучше, чем собственный опыт. Redis легко устанавливается, и в комплект установки входит простая командная оболочка, предоставляющая все необходимые возможности. Давайте потратим пару минут на то, чтобы установить и запустить Redis у себя на компьютере.
71 |
72 | ### В Windows
73 |
74 | Redis официально не поддерживает Windows, но есть возможности запустить ее на этой системе. Вы вряд ли станете так делать на рабочем сервере, но во время разработки я не сталкивался с какими-либо ограничениями.
75 |
76 | Сначала перейдите на и скачайте последнюю версию (вверху списка).
77 |
78 | Распакуйте архив и, в зависимости от архитектуры вашего компьютера, откройте \linebreak директорию `64bit` или `32bit`.
79 |
80 | ### В *nix и MacOSX
81 |
82 | Для пользователей *nix и Mac, сборка Redis из исходников является лучшим вариантом. Инструкции, вместе с номером последней версии, доступны по адресу . На момент написания этих строк последней версией является 2.4.6. Для \linebreak установки выполните:
83 |
84 | wget http://redis.googlecode.com/files/redis-2.4.6.tar.gz
85 | tar xzf redis-2.4.6.tar.gz
86 | cd redis-2.4.6
87 | make
88 |
89 | (В качестве альтернативы, Redis доступна через различные менеджеры пакетов. \linebreak Например, пользователи MacOSX с установленным Homebrew могут просто выполнить команду `brew install redis`.) (*Установка через менеджер пакетов может оказаться гораздо более \linebreak удачным решением с точки зрения сохранения правильной конфигурации вашей системы - прим. перев.*)
90 |
91 | Если вы собрали систему из исходных текстов, исполняемые файлы можно найти в \linebreak директории `src`. Перейдите в эту директорию, введя команду `cd src`.
92 |
93 | ### Запуск и Подключение к Redis
94 |
95 | Если все прошло успешно, исполняемые файлы Redis должны быть в вашем распоряжении. Redis имеет множество исполняемых файлов. Мы сосредоточимся на сервере и командной оболочке (DOS-подобный клиент). Давайте начнем с сервера. В Windows, двойным кликом запустите `redis-server`. В *nix/MacOSX выполните команду `./redis-server` в \linebreak терминале.
96 |
97 | Если вы прочитаете стартовое сообщение, вы увидите предупреждение о том, что файл `redis.conf` не обнаружен. Вместо этого Redis воспользуется настройками по умолчанию, что вполне подойдет для наших целей.
98 |
99 | Далее, запустите командную оболочку Redis двойным кликом на `redis-cli` (Windows) или командой `./redis-cli` (*nix/MacOSX). Таким образом создастся подключение к локальному серверу на 6379-м TCP-порту по умолчанию.
100 |
101 | Вы можете убедиться, что все работает, введя команду `info` в командную строку. Вы должны увидеть множество пар ключ-значение, которые предоставляют большое \linebreak количество информации о состоянии сервера.
102 |
103 | Если у вас возникли проблемы с выполнением описанных выше действий, я рекомендую поискать помощь на [официальных форумах поддержки Redis](https://groups.google.com/forum/#!forum/redis-db).
104 |
105 | ## Драйверы Redis
106 |
107 | Как вы скоро узнаете, API Redis лучше всего описать как набор функций в простом \linebreak процедурном стиле. Это значит, что, используете ли вы командную оболочку или драйвер вашего любимого языка программирования, все работает одинаково. Следовательно, у вас не должно возникнуть проблем, если вы решите работать посредством языка \linebreak программирования. Если хотите, зайдите на [страницу со списком клиентов](http://redis.io/clients) и скачайте необходимый драйвер.
108 |
109 | \clearpage
110 |
111 | ## Глава 1 - Основы
112 |
113 | Почему же Redis такой особенный? Какие задачи он решает? На что следует обращать внимание разработчикам? Перед тем, как ответить на все эти вопросы, мы должны понять, чем же все-таки является Redis.
114 |
115 | Очень часто Redis описывают как персистентное (*то есть сохраняющееся после окончания работы создавшего его процессса - прим. перев.*) хранилище данных типа ключ-значение в оперативной памяти. Я не думаю, что это совсем точное определение. Redis, \linebreak действительно, держит все данные в памяти (позже мы вернемся к этому), и он сохраняет данные на диск для обеспечения персистентности. Но он не просто хранилище данных типа ключ-значение. Очень важно разобраться в этом неточном определении, иначе у вас сложится впечатление, что спектр решаемых Redis проблем весьма узок.
116 |
117 | Суть в том, что Redis предоставляет пять разных структур данных, только одна из которых собственно и есть структура типа ключ-значение. Понимание этих пяти структур данных, как они работают, какие методы они предоставляют для взаимодействия, и что вы сможете сделать с их помощью, как раз и являются путем к пониманию Redis. Но сначала давайте разберемся с тем, что же означает предоставление структур данных.
118 |
119 | Если мы применим эту концепцию к реляционному миру, мы можем сказать, что базы данных предоставляют один тип структур данных - таблицы. Таблицы одновременно сложные и гибкие. Существует очень мало вещей, которые нельзя смоделировать, \linebreak сохранить и которыми нельзя управлять с помощью таблиц. Тем не менее, они не идеальны. А именно, они не на столько простые, или не на столько быстрые, как хотелось бы. Что, если вместо универсальной структуры мы бы использовали специализированные структуры? В этом случае, наверное, найдутся проблемы, которые мы не сможем решить (или, по крайней мере, не сможем решить достаточно хорошо). Однако, в любом случае, мы выиграем на простоте и скорости.
120 |
121 | Использование специфичных структур данных для специфичных задач? Разве это не тот подход, который мы постоянно используем при программировании? Вы не используете хеш-таблицы для всех данных программы, то же самое и со скалярными переменными. (*Скалярные типы данных: строки, целые числа, дроби, булев тип - прим. перев.*) Я считаю, в этом и состоит подход Redis. Если вы работаете со скалярами, списками, или множествами, почему бы не хранить их как скаляры, списки, хеши и множества? Почему проверка на существование должна быть сложнее чем просто вызов `exists(key)` или медленнее чем O(1) (постоянное время выполнения выражения, которое не зависит от количества записей в хранилище)?
122 |
123 | ## Составные Кирпичики
124 |
125 | ### Базы Данных
126 |
127 | Redis использует знакомую всем концепцию базы данных. База данных содержит набор данных. Типичное предназначение базы данных - это группирование всей информации определенного приложения в месте и изоляция ее от других приложений.
128 |
129 | В Redis база данных идентифицируется просто числом, которое по умолчанию равняется `0`. Если вы хотите сменить базу данных, то вы можете сделать это командой `select`. В командной строке просто введите `select 1`. Redis должен ответить сообщением `OK` и в терминале вы должны увидеть что-то типа `redis 127.0.0.1:6379[1]>`. Если вы хотите переключиться обратно на базу по умолчанию, просто введите в командной строке `select 0`.
130 |
131 | ### Команды, Ключи и Значения
132 |
133 | Несмотря на то, что Redis больше, чем просто хранилище типа ключ-значение, в его основе каждая из пяти используемых структур данных имеет, как минимум, ключ и \linebreak значение. Очень важно разобраться в том, что такое ключи и что такое значения, перед тем как двигаться дальше.
134 |
135 | Ключ - это то, чем мы помечаем части информации. Мы будем пользоваться ключами часто, но пока достаточно знать, что ключ может выглядеть вот так: `users:leto`. Под таким ключом можно ожидать информацию о пользователе под именем `leto`. Двоеточие не имеет никакого особого значения, но использование подобных разделителей является хорошим тоном.
136 |
137 | Значение - это данные, которые ассоциированы с ключом. Это может быть что угодно. Иногда это строки, иногда числа, а иногда там хранятся сериализованные объекты (в виде JSON, XML или любых других форматов). В основном, Redis рассматривает значение как массив байт и не интересуется тем, что они собой представляют. Обратите внимание, что разные драйверы производят сериализацию по-разному. Поэтому, здесь мы будем говорить только о строках (string), целых числах (integer) и JSON.
138 |
139 | Давайте попрактикуемся. Выполните следующие команды:
140 |
141 | set users:leto "{name: leto, planet: dune, likes: [spice]}"
142 |
143 | Почти все команды Redis выглядят подобным образом. Сначала идет сама команда, в нашем случае `set`, а потом параметры. Команда `set` принимает два параметра: ключ, который мы сохраняем, и значение, которое связано с ним. Многие, но не все, команды принимают ключ (это обычно первый параметр). А теперь угадайте, как получить это значение? Надеюсь, что вы догадались (не расстраивайтесь, если это не так):
144 |
145 | get users:leto
146 |
147 | Попробуйте самостоятельно поиграть с подобными комбинациями. Ключ и значение - это основная концепция, и простейший способ работать с ней - это команды `get` и `set`. Создайте других пользователей, попробуйте разные виды ключей и значений.
148 |
149 | ### Запросы
150 |
151 | По мере нашего знакомства с Redis, мы поймем две вещи. Ключи это - все, а значения - ничто. Или, другими словами, Redis не позволяет использовать в запросах значения объектов. Из вышесказанного следует, что мы не сможем найти пользователя, который живет на планете `dune`.
152 |
153 | У многих это может вызвать некоторое беспокойство. Мы живем в мире, где запросы к базам данных стали столь гибкие и мощные, что подход Redis кажется примитивным и неудобным. Не дайте ввести себя в заблуждение. Redis не является универсальным решением на все случаи жизни. Существуют проблемы, которые просто не решаются с помощью Redis (из-за ограничений в запросах). Также стоит принять к сведению, что в некоторых случаях, вы найдете другие способы смоделировать ваши данные.
154 |
155 | Мы рассмотрим более конкретные примеры в дальнейшем. Сейчас очень важно, чтобы мы разобрались в этих базовых принципах Redis. Этот подход позволяет использовать в качестве значений что угодно - Redis никогда не обращается к ним. Мы научимся строить модели наших данных по-другому, как того требует этот новый мир.
156 |
157 | ### Память и Персистентность
158 |
159 | Мы упоминали ранее, что Redis является персистентным хранилищем в оперативной \linebreak памяти. По умолчанию, для поддержания персистентности, Redis сохраняет снимки базы данных на диск в зависимости от того, сколько ключей было изменено. Вы можете \linebreak настроить этот процесс таким образом, что если X - количество изменившихся ключей, то базу данных нужно сохранять каждые Y секунд. По умолчанию, Redis сохраняет базу с интервалами от 60 секунд, если 1000 или более ключей изменились, до 15 минут, если изменились хотя бы 9 ключей.
160 |
161 | Кроме того, (или в дополнение к сохранению снимков базы на диске), Redis может быть запущен в режиме дозаписи (append mode). Каждый раз, когда ключ меняется, на диске дописывается запись в файле, открытом в режиме дозаписи (новые записи добавляются в конец файла). Иногда стоит принять риск потери данных последних 60 секунд в случае аппаратной или программной ошибки в обмен на производительность. В других случаях такой риск неприемлем. Redis дает вам выбор. В главе 5 мы рассмотрим третью \linebreak возможность - обеспечение персистентности за счет вспомогательного (slave) хранилища.
162 |
163 | Что касается использования памяти, Redis хранит все данные в оперативной памяти. Явным следствием этого является цена использования Redis: оперативная память до сих пор является самой дорогой аппаратной частью серверов.
164 |
165 | Я чувствую, что некоторые разработчики забывают о том, как мало места могут занимать данные. Полное собрание сочинений Уильяма Шекспира занимает примерно 5,5 МБ. Что касается масштабирования, другие решения, как правило, ограничены временем ввода/вывода или производительностью процессора. Какое из ограничений (память или ввод/вывод) вынудит вас масштабировать приложение на большем количестве серверов, зависит от типа обрабатываемых данных и от того, как вы их храните и запрашиваете. Пока вы не храните огромные медиа-файлы в Redis, использование оперативной памяти не должно быть проблемой. Для приложений, где это все же будет проблемой, вы, скорее всего, выберете дополнительную нагрузку на ввод/вывод, чем ограничения размера \linebreak памяти.
166 |
167 | В Redis была реализована поддержка виртуальной памяти. Тем не менее, эта функция была признана неудачной (самими инженерами Redis) и ее использование не \linebreak рекомендуется.
168 |
169 | К слову, 5,5 МБ сочинений Шекспира могут быть сжаты до примерно 2 МБ. Redis не использует автоматическое сжатие, но, поскольку он трактует данные, как набор байт, нет причин, по которым вы не могли бы обменять время обработки на дополнительную память путем сжатия/распаковки данных.
170 |
171 | ### Собираем Все Вместе
172 |
173 | Мы коснулись нескольких общих тем. Последнее, что я хотел бы сделать перед \linebreak погружением в Redis, это собрать несколько этих тем вместе. А именно, ограничения запросов, структуры данных и способ хранения данных Redis в оперативной памяти.
174 |
175 | Когда вы собираете три эти вещи вместе, вы получаете нечто замечательное - скорость. Некоторые люди думают: «Конечно, Redis быстр, ведь все хранится в памяти». Но это только часть. Настоящая причина, по которой Redis так хорош по сравнению с другими решениями, заключается в его специализированных структурах данных.
176 |
177 | Насколько высока скорость? Это зависит от многих вещей: какие команды и типы данных вы используете и так далее. Но производительность Redis обычно измеряется в десятках тысяч или даже сотнях тысяч операций **в секунду**. Вы можете запустить `redis-benchmark` (расположен в той же директории, что и `redis-server` и `redis-cli`), чтобы проверить это.
178 |
179 | Однажды я переписал код, использовавший традиционную модель, для использования Redis. Нагрузочный тест, написанный мной, занимал более 5 минут при использовании реляционной модели данных. Он занял примерно 150 мс с Redis. Вы не всегда будете получать такой значительный прирост, но я надеюсь, что этот пример даст вам \linebreak представление о том, с чем мы имеем дело.
180 |
181 | Важно понимать этот аспект Redis, поскольку это влияет на то, как ваше приложение с ним взаимодействует. Разработчики с опытом работы в SQL обычно стараются минимизировать количество обращений к базе данных. Это хороший совет для всех систем, включая Redis. Но, с учетом того, что мы имеем дело с более простыми структурами данных, нам иногда придется обращаться к серверу Redis несколько раз для достижения цели. Такой шаблон доступа к данным может показаться неестественным на первый взгляд, но на самом деле это незначительно по сравнению с получаемой взамен производительностью.
182 |
183 | ### В Этой Главе
184 |
185 | Несмотря на то, что мы лишь немного поиграли с Redis, был рассмотрен широкий спектр вопросов. Не волнуйтесь, если что-то не ясно до конца - как, например, запросы данных. В следующей главе мы перейдем к практике и все ваши вопросы, я надеюсь, найдут ответы.
186 |
187 | Ключевые моменты этой главы:
188 |
189 | * Ключи - это строки, идентифицирующие фрагменты данных (значения)
190 |
191 | * Значения - это произвольные массивы байт, которым Redis не придает никакого смысла
192 |
193 | * Redis предоставляет пять специализированных структур данных (он реализован на их основе)
194 |
195 | * Все упомянутое вместе делает Redis быстрым и простым в использовании, но не подходящим для любого сценария использования
196 |
197 | \clearpage
198 |
199 | ## Глава 2 - Структуры Данных
200 |
201 | Пришло время взглянуть поближе на пять структур данных Redis. Мы узнаем, что \linebreak представляет собой каждая из этих структур, что с ней можно делать, и в каких случаях она будет полезна.
202 |
203 | До этого момента, мы видели в Redis только команды, ключи и значения. Не было сказано ничего конкретного о структурах данных. Как Redis решал какую структуру данных использовать, когда мы выполняли команду `set`? На самом деле каждая команда \linebreak применима только к одной конкретной структуре данных. Например, когда вы используете `set`, данные сохраняются в виде строки (string). Когда вы используете `hset`, вы сохраняете данные в хеше (hash). С учетом небольшого количества команд в Redis, к этому достаточно просто привыкнуть.
204 |
205 | **[Сайт Redis](http://redis.io/commands) содержит отличный справочный раздел. Нет смысла повторять уже проделанную работу. Мы рассмотрим только самые важные команды, которые необходимы для понимания предназначения каждой структуры данных.**
206 |
207 | Нет ничего важнее, чем получать удовольствие от практики и экспериментов. Вы всегда можете стереть все значения в вашей базе данных, введя команду `flushdb`, поэтому не стесняйтесь и попробуйте сделать что-нибудь интересное!
208 |
209 | ### Строки (Strings)
210 |
211 | Строки являются самой простой структурой данных в Redis. Когда вы думаете о паре ключ-значение, вы думаете о строках. Имея уникальные имена (ключи), значения строк могут быть какими угодно. Я предпочитаю называть их «скалярными величинами» - возможно, это только мое предпочтение.
212 |
213 | Мы уже видели типичный пример использования строк: хранение значений объектов с определенными ключами. Это именно то, с чем вам придется сталкиваться наиболее часто:
214 |
215 | set users:leto "{name: leto, planet: dune, likes: [spice]}"
216 |
217 | Дополнительно, Redis позволяет выполнять некоторые стандартные действия со строками. Например, `strlen <ключ>` используется для вычисления длины значения, связанного с ключом; `getrange <ключ> <начало> <конец>` возвращает подстроку из строки; `append <ключ> <значение>` добавляет введенное значение к концу существующей строки (или создает новое значение, если указанный ключ не определен). Попробуйте эти команды в действии. Вот что получилось у меня:
218 |
219 | > strlen users:leto
220 | (integer) 42
221 |
222 | > getrange users:leto 27 40
223 | "likes: [spice]"
224 |
225 | > append users:leto " OVER 9000!!"
226 | (integer) 54
227 |
228 | Сейчас вы наверное думаете, что это, конечно, замечательно, но лишено смысла. С помощью этих операций невозможно (в общем случае) получить или добавить значение в JSON-строку. Вы правы - некоторые команды, особенно для работы со строками, имеют смысл только при использовании специфичных форматов данных.
229 |
230 | Ранее мы узнали, что Redis не придает смысла введенным вами значениям. Это \linebreak действительно так в большинстве случаев. Тем не менее, некоторые команды \linebreak предназначены лишь для строк, значения которых удовлетворяют определенным условиям. В качестве грубого примера можно привести использование команд `append` и `getrange` для какого-нибудь нестандартного способа сериализации данных (*сериализация - \linebreak преобразование произвольного объекта/данных в строку - прим. перев.*) Более наглядным примером будут команды `incr`, `incrby`, `decr` и `decrby`. Они предназначены для выполнения инкремента/декремента (увеличения/уменьшения значения) значений строк (*в примерах ниже строки в этих командах интерпретируются как числа, в связи с чем используемый автором термин «скалярные значения» (scalars) имеет более точное значение, чем «строки» - прим. перев.*):
231 |
232 | > incr stats:page:about
233 | (integer) 1
234 | > incr stats:page:about
235 | (integer) 2
236 |
237 | > incrby ratings:video:12333 5
238 | (integer) 5
239 | > incrby ratings:video:12333 3
240 | (integer) 8
241 |
242 | Как вы можете догадаться, строки Redis отлично подходят для аналитики. Попробуйте инкрементировать значение `users:leto` (не-числовое) и посмотреть, что получится (вы должны получить ошибку).
243 |
244 | Более сложным примером будут команды `setbit` и `getbit`. Есть [замечательный пост](http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/) о том, как Spool (*Spool - приложение, на которое ссылаются выше - прим. перев.*) использует эти две команды для эффективного ответа на вопрос «сколько уникальных посетителей было у нас сегодня?». Для 128 миллионов пользователей ноутбук генерирует ответ менее чем за 50 мс и использует всего лишь 16 МБ памяти.
245 |
246 | Не так уж важно понимание того, как работают битовые маски и как Spool использует их, но важно понять, что строки в Redis являются гораздо более мощным инструментом, чем кажется на первый взгляд. Тем не менее, наиболее частыми примерами использования являются те, что мы видели выше: хранение объектов (простых или сложных) и счетчиков. Кроме того, поскольку получение значения по ключу настолько быстрая операция, строки часто используются для кеширования данных.
247 |
248 | ### Хеши (Hashes)
249 |
250 | Хеши - хороший пример того, почему называть Redis хранилищем пар ключ-значение не совсем корректно. Хеши во многом похожи на строки. Важным отличием является то, что они предоставляют дополнительный уровень адресации данных - поля (fields). Эквивалентами команд `set` и `get` для хешей являются:
251 |
252 | hset users:goku powerlevel 9000
253 | hget users:goku powerlevel
254 |
255 | Мы также можем устанавливать значения сразу нескольких полей, получать все поля со значениями, выводить список всех полей и удалять отдельные поля:
256 |
257 | hmset users:goku race saiyan age 737
258 | hmget users:goku race powerlevel
259 | hgetall users:goku
260 | hkeys users:goku
261 | hdel users:goku age
262 |
263 | Как вы видите, хеши дают чуть больше контроля, чем строки. Вместо того, чтобы хранить данные о пользователе в виде одного сериализованного значения, мы можем использовать хеш для более точного представления. Преимуществом будет возможность извлечения, изменения и удаления отдельных частей данных без необходимости читать и записывать все значение целиком.
264 |
265 | Рассматривая хеши как структурированные объекты, такие как данные пользователя, важно понимать, как они работают. И это правда, что такой подход может быть полезен для повышения производительности. Тем не менее, в следующей главе мы увидим, как хеши могут использоваться для организации ваших данных и удобства запросов к ним. Мне кажется, что именно в этом хеши особенно хороши.
266 |
267 | ### Списки (Lists)
268 |
269 | Списки позволяют хранить и манипулировать массивами значений для заданного ключа. Можно добавлять значения в список, получать первое и последнее значение из списка и манипулировать значениями с заданными индексами. Списки сохраняют порядок своих значений и имеют эффективные операции с использованием индексов. Мы можем создать список `newusers`, содержащий последних зарегистрировавшихся на нашем сайте \linebreak пользователей:
270 |
271 | lpush newusers goku
272 | ltrim newusers 0 49
273 |
274 | Сначала мы добавляем нового пользователя в начало списка, затем укорачиваем список так, чтобы он содержал 50 последних записей. Это типичный пример использования. Операция `ltrim` имеет асимптотическую сложность O(N), где N - число значений, которое мы удаляем. В этом случае, если мы всегда укорачиваем список после каждой единичной вставки, эта операция имеет постоянную производительность O(1) (потому что N всегда будет равным единице).
275 |
276 | Сейчас мы первый раз столкнемся с тем, что значение с одним ключом ссылается на значение с другим ключом. Если мы хотим получить сведения о последних 10 \linebreak пользователях, мы должны сделать следующее:
277 |
278 | keys = redis.lrange('newusers', 0, 9)
279 | redis.mget(*keys.map {|u| "users:#{u}"})
280 |
281 | Приведенный выше код на языке Ruby показывает случай многократного обращения к базе данных, о котором мы говорили выше.
282 |
283 | Конечно, списки хороши не только для хранения ссылок на другие ключи. Значения могут быть чем угодно. Можно использовать списки для хранения записей журнала или пути, по которому пользователь перемещается по вашему сайту. Если вы создаете игру, вы можете использовать список для хранения последовательности действий пользователя.
284 |
285 | ### Множества (Sets)
286 |
287 | Множества используются для хранения уникальных значений и предоставляют набор операций - таких, как объединение. Множества не упорядочены, но предоставляют \linebreak эффективные операции со значениями. Список друзей является классическим примером использования множеств:
288 |
289 | sadd friends:leto ghanima paul chani jessica
290 | sadd friends:duncan paul jessica alia
291 |
292 | Независимо от того, сколько друзей имеет пользователь, мы можем эффективно (O(1)) определить, являются ли пользователи userX и userY друзьями, или нет.
293 |
294 | sismember friends:leto jessica
295 | sismember friends:leto vladimir
296 |
297 | Более того, мы можем узнать, имеют ли два пользователя общих друзей:
298 |
299 | sinter friends:leto friends:duncan
300 |
301 | и даже сохранить результат этой операции под новым ключом:
302 |
303 | sinterstore friends:leto_duncan friends:leto friends:duncan
304 |
305 | Множества отлично подходят для теггинга (*tagging, присвоение семантических ярлыков-меток - прим. перев.*) и отслеживания любых других свойств, для которых повторы не имеют смысла (или там, где мы хотим использовать операции над множествами, такие как пересечение и объединение).
306 |
307 | ### Упорядоченные Множества (Sorted Sets)
308 |
309 | Последней и самой мощной структурой данных являются упорядоченные множества. Хеши похожи на строки, но имеют поля, а упорядоченные множества похожи на множества, но имеют счетчики. Счетчики предоставляют возможности упорядочивания и \linebreak ранжирования. Если мы хотим получить ранжированный список друзей, мы можем сделать следующее:
310 |
311 | zadd friends:leto 100 ghanima 95 paul 95 chani 75 jessica 1 vladimir
312 |
313 | Хотим узнать, сколько друзей с рейтингом 90 и выше имеет пользователь `leto`?
314 |
315 | zcount friends:leto 90 100
316 |
317 | Как насчет рейтинга `chani`?
318 |
319 | zrevrank friends:leto chani
320 |
321 | Мы используем `zrevrank` вместо `zrank`, поскольку по умолчанию в Redis сортировка \linebreak происходит от меньшего к большему (мы ранжируем от большего к меньшему). Самым очевидным примером использования упорядоченных множеств являются системы \linebreak рейтингов. На самом деле упорядоченные множества подойдут для любых случаев, когда вы имеете значения, которые вы хотите упорядочить, используя для этого целочисленные «весовые коэффициенты», и которыми вы хотите управлять на основании этих \linebreak коэффициентов.
322 |
323 | В следующей главе мы увидим как упорядоченные множества могут использоваться для отслеживания событий, основанных на времени (когда время используется в качестве весового коэффициента), что является другим типичным примером использования \linebreak упорядоченных множеств.
324 |
325 | ### В Этой Главе
326 |
327 | Вы обзорно познакомились с пятью структурами данных Redis. Одна из замечательных особенностей Redis состоит в том, что вы можете сделать больше, чем вам показалось на первый взгляд. Вероятно, есть способы использования строк и упорядоченных множеств, о которых еще никто не догадался. Тем не менее, понимая стандартные способы их применения, вы сможете использовать Redis для решения любых типов задач. Кроме того, нет нужды использовать все структуры данных и операции над ними только потому, что Redis дает такую возможность. Не так уж редко многие задачи решаются весьма ограниченным набором команд.
328 |
329 | \clearpage
330 |
331 | ## Глава 3 - Использование Структур Данных
332 |
333 | В предыдущей главе мы говорили о пяти структурах данных и увидели несколько \linebreak примеров того, какие задачи они могут решать. Теперь пришло время посмотреть на более специфичные, но все же довольно распространенные, особенности и шаблоны проектирования.
334 |
335 | ### Асимптотическая Сложность (Запись «Большое O»)
336 |
337 | В этой книге мы уже упоминали запись асимптотической сложности (или времени \linebreak выполнения) («большого О») в форме O(N) и O(1). Эта форма записи используется для объяснения того, как некий алгоритм ведет себя при определенном количестве элементов, над которыми совершается операция. В Redis это показывает нам, насколько быстро исполняется команда в зависимости от числа элементов в структуре данных, с которой мы работаем. (*Здесь автор неточен. Это верно не только для Redis, а вообще для любых алгоритмов, причем не только в зависимости от числа элементов, но и от их значений - например, сложность алгоритма вычисления факториала, где в качестве аргумента \linebreak выступает всего одно число - прим. перев.*)
338 |
339 | Документация Redis содержит сведения об асимптотической сложности для каждой \linebreak команды. Это также указывает на то, какие факторы влияют на производительность. Давайте рассмотрим несколько примеров.
340 |
341 | Самые быстрые алгоритмы имеют сложность O(1) - константу. Независимо от того, \linebreak выполняется ли операция над 5 элементами или 5 млн. элементов, вы получаете \linebreak одинаковую производительность. Команда `sismember`, показывающая, принадлежит ли элемент множеству, имеет сложность O(1). `sismember` - мощная команда, и ее \linebreak производительность, помимо прочего, является тому причиной. Многие команды Redis имеют такую сложность.
342 |
343 | Логарифмическая сложность - O(log(N)) - следующая по скорости, поскольку нуждается в сканировании все меньшего и меньшего числа элементов. При использовании такого подхода, как «разделяй и властвуй», даже огромное количество элементов быстро \linebreak разбивается на части за несколько итераций. Команда `zadd` имеет сложность O(log(N)), где N - количество элементов, которые уже включены во множество.
344 |
345 | Далее следуют линейные по сложности команды - O(N). Поиск в неиндексированной строке таблицы, а также использование команды `ltrim` являются операциями с такой сложностью. Тем не менее, в случае с `ltrim` N - это не общее количество элементов в списке, а количество удаляемых элементов. Использование `ltrim` для удаления 1 элемента из списка с миллионом элементов будет быстрее, чем удаление 10 элементов из списка, содержащего сотни элементов. (Хотя обе операции, вероятно, будут настолько быстрыми, что вы не сможете измерить их время.)
346 |
347 | Команда `zremrangebyscore`, удаляющая элементы упорядоченного множества с весовыми коэффициентами в диапазоне между минимальным и максимальным указанным значением, имеет сложность O(log(N)+M). То есть имеет смешанную сложность. Читая документацию, мы видим, что N - это общее число элементов множества, а M - количество удаляемых элементов. Другими словами, количество удаляемых элементов, вероятно, сильнее \linebreak повлияет на производительность, чем общее количество элементов.
348 |
349 | Команда `sort`, которую мы более детально рассмотрим в следующей главе, обладает сложностью O(N+M*log(M)). Из этой записи вы можете заключить, что это, вероятно, наиболее сложная в вычислительном плане команда Redis.
350 |
351 | Есть и другие степени сложности, из которых две наиболее распространенные - O(N^2) и O(C^N). Чем больше N, тем хуже производительность по сравнению с меньшими N. (*Это верно и для других форм, описанных выше - прим. перев.*) Никакие из команд Redis не имеют такой сложности.
352 |
353 | Важно заметить, что асимптотическая запись «большого O» указывает на худший случай. Когда мы говорим, что выполнение операции займет время, определяемое как O(N), мы на самом деле можем получить результат сразу же, но может случиться и так, что искомый элемент окажется самым последним.
354 |
355 | ### Псевдо-Многоключевые Запросы
356 |
357 | Типичной ситуацией, в которую вы будете попадать, будет необходимость запрашивать одно и то же значение по разным ключам. Например, вы можете хотеть получить данные пользователя по адресу электронной почты (в случае, если пользователь входит на сайт впервые) и по идентификатору (после входа пользователя на сайт). Одним из ужасных решений будет дублирование объекта в двух строковых значениях:
358 |
359 | set users:leto@dune.gov "{id: 9001, email: 'leto@dune.gov', ...}"
360 | set users:9001 "{id: 9001, email: 'leto@dune.gov', ...}"
361 |
362 | Это неправильно, поскольку такими данными трудно управлять, и они занимают в два раза больше памяти.
363 |
364 | Было бы здорово, если бы Redis позволял создавать связь между двумя ключами, но такой возможности нет (и скорее всего никогда не будет). Главным принципом развития Redis является простота кода и программного интерфейса (API). Внутренняя реализация связанных ключей (есть много вещей, которые можно делать с ключами, о чем мы еще не говорили) не стоит возможных усилий, если мы увидим, что Redis уже предоставляет решение - хеши.
365 |
366 | Используя хеш, мы можем избавиться от необходимости дублирования:
367 |
368 | set users:9001 "{id: 9001, email: leto@dune.gov, ...}"
369 | hset users:lookup:email leto@dune.gov 9001
370 |
371 | Мы используем поле как вторичный псевдо-индекс, и получаем ссылку на единственный объект, представляющий пользователя. Чтобы получить пользователя по идентификатору, мы используем обычную команду `get`:
372 |
373 | get users:9001
374 |
375 | Чтобы получить пользователя по адресу электронной почты, мы воспользуемся сначала `hget`, а затем `get` (код на Ruby):
376 |
377 | id = redis.hget('users:lookup:email', 'leto@dune.gov')
378 | user = redis.get("users:#{id}")
379 |
380 | Это то, чем вы, скорее всего, будете пользоваться очень часто. Для меня это как раз тот случай, когда хеши особенно хороши, но это не очевидный способ использования, пока вы не увидите это своими глазами.
381 |
382 | ### Ссылки и Индексы
383 |
384 | Мы рассмотрели пару примеров, где одно значение ссылается на другое. Мы видели это, когда смотрели на пример со списком, и мы видели это в разделе выше, когда использовали хеши для того, чтобы сделать запросы немного проще. Это в итоге приводит к необходимости ручного управления индексами и ссылками между значениями. Честно говоря, можно назвать это недостатком, особенно когда вы столкнетесь с необходимостью управлять этими ссылками, обновлять и удалять их вручную. В Redis нет магического решения этой проблемы.
385 |
386 | Мы уже видели, как множества часто используются для реализации ручного индекса:
387 |
388 | sadd friends:leto ghanima paul chani jessica
389 |
390 | Каждый элемент этого множества является ссылкой на строковое значение, содержащее информацию о пользователе. А что, если `chani` изменит свое имя или удалит учетную запись? Может быть, имеет смысл хранить обратные взаимосвязи:
391 |
392 | sadd friends_of:chani leto paul
393 |
394 | Не беря в расчет сложность поддержки такой структуры, если вы похожи на меня, вы, вероятно, испугаетесь дополнительных расходов памяти и времени обработки таких \linebreak дополнительных индексных значений. В следующем разделе мы поговорим о путях \linebreak снижения затрат производительности, возникающих из-за необходимости дополнительных обращений к базе данных (мы кратко упоминали об этом в первой главе).
395 |
396 | Если вы задумаетесь об этом, то поймете, что реляционные базы данных также имеют эти накладные расходы. Индексы занимают память, они должны сканироваться и затем соответствующие записи должны извлекаться. От накладных расходов ловко \linebreak абстрагируются (делается множество оптимизаций для того, чтобы сделать обработку максимально эффективной).
397 |
398 | Необходимость ручного управления ссылками в Redis огорчает. Но все первоначальные опасения о влиянии на производительность и потребление памяти должны быть проверены на практике. Я думаю, вы обнаружите, что это не такая уж большая проблема.
399 |
400 | ### Дополнительные Запросы и Конвейерная Обработка
401 |
402 | Мы уже упоминали, что частые обращения к серверу являются типичными во время \linebreak использования Redis. Поскольку это делается часто, будет полезно узнать больше о возможностях, которые мы можем использовать для получения наилучших результатов.
403 |
404 | Прежде всего, команды или принимают один или более параметров, или существуют подобные команды, которые принимают несколько параметров. Ранее мы видели команду `mget`, принимающую несколько ключей и возвращающую их значения:
405 |
406 | keys = redis.lrange('newusers', 0, 10)
407 | redis.mget(*keys.map {|u| "users:#{u}"})
408 |
409 | Или команда `sadd`, добавляющая одно или более значений к множеству:
410 |
411 | sadd friends:vladimir piter
412 | sadd friends:paul jessica leto "leto II" chani
413 |
414 | Redis также поддерживает конвейерную обработку. Обычно, когда клиент посылает \linebreak запрос к Redis, он ждет ответа, прежде чем послать следующий запрос. С конвейерной обработкой вы можете посылать несколько запросов без ожидания момента, когда они вернут результат. Это снижает накладные расходы обмена данными по сети и может значительно увеличить производительность.
415 |
416 | Для Redis ничего не стоит использовать память для создания очереди запросов, поэтому хорошей идеей будет группировать запросы. Насколько большим будет используемый вами пакет запросов зависит от того, какие команды вы используете, и, что более важно, каков размер их параметров. Но, если вы используете команды с длиной параметров примерно в 50 символов, вы, вероятно, можете группировать их тысячами или десятками тысяч.
417 |
418 | То, как именно вы исполняете команды в конвейере, будет зависеть от используемого драйвера. В Ruby вы передаете блок в метод `pipelined`:
419 |
420 | redis.pipelined do
421 | 9001.times do
422 | redis.incr('powerlevel')
423 | end
424 | end
425 |
426 | Как вы можете догадаться, конвейерная обработка может значительно ускорить \linebreak импортирование пакета запросов!
427 |
428 | ### Транзакции
429 |
430 | Каждая команда Redis атомарна, включая команды, которые делают несколько вещей. Дополнительно, Redis поддерживает транзакции при использовании нескольких команд.
431 |
432 | Возможно, вы знаете, что Redis на самом деле работает в один поток, благодаря чему и гарантируется атомарность (неделимость) каждой команды. Пока исполняется одна команда, остальные не могут быть исполнены. (Мы кратко обсудим масштабирование позже.) Это бывает полезно, когда речь идет о командах, делающих несколько вещей. Например:
433 |
434 | `incr` - это, по сути, `get` за которой следует `set`
435 |
436 | `getset` устанавливает новое значение и возвращает старое
437 |
438 | `setnx` сначала проверяет, существует ли ключ, и устанавливает значение только в том случае, если ключ не существовал
439 |
440 | Несмотря на то, что эти команды полезны, вам неизбежно понадобится исполнять \linebreak несколько команд как атомарную группу. Вы можете это сделать, вызвав сначала команду `multi`, за которой следуют команды, которые вы хотите выполнить как часть транзакции, и в конце вызвать команду `exec` для того, чтобы выполнить команды, или `discard`, чтобы отменить их выполнение. Какие гарантии дает Redis касательно транзакций?
441 |
442 | * Команды исполнятся по порядку
443 |
444 | * Команды исполнятся как единая, неделимая операция (никакая другая команда не будет исполнена в ходе выполнения транзакции)
445 |
446 | * Будут выполнены либо все команды транзакции, либо ни одна из них
447 |
448 | Вы можете, и должны, попробовать это в командной строке. Также обратите внимание, что нет причин отказываться от использования конвейерной обработки и транзакций вместе.
449 |
450 | multi
451 | hincrby groups:1percent balance -9000000000
452 | hincrby groups:99percent balance 9000000000
453 | exec
454 |
455 | Наконец, Redis позволяет указывать ключ (или ключи) для отслеживания изменений, и применять транзакцию, если ключ(и) изменился(-лись). Это используется, когда вам нужно получить значения и исполнить код в зависимости от них в одной транзакции. В коде выше мы не смогли бы реализовать собственную команду `incr`, поскольку все команды исполняются вместе, когда вызывается `exec`. Мы не можем сделать так:
456 |
457 | redis.multi()
458 | current = redis.get('powerlevel')
459 | redis.set('powerlevel', current + 1)
460 | redis.exec()
461 |
462 | Транзакции в Redis работают иначе. Но, если мы добавим `watch` к `powerlevel`, мы сможем сделать следующее:
463 |
464 | redis.watch('powerlevel')
465 | current = redis.get('powerlevel')
466 | redis.multi()
467 | redis.set('powerlevel', current + 1)
468 | redis.exec()
469 |
470 | Если другой клиент изменит значение ключа `powerlevel` после того, как мы вызвали `watch` для этого ключа, наша транзакция не будет исполнена. Если же значение не изменится, все сработает. Мы можем исполнять этот код в цикле, пока он не сработает.
471 |
472 | ### Анти-Шаблон Использования Ключей
473 |
474 | В следующей главе мы поговорим о командах, не относящихся непосредственно к \linebreak структурам данных. Некоторые из них служат для администрирования и отладки. Но есть одна, о которой я бы хотел упомянуть отдельно - `keys`. Эта команда принимает шаблон и находит все ключи, соответствующие ему. Эта команда кажется подходящей для определенного рода задач, но ее не следует использовать в готовом приложении. Почему? Потому что она осуществляет линейный поиск совпадений с шаблоном по всем ключам. Проще говоря, она медленная.
475 |
476 | Как же ее используют? Допустим, вы создаете веб-приложения для баг-трекинга \linebreak (отслеживания ошибок). Каждая учетная запись будет иметь идентификатор `id_аккаунта` и вы можете решить хранить каждую ошибку в строковом значении с ключом в формате `bug:id_аккаунта:id_ошибки`. Если вам когда-нибудь понадобится найти все ошибки для определенной учетной записи (для отображения или удаления в случае удаления учетной записи), вы, возможно, будете (как я когда-то) использовать команду `keys`:
477 |
478 | keys bug:1233:*
479 |
480 | Лучшим решением будет использование хеша. Так же, как мы можем использовать хеши в качестве вторичного индекса, мы можем использовать их для организации данных:
481 |
482 | hset bugs:1233 1 "{id:1, account: 1233, subject: '...'}"
483 | hset bugs:1233 2 "{id:2, account: 1233, subject: '...'}"
484 |
485 | Для получения идентификаторов всех ошибок для заданной учетной записи мы просто вызовем `hkeys bugs:1233`. Для удаления определенной ошибки мы можем использовать `hdel bugs:1233 2`, а для удаления учетной записи со всеми данными - `del bugs:1233`.
486 |
487 | ### В Этой Главе
488 |
489 | Надеюсь, что эта глава вместе с предыдущей дали вам основы для понимания того, как использовать Redis для реализации полезных возможностей. Есть и другие шаблоны, которые вы можете использовать для построения приложений другого типа, но суть здесь состоит в понимании фундаментальных структур данных и того, как они могут быть \linebreak использованы для реализации решений, лежащих за пределами того, что вы изначально могли себе вообразить.
490 |
491 | \clearpage
492 |
493 | ## Глава 4 - За Пределами Структур Данных
494 |
495 | Несмотря на то, что пять структур данных формируют основу Redis, в системе также есть команды, не относящиеся к структурам данных. Мы уже видели некоторые из них: `info`, `select`, `flushdb`, `multi`, `exec`, `discard`, `watch` и `keys`. В этой главе мы рассмотрим несколько других подобных команд.
496 |
497 | ### Срок Существования Ключей
498 |
499 | Redis позволяет назначать ключам срок существования. Вы можете использовать \linebreak абсолютные значения времени в формате Unix (Unix timestamp, количество секунд, \linebreak прошедших с 1 января 1970 года) или оставшееся время существования в секундах. Эта команда оперирует ключами, поэтому неважно, какая структура данных при этом используется.
500 |
501 | expire pages:about 30
502 | expireat pages:about 1356933600
503 |
504 | Первая команда удалит ключ (и ассоциированное с ним значение) по истечении 30 секунд. Вторая сделает то же самое в 12:00, 31 декабря 2012 года.
505 |
506 | Это делает Redis идеальным движком для кеширования. Вы можете узнать, сколько еще будет существовать заданный элемент, с помощью команды `ttl`, а также удалить срок существования с помощью команды `persist`:
507 |
508 | ttl pages:about
509 | persist pages:about
510 |
511 | Наконец, есть специальная строковая команда `setex`, позволяющая создавать строковые значения с указанием времени существования одной атомарной операцией (в большей степени только для удобства):
512 |
513 | setex pages:about 30 'about us
....'
514 |
515 | (*обновление значения по ключу сотрет информацию о сроке существования, поэтому эту информацию также необходимо обновлять - прим. перев.*)
516 |
517 | ### Публикация Сообщений и Подписка
518 |
519 | Над списками в Redis опеределены команды `blpop` и `brpop`, которые возвращают и удаляют, соответственно, первый и последний элементы списка, или же блокируют список, пока указанные элементы в нем не появятся. Это может быть использовано для создания простой очереди.
520 |
521 | Более того, Redis поддерживает публикацию сообщений и подписку на каналы как на объекты первого класса (*То есть эти объекты можно создавать, изменять, удалять и т.д. - прим. перев.*). Вы можете сами попробовать это, запустив вторую копию `redis-cli` в другом окне. В первом окне подпишемся на канал (назовем его `warnings`):
522 |
523 | subscribe warnings
524 |
525 | Ответом будет информация о подписке. Теперь, в другом окне, опубликуем сообщение в канал `warnings`:
526 |
527 | publish warnings "it's over 9000!"
528 |
529 | Если вы вернетесь в первое окно, вы должны увидеть сообщение, полученное через канал `warnings`.
530 |
531 | Можно подписываться на несколько каналов (`subscribe channel1 channel2 channel3 ...`) или на каналы, названия которых соответствуют шаблону (`psubscribe warnings:*`), и использовать команды `unsubscribe` и `punsubscribe` для прекращения подписки на один или более канал.
532 |
533 | Наконец, обратите внимание, что команда `publish` вернула значение 1. Это значение показывает число клиентов, получивших сообщение.
534 |
535 | ### Мониторинг и Журнал Медленных Запросов
536 |
537 | Команда `monitor` позволяет вам увидеть, что Redis собирается сделать. Этот отличный инструмент отладки дает вам понимание того, как ваше приложение взаимодействует с Redis. В одном из окон `redis-cli` (если одна из копий все еще подписана на канал, вы можете использовать команду `unsubscribe` или же закрыть окно и открыть новое) введите команду `monitor`. В другом запустите команду другого типа (например, `get` или `set`). Вы должны увидеть эти команды вместе с их параметрами в первом окне.
538 |
539 | Не следует запускать команду `monitor` на «боевом» сервере, это действительно только средство отладки и разработки. Кроме этого, ничего более об этой команде сказать нельзя. Это просто очень удобный инструмент.
540 |
541 | Помимо `monitor`, Redis имеет команду `slowlog`, которая служит отличным инструментом для профилирования (*Измерения производительности - прим. перев.*). Она записывает в журнал все команды, выполнение которых заняло больше времени, чем указанное число **микро**секунд. В следующем разделе мы кратко рассмотрим, как конфигурировать Redis, а сейчас вы можете настроить систему на журналирование всех команд, введя:
542 |
543 | config set slowlog-log-slower-than 0
544 |
545 | Далее, запустите несколько команд. Теперь вы можете получить все записи журнала или только самые последние, введя:
546 |
547 | slowlog get
548 | slowlog get 10
549 |
550 | Вы также можете получить число записей в журнале медленных запросов, введя `slowlog len`
551 |
552 | Для каждой введенной вами команды вы должны увидеть четыре параметра:
553 |
554 | * Автоинкрементирующийся идентификатор
555 |
556 | * Время (в формате Unix), когда команда была выполнена
557 |
558 | * Продолжительность выполнения команды в микросекундах
559 |
560 | * Саму команду с параметрами
561 |
562 | Журнал медленных запросов поддерживается в памяти, поэтому его ведение на рабочем сервере даже с низким порогом журналирования не должно создавать проблем. По \linebreak умолчанию в журнале будут храниться последние 1024 записи.
563 |
564 | ### Сортировка
565 |
566 | Одна из наиболее мощных команд в Redis - `sort`. Она позволяет сортировать значения в списке, множестве или упорядоченном множестве (элементы упорядоченных множеств упорядочены по значению своих весовых коэффициентов, а не по хранящимся значениям). В самой простой форме, эта команда позволяет сделать следующее:
567 |
568 | rpush users:leto:guesses 5 9 10 2 4 10 19 2
569 | sort users:leto:guesses
570 |
571 | Последняя команда вернет значения, отсортированные от меньшего к большему. Вот более сложный пример:
572 |
573 | sadd friends:ghanima leto paul chani jessica alia duncan
574 | sort friends:ghanima limit 0 3 desc alpha
575 |
576 | Команда выше показывает, сколько значений выводить (с помощью `limit`), как получать результат в порядке убывания (посредством `desc`), и как упорядочивать по алфавиту, а не по числовым значениям (посредством `alpha`).
577 |
578 | Реальная мощь `sort` состоит в способности сортировать на основании объектов по ссылкам. Ранее мы видели, что списки, множества и упорядоченные множества часто используются для указания ссылок на другие объекты Redis. Команда `sort` может получать значения по этим ссылкам и сортировать их. Например, у нас есть система отслеживания ошибок (баг-трекер), позволяющая пользователям следить за статусами ошибок. Мы можем \linebreak использовать множество для поддержания списка ошибок, за которыми ведется \linebreak наблюдение:
579 |
580 | sadd watch:leto 12339 1382 338 9338
581 |
582 | Удобно будет сортировать по идентификатору (что по умолчанию и произойдет), но нам также хотелось бы сортировать по критичности ошибки. Чтобы это сделать, мы указываем Redis, по какому шаблону проводить сортировку. Сначала, давайте добавим больше данных, чтобы увидеть значимые результаты:
583 |
584 | set severity:12339 3
585 | set severity:1382 2
586 | set severity:338 5
587 | set severity:9338 4
588 |
589 | Чтобы отсортировать ошибки по критичности от более важных к менее важным, введите следующее:
590 |
591 | sort watch:leto by severity:* desc
592 |
593 | Redis подставит значения из нашего списка (а также множества или упорядоченного множества) вместо `*` в указанном шаблоне. Это создаст имена ключей, по которым Redis будет запрашивать значения для сортировки.
594 |
595 | Хотя вы можете иметь миллионы ключей в Redis, я думаю, что данные в примере выше могут стать несколько нагроможденными. К счастью, сортировка может также работать на хешах и их полях. Вместо того, чтобы иметь кучу ключей верхнего уровня, вы можете использовать хеши:
596 |
597 | hset bug:12339 severity 3
598 | hset bug:12339 priority 1
599 | hset bug:12339 details "{id: 12339, ....}"
600 |
601 | hset bug:1382 severity 2
602 | hset bug:1382 priority 2
603 | hset bug:1382 details "{id: 1382, ....}"
604 |
605 | hset bug:338 severity 5
606 | hset bug:338 priority 3
607 | hset bug:338 details "{id: 338, ....}"
608 |
609 | hset bug:9338 severity 4
610 | hset bug:9338 priority 2
611 | hset bug:9338 details "{id: 9338, ....}"
612 |
613 | Теперь все не только лучше организовано, и мы имеем возможность сортировать по \linebreak параметрам `severity` (критичность) и `priority` (приоритет), но также можем указать \linebreak команде `sort`, значения каких полей извлекать:
614 |
615 | sort watch:leto by bug:*->priority get bug:*->details
616 |
617 | Подстановка значений осталась прежней, но теперь Redis также распознает \linebreak последовательности `->` и будет использовать их для обращения к определенному полю хеша. Мы также включили параметр `get`, который используется в подстановке и поиске значения поля, для извлечения подробных сведений (details) об ошибке.
618 |
619 | На больших множествах `sort` может быть медленной. К счастью, возвращаемое командой `sort` значение может быть сохранено:
620 |
621 | sort watch:leto by bug:*->priority get bug:*->details store watch_by_priority:leto
622 |
623 | Комбинирование возможности сохранения (параметр `store`) команды `sort` с указанием срока существования, который обсуждался выше, будет отличным решением.
624 |
625 | ### В Этой Главе
626 |
627 | Эта глава посвящена командам, не связанным со структурами данных. Как и везде, их использование ситуативно. Не так уж редки случаи построения приложений, не \linebreak использующих указание срока существования, публикации сообщений и подписки, \linebreak сортировки. Но полезно знать, что такие возможности существуют. Кроме того, мы рассмотрели лишь несколько команд. Кроме них есть и другие, и когда вы усвоите материал этой книги, имеет смысл ознакомиться с [полным списком](http://redis.io/commands).
628 |
629 | \clearpage
630 |
631 | ## Глава 5 - Администрирование
632 |
633 | Наша последняя глава посвящена некоторым аспектам администрирования Redis. Она ни в коем случае не является полным руководством. В лучшем случае мы осветим некоторые базовые вопросы, с которыми сталкиваются новые пользователи Redis.
634 |
635 | ### Конфигурирование
636 |
637 | Когда вы впервые запустили сервер Redis, он предупредил вас, что файл `redis.conf` не может быть найден. Этот файл может быть использован для настройки различных параметров Redis. Хорошо документированный файл `redis.conf` доступен для каждой release-версии Redis. Образец файла содержит стандартные варианты конфигурации, что очень полезно для понимания того, для чего предназначены конкретные опции и каковы их значения по умолчанию. Вы можете найти его на .
638 |
639 | **Это конфигурационный файл для Redis 2.4.6. Вы должны заменить «2.4.6» в приведенном выше URL на номер вашей версии. Вы можете узнать вашу версию, выполнив команду `info`.**
640 |
641 | Конфигурационный файл содержит множество комментариев, поэтому мы не будем \linebreak рассматривать настройки.
642 |
643 | Кроме конфигурирования Redis через файл `redis.conf`, команда `config set` может быть использована для задания конкретных опций. Мы уже использовали ее, когда \linebreak устанавливали параметр `slowlog-log-slower-than` в 0.
644 |
645 | Существует также специальная команда `config get`, которая выводит значения параметров конфигурации. Эта команда поддерживает поиск по образцу. Мы можем посмотреть все опции, касающиеся журналирования следующим способом:
646 |
647 | config get *log*
648 |
649 | ### Авторизация
650 |
651 | Redis можно настроить так, чтобы он требовал пароль. За это отвечает специальная опция конфигурации `requirepass` (устанавливается через `redis.conf` или командой `config set`). Когда опция `requirepass` устанавливается в какое-либо значение (которое является паролем), клиенты должны выполнять команду `auth password` при обращении к серверу.
652 |
653 | Как только клиент проходит авторизацию, он может выполнять команды в любой базе данных. В том числе команду `flushall`, которая удаляет все ключи из всех баз. У вас есть возможность переименовать команды для обеспечения определенного уровня защиты:
654 |
655 | rename-command CONFIG 5ec4db169f9d4dddacbfb0c26ea7e5ef
656 | rename-command FLUSHALL 1041285018a942a4922cbf76623b741e
657 |
658 | Вы также можете полностью отключить команду, используя в качестве ее нового названия пустую строку.
659 |
660 | ### Ограничения Размеров
661 |
662 | Когда вы начнете использовать Redis, у вас может возникнуть вопрос: «Как много ключей я могу использовать?». Кроме этого, вас могут интересовать ограничения на количество полей в хешах (особенно, когда вы используете их для организации ваших данных) и количество элементов в списках и множествах. Для одного узла, лимиты представляют собой числа порядка сотен миллионов.
663 |
664 | ### Репликация
665 |
666 | Redis поддерживает репликацию, которая означает, что все данные, которые попадают на один узел Redis (который называется master) будут попадать также и на другие узлы (называются slave). Для конфигурирования slave-узлов можно изменить опцию `slaveof` или выполнить аналогичную по написанию команду (узлы, запущенные без подобных опций являются master-узлами).
667 |
668 | Репликация помогает защитить ваши данные, копируя их на другие сервера. Репликация также может быть использована для увеличения производительности, т.к. запросы на чтение могут обслуживаться slave-узлами. Эти узлы могут ответить слегка устаревшими данными, но для большинства приложений это приемлемо.
669 |
670 | К сожалению, система репликации Redis еще не поддерживает автоматическую \linebreak отказоустойчивость. Если master-узел выходит из строя, необходимо вручную выбрать новый из slave-узлов. Необходимо использовать традиционные утилиты, использующие мониторинг и специальные скрипты для переключения master-узлов, если вам необходима устойчивая к сбоям система.
671 |
672 | ### Резервное Копирование Данных
673 |
674 | Резервное копирование Redis это просто копирование снимка базы данных Redis в любое место (Amazon S3, FTP, ...). По умолчанию, Redis сохраняет данные в файл `dump.rdb`. В любой момент времени вы можете скопировать этот файл куда угодно.
675 |
676 | Допускается одновременное отключение сохранения данных на диск и режима дозаписи в файл у master-узлов и возложение функции резервирования на slave-узлы. Данный шаг уменьшает нагрузку на master-узел и позволяет slave-узлам использовать более \linebreak агрессивные настройки сохранения данных, не снижая общую доступность системы.
677 |
678 | ### Масштабирование и Redis Cluster
679 |
680 | Репликация является первым инструментом, который используют при росте нагрузки на систему. Некоторые команды являются более медленными, чем другие (например `sort`), и перенапрвление таких запросов на slave-узлы позволяет сохранять высокую доступность системы.
681 |
682 | Помимо этого, по-настоящему масштабируемый Redis использует распределение ключей между несколькими узлами Redis (которые могут быть запущены в той же системе - как мы помним, Redis однопоточный). В настоящее время, это то, о чем приходится заботится нам - разработчикам (хотя ряд драйверов предоставляют алгоритмы консистентного \linebreak хеширования). (*Консистентное хеширование - способ создания распределенных хеш-таблиц, при котором вывод из строя одного или более серверов-хранилищ не приводит к необходимости полного переразмещения всех хранимых ключей и значений - прим. перев.*) Мы не можем рассмотреть в этой книге вопросы, касающиеся работы с данными при использовании горизонтального масштабирования. Тем более, вам скорее всего не придется беспокоится о подобных вещах, однако стоит быть в курсе независимо от того, какое решение вы используете.
683 |
684 | Хорошей новостью является то, что ведется работа над Redis Cluster. Это решение \linebreak предоставит не только возможность горизонтального масштабирования, включая \linebreak автоматическую балансировку нагрузки, но также решения в плане отказоустойчивости и высокой доступности.
685 |
686 | Высокая отказоустойчивость и масштабируемость может быть достигнута уже сегодня, но вам придется вложить некоторое количество времени и усилий в это. В конечном счете, Redis Cluster должен сделать эти вещи гораздо более простыми.
687 |
688 | ### В Этой Главе
689 |
690 | Учитывая количество проектов, уже использующих Redis, не может быть никаких \linebreak сомнений, что эта система является пригодной для практического использования в \linebreak настоящий момент. Однако, некоторые инструменты, особенно касающиеся поддержания безопасности и доступности, все еще требуют доработки. Redis Cluster, который мы надеемся увидеть в ближайшее время, должен помочь решить некоторые из существующих проблем администрирования.
691 |
692 | \clearpage
693 |
694 | ## Заключение
695 |
696 | Redis использует множество способов облегчить наше взаимодействие с данными. Эта система убирает большую часть сложности и абстракции, присущих другим системам. Во многих случаях это делает Redis неудачным выбором. Но в других случаях может показаться, что Redis был написан для решения именно вашей проблемы.
697 |
698 | В конечном итоге мы вернулись к тому, о чем я говорил в самом начале: Redis прост в освоении. Когда вокруг так много новых технологий, не просто понять, на изучение какой стоит потратить время. Когда вы увидите реальные преимущества, которые Redis может предложить, я уверен, что у вас не останется вопросов о том, уделять ли время на изучение Redis.
699 |
--------------------------------------------------------------------------------
/ru/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akandratovich/the-little-redis-book/13a2f0979fffcb03c43bf8945539b2e9036a43c8/ru/title.png
--------------------------------------------------------------------------------
/ru/title.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akandratovich/the-little-redis-book/13a2f0979fffcb03c43bf8945539b2e9036a43c8/ru/title.psd
--------------------------------------------------------------------------------
/ru/xetex.template:
--------------------------------------------------------------------------------
1 | \documentclass{book}
2 | \usepackage[dvipsnames,usenames]{color}
3 | \usepackage{fullpage}
4 | \usepackage{changepage}
5 | \usepackage{fontspec,xltxtra,xunicode}
6 | \usepackage{setspace}
7 | \usepackage{hyperref}
8 | \setmainfont{$mainfont$}
9 | \setsansfont{$sansfont$}
10 | \setmonofont{$monofont$}
11 | \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase}
12 |
13 | \setlength{\parindent}{0pt}
14 | \setlength{\parskip}{12pt plus 2pt minus 1pt}
15 | \linespread{1.2}
16 |
17 |
18 | $if(fancy-enums)$
19 | \usepackage{enumerate}
20 | $endif$
21 | \setcounter{secnumdepth}{-1}
22 |
23 | \usepackage{hyperref}
24 | \hypersetup{
25 | colorlinks=true,%
26 | citecolor=YellowOrange,%
27 | filecolor=YellowOrange,%
28 | linkcolor=YellowOrange,%
29 | urlcolor=YellowOrange
30 | }
31 |
32 | \usepackage[compact]{titlesec}
33 | \titlespacing{\section}{0pt}{*0}{*-2}
34 | \titlespacing{\subsection}{0pt}{*0}{*-2}
35 | \titlespacing{\subsubsection}{0pt}{*1}{*-2}
36 |
37 | \begin{document}
38 | $body$
39 | \end{document}
40 |
--------------------------------------------------------------------------------
/template/xetex.template:
--------------------------------------------------------------------------------
1 | \documentclass{book}
2 | \usepackage[dvipsnames,usenames]{color}
3 | \usepackage{fullpage}
4 | \usepackage{changepage}
5 | \usepackage{fontspec,xltxtra,xunicode}
6 | \setmainfont{$mainfont$}
7 | \setsansfont{$sansfont$}
8 | \setmonofont{$monofont$}
9 | \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase}
10 |
11 | \setlength{\parindent}{0pt}
12 | \setlength{\parskip}{12pt plus 2pt minus 1pt}
13 | \linespread{1.2}
14 |
15 |
16 | $if(fancy-enums)$
17 | \usepackage{enumerate}
18 | $endif$
19 | \setcounter{secnumdepth}{-1}
20 |
21 | \usepackage{hyperref}
22 | \hypersetup{
23 | colorlinks=true,%
24 | citecolor=YellowOrange,%
25 | filecolor=YellowOrange,%
26 | linkcolor=YellowOrange,%
27 | urlcolor=YellowOrange
28 | }
29 |
30 | \usepackage[compact]{titlesec}
31 | \titlespacing{\section}{0pt}{*0}{*-2}
32 | \titlespacing{\subsection}{0pt}{*0}{*-2}
33 | \titlespacing{\subsubsection}{0pt}{*1}{*-2}
34 |
35 | \begin{document}
36 | $body$
37 | \end{document}
--------------------------------------------------------------------------------