NoSQLFan上看到的一篇Redis教程,原文链接:http://labs.alcacoop.it/doku.php?id=articles:redis_land
自己随便翻译了下。
Short summary of an adventurous journey in the NOSQL world with Redis.
The legs of our journey
As every other journey, the ours too is made of legs, so let’s write them down before going on:
- Redis? What is it?
- Available datatypes
- Where’s my tables?
- A simple use case
- Back home
Leg 1: Redis? What is it?
Leg 1: Redis? 是什么?
To make a long story short, Redis is a sort of key-value database on steroids. Why steroids? Simply because Redis is extremely fast (all data are loaded in memory and saved on disk, if desired) and feature rich (it supports various datatypes along with many complex operations on them).
Redis是一个key-value数据库 on sterodis.Why steroids?简单的来说Redis是非常的快(如果可以的话,所有数据都被加载到内存中,保存在磁盘上)而且有很多的特点(支持多种独立的数据类型,和在类型上的复杂操作)。
An important peculiarity of Redis is the fact that it’s not a database in the common meaning of the word; it is a database in the sense that it stores and keeps data for you, but it doesn’t provide any SQL dialect to play with them (as relational databases do). Don’t be afraid, Redis is not a store-and-lose database, it just doesn’t provide our beloved SQL and all its amenities, but it has a robust and tested protocol that lets you interact with it.
Redis的一个重要的特性是它不是一般定义的数据库;它为你存储数据,但是不支持SQL操作(这点和关系型数据库不同)。不过不要害怕,Redis并不是一个会丢失数据的数据库,它只是不支持SQL,但是它有其他的操作方式。
In Redis you’ll not deal with tables, selects, joins, views; there’re no integer or varchar fields and so on. You have to play with a very low level dataset structure and with more primitive datatypes.
在Redis中你不需要面对表、查询、关联、视图;没有数字或者字符类型等等。你不得不面对更加低级别的数据结构和更多的原始的数据类型。
Leg 2: Available datatypes
Leg 2: 有效的数据类型
Let’s give a more deep look at how this odd database work. As seen in the previous leg, Redis is based on a key-value store paradigm and we’ll focus for the moment on its "key" part.
让我们来深入的看下这个古怪的数据库是如何工作的。在上面一章讲到,Redis是基于key-value存储的数据库,我们关注下这个”key”部分。
Keys are simply strings (officially not binary safe. UPDATE: now, key strings are binary safe.) such as "username" or "password". You can’t use space in keys, but alphanumeric values such as ".", ":", "_" (and so on) work fine, so keys like "user_name", "user:123:age", "user:123:username" are perfectly legal ones.
Keys就是简单的字符串(官方说这个不是二进制安全的。更新:现在,Key字符串是二进制安全的了)比如说”username”或者”password”。你不能在keys中使用空格,但是字符例如”.”、”:”、”_”(等等)是可以的,所以像 ”user_name” 、 ”user:123:age”、”user:123:username”是完全合法的。
We must put a lot of attention to keys because, unlike field names in the RDBMS world, they are fully engaged in the game. As we’ll see in the following sub-legs, Redis doesn’t knows about tables, so simple tasks such as "SELECT username from users WHERE user_id=123;" have to be approached in a different way. As an example, to obtain in Redis the same result of the previous SQL query, we’ll have to get the value of the key "user:123:username", so, as you can see, keys could bring "information" (such as user ids) with them and play a very different role than the field names of an SQL table.
我们必须更多的关注key,因为和关系型数据库中的字段名称不同,Redis不识别表,所以简单的查询,比如” SELECT username from users WHERE user_id=123;”不得不用另外一种途径来实现。在Redis中要获取和这个SQL相同的结果的话,我们就需要获取key”user:123:username”的值,你可以看到keys可以带一些信息的(比如”user”),这个和关系型数据库中的字段名字是完全不同的。
Now that things about keys should be more clear, let’s take a tour of the available datatypes.
现在关于key我们比较清楚了,让我们来看下有效的数据类型吧。
Strings
Redis strings are the most basic available datatype; they are normal, binary-safe strings of maximum dimension of 1 Gb.
Redis strings是最基本有效的数据类型,他们是普通的、二进制安全的字符串,最大长度是1Gb。
You can set a key to a string value using the SET command and get it using GET. If you need to store a numeric value, such as counter, you can safely save it on a string and use INCR or DECR to increment or decrement it.
可以使用SET命令设置key的字符串值或者用GET命令或者它的值。如果你需要存储一个数字,比如要计数,任然可以保存到字符串中,然后使用INCR或者DECR来增加或者减少它。
Lists
Redis lists are collections of Redis strings sorted by insertion order. You can think of a list as a chain, you can add a new link on the left extremum (the head) or on the right (the tail); you can also add a link in the center, but you have to broke another link before.
Redis lists 是Redis字符串的集合,它按照插入的顺序储存。你可以把list看成是个链,你可以在它的最左边(头)或者最右边(尾)增加新的链接。你也可以在中间增加,不过你需要先把原来的list断开成2个。
You can add elements to a list using the LPUSH and RPUSH commands (L for left and R for right), get an element (removing it) with LPOP and RPOP and get a range of elements (without removing any) with LRANGE. You can also add an element on a given point with LSET, but, as for the chain example, this operation is much more slower than a simple push.
你可以用LPUSH和RPUSH命令来增加元素到list中,用LPOP或者RPOP来获取一个元素,或者用LRANGE来获取一段范围的元素。你可以可以用LSET来在给定的一个点增加元素,但是这个操作将会比简单的PUSH操作慢很多。
Hashes
At the time of writing, hashes are the newest datatype, implemented few days ago, it will be released shortly in the coming release. Hashes are a relatively long waited feature that helps storing data in a more coherent and neat way. Hashes implements a sort of key-value paradigm inside a key, so, for example an hash "user" may cointain some different fields to wich correspond different values, just like hashes do in programming languages such as ruby, javascript, etc…
Hashs是几天前才出来的新类型,很快会在后续的正式版中出现。这个新的特性将帮助更加有条理和整洁的方式保存数据。Hashes是将key-value分类存储来实现的,所以一个”user”的hash可能对应很多不同的值,就像其他编程语言中的hash一样,比如ruby,javascript等等。
Sets
Redis sets are just like the homonymous mathematical structure, collection of distinct objects; in this particular case the objects are Redis strings. As you could argue, sets are different from lists for two main reasons: elements are unordered and they are all distinct, you can’t have two equal elements.
Redis sets就像是homonymous mathematical structure,不同值的集合。它是由Redis字符串组成的。Sets和lists的主要区别有2点:元素是无序的和他们都是唯一的,你不可能得到2个一样的元素。
You can add an element to a set with the command SADD, remove an element with SREM, get (and remove) a random element with SPOP and perform the set operations of union, intersection and difference with SUNION, SINTER and SDIFF respectively.
你可以用命令SADD添加元素,或者用SREM来删除元素,或者用SPOP来随机返回并删除名称为key的set中一个元素,你还可以用SUNION,SINTER和SIDFF命令来对set进行集合、交集、差集的操作。
Ordered sets
Redis ordered sets are quite like normal sets, with the exception that every set element has an associated weight used to take this element in order with others.
Redis ordered sets和普通的set十分相似,只是每个set中的元素都被分配了一个权重用来排序。
You can work with sorted sets as with normal ones, what changes is just the names of the commands: ZADD to add an element, ZREM to remove it and so on. Two different commands are ZINCR and ZSCORE, the former is used to increment the score of an element and the latter to get its score.
你可以和操作set一样来操作ordered sets,唯一不同的只是命令的名字变了:ZADD增加元素,ZREM移除元素等等。2个不同的命令是ZINCR和ZSCORE,前者用来增加元素的积分,后者用来获取指定key的排序sets集合中成员的积分。
Leg 3: Where are my tables?
Leg3: 我的表在哪?
So, working with Redis data is really different from what we’re used to with SQL tables, you haven’t a language to query the server, you just have some commands to manipulate the keys stored in the database. Commands are datatype-sensitive, you can’t use a set command on a list, otherwise you’ll get an error. Commands could be issued via the redis-cli or using one of the many wrappers for your preferred programming language. In the following, we’ll be agnostic about the way you’ll query Redis, we’ll just write the commands and you’ll issue them as you prefer.
对Redis 数据操作和我们操作SQL表有很多的不同,你无法在server上使用语言来查询。,你只有一些命令来操作存储在数据库中的keys。命令是数据类型敏感的,你不能对list使用set的命令,这样会导致错误。命令可以通过redis-cli或者你喜欢的语言的封装接口来执行。接下来我们只会写出命令,你可以通过你喜欢的方式来执行这些命令。
Let’s think about a simple SQL table where we could save users for some application:
让我们考虑一个简单的SQL表,我们可以通过一些程序来保存用户信息到这个包里面:
id |
username |
password |
name |
surname |
1 |
user1 |
pass1 |
Bob |
Smith |
2 |
user2 |
pass2 |
Mario |
Rossi |
Storing the data
We want now to save the same data in Redis, so we have to design our database to fit this situation. Maybe, it’s better to think about the problem from an application-centric point of view; in the SQL version, our application would get users’ data issuing a SELECT statement having as clause the users’ id; in other words, we need a way to distinguish between different data entries. Additionally, we need to get user’s data with the sole knowledge of an unique identifier. This situation could be solved solved if we use the user id as a part of the redis key, so the records of the previous table would translate as
现在我们想保存相同的数据到Redis里面,所以我们需要设计我们的数据库来适合这个场景。当然我们如果能从程序的角度来考虑这个问题那更好。在使用SQL的时候,程序通过对表users的ID字段来查询得到想要的用户数据。换个说法,就是说我们需要一个方法来区别不同的数据入口。也就是说我们需要一个专有的唯一标示来获取数据。在这个场景下我们可以使用user di作为redis key的一部分来解决问题,所以上面的数据可以转换成:
Key |
Value |
user:1:username |
user1 |
user:1:password |
pass1 |
user:1:name |
Bob |
user:1:surname |
Smith |
user:2:username |
user2 |
user:2:password |
pass2 |
user:2:name |
Mario |
user:2:surname |
Rossi |
Well, given the id of an user, we could now get all its data reading the keys user:id:username, user:id:password, user:id:name and user:id:surname.
然后只要有了用户的id,我们就可以获取所有这个key的数据。
Loggin’ in users
Our data are suitable for a login process, so we have to provide a way to gets the user’s id, given the username; in other words, we have to establish a relation between usernames and user ids. This can be done if whe add another redis key to our data design: user:username:id.
我们的数据还要适合登录的流程,所以我们不得不提供给用户名,获取用户id的方法。也就是我们必须建立用户名和用户ID的关联。我们可以增加另外一个redis key来解决这个问题。
Key |
Value |
user:user1:id |
1 |
user:user2:id |
2 |
So, if Mario Rossi will try to login to our application, we can get his id knowing its username and so we’ll get all its data.
所以,如果Mario Rossi登录我们系统的时候,我们可以通过用户名获取他的ID,然后就可以获取所有他的数据了。
Primary keys?
Another problem is the uniqueness of our ids. In the SQL world we say, for example, "id int primary key auto_increment", now we have to implement a similar behavior to ensure that we have a different id for each user. As in the previous case, Redis has a solution: we can simply create another key "user:next_id" and use it as a counter that we manipulate via the INCR command whenever we add a new user.
另外一个问题是id的唯一性。在SQL里面我们只需要写"id int primary key auto_increment"就可以了,但是现在我们要在Redis里面实现这个功能来确保每个用户有不同的id。在前面的例子里面,Redis有个解决方法:我们可以创建另外一个key “user:next_id” ,当我们添加用户的时候,就使用INCR命令来增加这个值。
SELECT * FROM users;
The next problem is to get the user list. We could think that what we’ve already done is enough to get this list: we could get the current value "user:next_id" and get users’ data from 0 to this value in one or more steps. Well, now let’s think about an user that has cancelled it’s subscription (our next problem), when we’ll traverse all the integers from 0 to user:next_id we’ll find its old id that now should not has any data.
接下来的问题是如何获取用户列表。先让我们看看我们如何可以获取这个清单:我们可以从 “user:next_id”获取当前的用户数,然后从0到这个值一个个读取用户信息。然后让我们想想如果有用户取消了注册(下个问题),我们会发现无法查询到这个值对应的用户信息。
Althought this is not a serious problem, we wouldn’t waste our time trying to get the data of users that don’t exist anymore, so we’ll create a new key "user:list" of list (or set) type in wich we’ll add all new user ids, removing them when needed. We prefer using a list because this give us the possibility to do a sort of "pagination" (maybe you’re thinking about "LIMIT"?) using the LRANGE command.
虽然这不是一个严重的问题,但是我们不想浪费我们的时间去读取一个不存在的用户。所以我们需要创建一个新的list(或者set)类型的key “user:list” ,我们会添加新的用户id并在需要的时候删除它。我们推荐使用list因为这样我们可以用LRANGE命令来对数据做分页。
Deleting users
The last problem is a matter of "data integrity"; what would happen when we delete a user? We sould remove anything that references to it. In other words, we should delete all the keys user:id:*, user:username:id and the id in "user:list".
上一个问题是”数据的健全性”。当我们删除数据的时候会发生什么?我们需要移除所有的相关数据。也就是说我们必须删除所有的key user:id:*, user:username:id 和 "user:list"中的id。
Leg 4: A simple use case
Leg4: 一个简单的案例
As an example of what we learned in this journey, let’s try to design an application that will work as a virtual library that let us group our books by topics. This example will be slightly more complex than the user’s table, but we’ll teach us how to handle relations with Redis.
接下来我们用我们学到的东西来实现一个简单的例子。设计一个虚拟图书馆的程序。我们可以通过书的标题来分组。这个例子比用户表的例子要复杂,但是它可以更好的让我们掌握用Redis创建关联。
In our application, we should be able to collect our books, storing informations such as title, author(s), topic(s), pages, price, ISBN and a description. Obviously, we could have a book with more than one author and that cover more than one topic at the same time (for example, a book that is about programming ad ruby). Additionally, one author could have written more than one book and a topic could be dealed with in more than one book. From what we’ve written it arises clearly that there’re a many-to-many relation between authors and books and between topics and books.
在这个程序里面,我们需要能收集并存储书的信息,例如:书名、作者、主题、页数、价格、ISBN和描述。很明显,一本书是可以有多个作者,并且一个本书可以有多个主题(比如一本的主题可以使 程序 和 ruby )。一个作者可以写多余一本的书;一个主题可以在多余一本的书上声明。也就是说 书和作者 主题和书 都是多对多的关系。
The SQL case
First of all, let’s try to design the SQL tables (with some data) that model this situation, in order to see how works the translation in the Redis idiom:
首先尝试设计SQL表(带一些数据),来看下在Redis中事物应该如何工作。
Books
id |
title |
pages |
price |
isbn |
description |
1 |
Programming Ruby: The Pragmatic Programmers’ Guide |
829 |
$26 |
0974514055 |
The bible of the ruby programming language |
2 |
Erlang Programming |
496 |
$42 |
0596518188 |
An interesting introduction to erlang |
Authors
id |
name |
surname |
1 |
Dave |
Thomas |
2 |
Chad |
Fowler |
3 |
Andy |
Hunt |
4 |
Francesco |
Cesarini |
5 |
Simon |
Thompson |
Topics
id |
name |
description |
1 |
programming |
Books about programming |
2 |
ruby |
Books about ruby |
3 |
erlang |
Books about erlang |
Books-Authors
book_id |
author_id |
1 |
1 |
1 |
2 |
1 |
3 |
2 |
4 |
2 |
5 |
Books-Topics
book_id |
topic_id |
1 |
1 |
1 |
2 |
2 |
1 |
2 |
3 |
The Redis case
It should be already clear that the trhee tables Books, Authors and Topics are not a problem; we’ve already learned how to save this data in Redis in the previous leg (see the next image). The problem arises when considering the link tables Books-Authors and Books-Topics that implement the two many-to-many relations. How to implement them? Let’s think about topics only, the problem for authors is merely a twin (with a different name) of the former and we’ll use the same strategy.
首先要确认的是 书、作者和主题这3个表是肯定要有的。我们已经在之前学过在Redis中如何保存数据了。然后让我们考虑书-作者和书-主题这2个多对多的实现方法。让我们先考虑主题,因为作者和主题的问题是一样的,我们可以采用相同的策略来解决。
For every book we have to know the topics that belongs to it and, in the same way, for every topic we have to know every book that deals with it. In other words, for each book we need a collection of the related topic ids and for each topic a collection of the related book ids. This is a problem where sets fit perfectly! We’re going to create two sets, book:id:topic and topic:id:books, in the former we’ll save the topic ids that match the book and in the latter we’ll save the ids of the books that match this topic. For example, referring to the data shown in the previous sub-leg, the book "Programming Erlang" (book id 2) will have a key book:2:topics of set type, with elements (1, 3); while the topic programming will have a set topic:1:books with elements (1, 2).
对于每本书,我们需要知道它的所有主题,同样对于每个主题我们需要知道和它相关的每本书。换句话说,对于每本书,我们需要一个主题ID的集合。对于每个主题,我们需要相关的图书ID的集合。这个问题可以用sets来解决!我们可以创建2个sets,book:id:topic 和 topic:id:books,第一个sets用来保存和图书匹配的主题ID,第二个sets用来保存和主题匹配的图书ID。比如在上一节提到的图书“Programming Erlang”(ID是2)就会有key book:2:topics的set类型,对应的值是(1,3); 主题 programming 会有个 topic:1:books 的set,对应的值是(1,2)。
As said early, the same works for authors, so we could say that a SQL’s many-to-many relation translates in Redis as two sets. This is really useful, because it give us the ability to obtain an important feature for free: we could easily find a book that covers more than one topic doing a set intersection between all topic:id:books sets related to the topics we are interested on. So, for example, the intersection between topic:1:books (books about programming) and topic:2:books (books about ruby) will return the set (1), i.e. only the book "Programming Ruby" with id 1.
之前就说过,对于作者的处理是一样的,所以在Redis中会用2个sets来处理SQL中的多对多的关系。这个十分有用,因为它可以让我们可以随时获取想要的重要信息:我们可以十分轻松的找到包含多个主题的书(通过对相关的主题所对应的topic:id:books做差集),比如对topic:1:books (和programming相关的书)和 topic:2:books (和ruby相关的书)做差集,就可以得到"Programming Ruby"的结果。
A special care should be used whenever we remove something from our database. As shown in the previous figure, for example, topics have a reference to books and books have a reference to topics, so, what to remove? Well, surely we should remove every book:id:* keys, but before doing this we have to traverse all topics to remove the book id from the set topic:id:books. Additionally, we should remove the book id from the book:list list. If we want to remove a topic, we have to procede in a very similar way: before removing each topic:id:* key we have to traverse all books referenced by the topic we want to delete and remove the topic id from the set book:id:topics. The same apply for authors.
有个特殊的例子需要考虑下,就是如何删除信息。因为主题和书有关联,书和主题也有关联,所以如何删除?当然我们肯定需要删除所有的book:id:* keys,但是在做这个之前我们不得不遍历所有的主题,从topic:id:books中删除图书ID。还有我们要从book:list 中删除图书ID。如果要删除注意,我们需要做相同的操作:在删除每个topic:id:* key之前要遍历所有和该主题相关的书籍并且从book:id:topics中删除主题ID。对于删除作者的步骤也一样。
We like fun and programming, so to lear by doing consider taking a look to the companion code of our virtual library. It is implemented in ruby, using ezmobius’ redis-rb. Both the code and the look’n’feel are very rough, so feel free to make a pull request if you have improvements.
Leg 5: Back home
Our journey is reaching its end, we’re quite back home and so now it’s time to pack our luggages and buy some souvenirs.
In the luggage we have to put all we’ve learned about Redis: datatypes, commands, oddities and so on.
The souvenirs are the three patterns we’ve learned:
- handling the unique id problem with a string key and the INCR command;
- handling a user login via an user:username:id type key;
- handling many-to-many relations with sets.
Our journey is completed and now we’re definitely at home; we just hope to don’t suffer of post-holidays stress, but in any case we’ve the right cure: having fun coding free software!
References
Domenico Delle Side (domenico.delleside AT alcacoop.it), AlcaCoop (info AT alcacoop.it)
相关推荐
Redis 使用教程详解 Redis 是一个高性能的 NoSQL 键值存储数据库,广泛应用于缓存、任务列表、网站访问统计数据、过期处理、应用排行榜、分布式集群架构中的 session 分离等领域。下面是 Redis 的详细使用教程。 ...
Redis,全称Remote Dictionary Server,是一款开源的、高性能的键值对存储系统,常用于数据库、缓存和消息中间件等场景。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合,以及更复杂的地理空间索引等。...
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/...
Redis基础教程及开发案例及解析Redis基础教程及开发案例及解析Redis基础教程及开发案例及解析Redis基础教程及开发案例及解析Redis基础教程及开发案例及解析Redis基础教程及开发案例及解析Redis基础教程及开发案例及...
Redis 是一个开源的、高性能的键值存储系统,主要用于存储数据并提供高速访问。它支持多种数据结构,如字符串(STRING)、列表(LIST)、哈希(HASH)、集合(SET)和有序集合(ZSET),这使得Redis在各种场景下都有...
Spring Boot 框架集成 Redis 教程 在现代 Web 应用开发中,Spring Boot 作为一款快速构建应用的框架,提供了丰富的自动化配置和便捷的集成能力。而 Redis,作为一个高性能的键值数据存储系统,常被用作缓存、消息...
在本教程中,我们将深入探讨Redis的安装、配置以及相关软件的使用。 一、Redis安装 1. Windows安装:对于Windows用户,可以通过下载Redis的Windows版本,如msi安装包,双击进行安装。安装完成后,在命令行中输入`...
### Redis安装配置详细教程 #### 一、Redis简介与特性 Redis(Remote Dictionary Server),即远程字典服务,是一款开源的、使用ANSI C语言编写的高性能键值(Key-Value)存储系统。它支持网络交互,可在内存中运行...
本教程主要涵盖Redis的各种数据类型及其应用、分布式锁的实现、主从复制、持久化策略、哨兵模式以及如何应对Redis可能出现的雪崩和穿透问题。 1. Redis数据类型: - String:基本类型,支持设置、获取、自增、自减...
在本教程中,我们将深入探讨如何使用Redis这个高性能的键值存储系统来实现一个点评项目的实际应用场景。Redis以其出色的数据结构支持、高速读写性能以及丰富的功能,被广泛应用于缓存、消息队列、计数器等多个领域。...
本教程主要针对集群的入门,适用于想要了解如何设置、测试和操作Redis集群的用户。 首先,Redis集群并不支持涉及多键的命令,如`MSET`或`KEYS`等,因为在高并发环境中,这些操作可能导致数据移动,降低性能并产生不...
redis
Redis是一款高性能的键值数据库,常用于数据缓存、消息队列、计数系统等多个场景。本教程将通过视频和代码案例的方式,帮助你深入理解并掌握Redis的使用。 首先,Redis的核心特性包括其内存存储模式,使得数据读取...
高级分布式数据库教程,nosql,mongodb,redis。非常好的分布式教程!
Redis,全称Remote Dictionary Server,是一款开源的、高性能的键值对存储系统,常用于数据库、缓存和消息中间件等场景。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合,这使得Redis在处理复杂的数据...
redis.chm
整理尚硅谷redis教程脑图xmind版(全),包含备注等,非pdf版
"Redis 配置安装详细教程" Redis 是一个开源、基于内存的数据结构存储系统,可以用作数据库、消息代理、缓存层等。下面是 Redis 配置安装详细教程。 安装 Redis 在 Windows 操作系统中安装 Redis 非常简单。首先...