zscore的意义_z-score是什么意思中文
2024-12-14 05:21 - 立有生活网
如何跳出SQL这个坑
下面我们深入看下这个奇怪的数据库是如何工作的。如上所见,Redis是基于key-value范式存储数据,所以先来重点看下"key"的概念。以下为原文:
zscore的意义_z-score是什么意思中文
zscore的意义_z-score是什么意思中文
zscore的意义_z-score是什么意思中文
探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Ces23arini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
想像一下,一个简单的SQL数据库表,像一些应用中会用到的保存用户数据的表:以下为原文:
探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
Books-Topics主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
以下为原文:
探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,对于Redis的探索到一段落,现在回头看看我们的旅行包里收获了哪些精彩。一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idn在Redis中如何保证id值的性呢。在SQL中,可以通过"id int primary key auto_increment"定义自增主键来实现,现在我们也需要一种类似的方式为每个用户生成一个不同的id。根据前面可用的数据类型中提到的数字数据,Redis中的方案是这样的:创建一个key"user:next_id",并把它作为计数器,每当要添加新用户时,就对key"user:next_id"执行INCR命令。amesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
Strings探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供
任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
key本质上就是简单的字符串,诸如"username"、"password"等。在定义key时,除了不能使用空格,你可以随意的使用普通的字
符、数字等,像".",":","_"等在定义key时都能正常使用,所以像"user_name", "user:123:age",
"user:123:username"都是不错的key的定义方式。
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有
table的概念,所以像"SELECT username from users WHERE
user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key
"user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user
ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left,
R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元
素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对
于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的
hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如
你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。
Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他
你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis
server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在
SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信
息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
在Redis中如何保证id值的性呢。在SQL中,可以通过"id int primary key
auto_increment"定义自增主键来实现,现在我们也需要一种类似的方式为每个用户生成一个不同的id。根据前面可用的数据类型中提到的数字数
key"user:next_id"执行INCR命令。
SELECT FROM users;
下一个面临的问题是查询用户列表。也许你认为我们上面的数据存储已经足以查询出用户列表:可以先获得"user:next_id"的当前值
counter,然后通过一步或多步遍历0到counter获得用户数据。但如果某个用户从系统中删除(下面会讲到删除作),而我们会遍历0到
counter中的所有id,这时就会有些id查询不到任何数据。
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或
set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE
命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr,
ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编
程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-
Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关
联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要
一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两
个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的
books'id列表。以前面SQL场景中的数据为例,图书"Programming
Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;
而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:23id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息
的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对集
合"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何
作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key
为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如
果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key
为"books:id:topics"的topic
id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
如何跳出SQL这个坑
-book:id:title以下为原文:
Books探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
Books-Authors探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析存储数据,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
删除用户以下为原文:
探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surnaListe Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
,从而获得id=1的图书:programming Ruby。探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有table的概念,所以像"SELECT username from users WHERE user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key "user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left, R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
SELECT FROM users;
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr, ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的books'id列表。以前面SQL场景中的数据为例,图书"Programming Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的,从而获得id=1的图书:programming Ruby。
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key为"books:id:topics"的topic id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及1programmingBooks about programming作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
译者注:本文是翻译而来,个人感觉是通过与SQL做对比,来强调Redis的不同,进而让读者能跳出SQL的枷锁,以实现对Redis的理解。
在真实场景中,Redis未必如文中描述的方式使用,我自己对Redis的使用场景也在学习中,欢迎指导。
如何跳出SQL这个坑
探索之三:Where are my tables?探索之一:Redis? What is it?
简而言之,Redis是一种强大的key-value数据库,之所以强大有两点:响应速度快(所以数据内存存储,只在必要时写入磁盘),特性丰富(支持多种数据类型,以及各类型上的复杂作)。
事实上,Redis的一个重要特性就是它并非通常意义上的数据库,虽然称之为数据库是因为它可以为你存储和维护数据,但它并不像关系数据库那样提供
任何的SQL方言。不过不用担心,Redis并不是吞噬数据的黑洞,它只是不支持SQL及相关功能,但却提供了稳健的协议用于与之交互。
在Redis中,没有数据表的概念,也无须关心select、join、view等作或功能,同时也不提供类似于int或varchar的数据字段。你面对的将是相对原始的数据及数据类型。
探索之二:Available datatypes
key本质上就是简单的字符串,诸如"username"、"password"等。在定义key时,除了不能使用空格,你可以随意的使用普通的字
符、数字等,像".",":","_"等在定义key时都能正常使用,所以像"user_name", "user:123:age",
"user:123:username"都是不错的key的定义方式。
不像RDBMS中的字段名称,这里的key是Redis中的重要组成部分,所以我们必须在处理key时多加小心。在下面的讲述中,Redis并没有
table的概念,所以像"SELECT username from users WHERE
user_id=123;"这种简单任务都只能换种方式实现,为了达到这种目的,在Redis上,一种方式是通过key
"user:123:username"来获取结果value。如你所见,key的定义中携带了神秘信息(像user
ids)。在Redis中,key的重要性可见一斑。(其他key-value数据库中key的地位也是如此。)
现在你应该对key有了清楚的了解,下面带你进入可用的数据类型的神奇世界。
String是Redis中最基本的数据类型,它就是普通的二进制安全的字符串,支持数据长度为1Gb。
可以通过SET命令给一个key设置String类型的数据,并可通过GET命令根据key取得结果。如果你想存储数字信息,像计数器,你也可以把它存储到String数据中,并可使用INCR和DECR对之做自增和自减作。
List是string数据的,其中各数据项按插入顺序排列。你可以把list当作一个锁链(chain),所以可以在锁链最左边(链头)或最右边(链尾)添加一个新的链结(link);当然也可以加在锁链中间,但却要打断某个链结。
可能通过LPUSH和RPUSH命令给list添加数据(L:left,
R:right),通过LPOP或RPOP命令弹出元素(同时删除该元素),也可以通过LRANGE获取指定范围的元素(仅返回数据,不会删除任何元
素)。另外也可通过LSET在指定位置添加元素,但通常这种作比简单的LPUSH或RPUSH要慢很多。
Hashes
Hashes以简洁的方式存储关系更为紧密的数据。Hashes为每个存储的key实现一个内置的key-value对来存储数据,例如对
于"user"这个key,它的值可以是多个字段以及与每个字符相应的值对组成数据集。如果你熟悉像ruby或jascript等编程语言,这里的
hashes与那些语言中的hash概念大同小异。
Sets
Sets和它在数学上的同名概念""意义相同,是包含不重复元素的。在Redis中,这些对象变成了Redis里的String类型。正如
你想,sets与lists不同的地方在于:sets中的元素是无序的,并且不能重复,你不能在sets中放进两个相同的数据。
可以通过SADD往set中添加数据,SREM删除数据,或者通过SPOP返回并删除此数据。此外还可以通过SUNION, SINTER, SDIFF命令分别实现上的"并集", "交集", "集"作。
Ordered sets
Ordered sets与sets类似,不同地方在于Ordered set中的每个元素都有一个权重,用于与其他元素比较并排序。
使用Redis与我们之前使用的SQL数据表完全不同,没有语言支持你在上查询数据,这里一些命令帮你作数据库中的keys值。
Redis中的命令是数据类型敏感型的,也就是说你不能在list上执行set命令,否则你将得到一个执行错误的提示。可以通过redis-cli或其他
你使用的编程语言中的接口给Redis server发送命令。在下面的示例中,我们只强调命令本身,而不关注你通过哪种方式提交给Redis
server。
idusernamepasswordnamesurname
1user1pass1BobSmith
2user2pass2MarioRossi
如我们想把上面的数据存储到Redis中,你会如何在Redis中设计数据库方案呢?也许以应用的视觉来看会更直观一些。使用SQL,我们在
SELECT中通过指定用户id来获得一个用户信息,换句话说就是需要有用于区分不同数据实体的方式,所以我们可以通过一个的标识来标识和获取用户信
息。所以如果在redis的key中加入用户的id信息,那么我们的查询需求就解决了,在redis中,数据被存储成如下形式:
KeyValue
user:1:usernameuser1
user:1:passwordpass1
user:1:nameBob
user:1:surname Smith
user:2:usernameuser2
user:2:passwordpass2
user:2:name Mario
user:2:surname Rossi
那么,给出任一个用户id,我们就可以通过key user:id:username,user:id:password,user:id:name,user:id:surname的形式读出用户信息。
用户登录
上面的存储形式也能用于用户登录,但需要一种方式能根据username来查询用户的id。也就是说我们还需要在username和id之间建立联系。这可以通过添加另外一个redis key"user:username:id"来实现。
keyvalue
user:user1:id1
user:user2:id2
现在如果Mario Rossi想要登录进来,我们可以通过key"user:user2:id"先查出username,进而获得用户的所有信息。
主键
在Redis中如何保证id值的性呢。在SQL中,可以通过"id int primary key
auto_increment"定义自增主键来实现,现在我们也需要一种类似的方式为下一个面临的问题是查询用户列表。也许你认为我们上面的数据存储已经足以查询出用户列表:可以先获得"user:next_id"的当前值counter,然后通过一步或多步遍历0到counter获得用户数据。但如果某个用户从系统中删除(下面会讲到删除作),而我们会遍历0到counter中的所有id,这时就会有些id查询不到任何数据。每个用户生成一个不同的id。根据前面可用的数据类型中提到的数字数
key"user:next_id"执行INCR命令。
SELECT FROM users;
下一个面临的问题是查询用户列表。也许你认为我们上面的数据存储已经足以查询出用户列表:可以先获得"user:next_id"的当前值
counter,然后通过一步或多步遍历0到counter获得用户数据。但如果某个用户从系统中删除(下面会讲到删除作),而我们会遍历0到
counter中的所有id,这时就会有些id查询不到任何数据。
尽管这通常不是问题,但我们不想在不存在的用户数据上浪费时间,所以需要创建另外一个key"user:list",其value为list或
set类型,用于存储每一个新增的用户id,并在必要的时候从"user:list"中删除该id。我更倾向于使用list,因为它可能通过LRANGE
命令实现分页功能。
还有一个要面临的问题是"数据完整性",看看我们在删除用户时会发生什么吧。我们需要删除每一个对此用户的引用,也就是说,需要删除下面所有的key"user:id:","user:username:id",以及"user:list"中的用户id。
探索之四:A Simple use case
在应用中,我们需要收集图书,并存储他们的title,author(s), topic(s), pages, pr,
ISBN和description。显然有些图书的作者不止一个,并且它也许会涵盖不同的主题(例如一本书可以是编程主题,也可以是描述的ruby编
程)。另外一个作者可能写了很多本书,而一个主题必然会包含很多本书。可以看出,这里出现了作者和图书、主题和图书的多对多关系。
SQL场景
首先,我们尝试使用SQL数据表为此种场景建数据模型,以便于我们更直观的在Redis领域中模拟:
idtitlepagesprindescription
1Programming Ruby 829 $260974514055ruby programming language
2Erlang Programming496 $42 0596518188an introduction to erlang
Authors
idnamesurname
1DeThomas
2ChadFowler
3AndyHunt
4Francesco Cesarini
5Simon Thompson
Topics
idnamedescription
2ruby Books about ruby
3erlang Books about erlang
book_idauthor_id
11
12
13
24
25
book_idtopic_id
11
12
21
Redis场景
前面已经介绍了如何在Redis中存储数据,所以这里理解Books,Authors和Topics这三张表应该不成问题。但当面对Books-
Authors和Book-Topics这种表之间的多对多关联时,问题变得复杂起来。下面以Topic为例来看如何解决Book与Topic之间的关
联,一旦对这个关系清楚了,Book与Author之间的关系也就迎刃而解了。
对于每本book,我们需要知道它属于哪些topics;同样对于每个topic,也要处理它包含的每本book。换句话说,对每本book,需要
一个存储它所关联的topic的id列表,对于每个topic,同样需要一个存储它关联的book的id列表。这正是set大展身手的地方。我们将创建两
个sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,后者存储topic的
books'id列表。以前面SQL场景中的数据为例,图书"Programming
Erlang"(books表中的id为2),将有一个key为"book:2:topics",value为set类型且数据为(1,3)的数据信息;
而主题"programming"则会有一个key为"topic:1:books",值为(1,2)的数据集。
经过分析,就得出了Redis场景下的数据模型:
Authors
-author:id
-author:id:name
-author:id:surname
Sets
-author:id:books
-authorlist
-book:id
-book:id:pages
-book:id:pr
-book:id:in
-book:id:description
Sets
-books:id:authors -books:id:topics
-book:list
Topics
String
-topic:id
-topic:id:name
-topic:id:description
Sets
-topic:id:books
-topic:list
可以看出,在SQL中的多对多关联,在Redis中可以通过两个set来实现。你会发现这种实现相当有用,它给我们提供了一种可以自由获得其他信息
的能力:可以通过对所有感兴趣的"topic:id:books"中交集作,从而获得隶属于多个主题的图书。例如对集
合"topic:1:books"(programming主题)和"topic:2:books"(ruby主题)做交集,会得到只有一个元素(1)的
对于这种实现,你必须特别关注对数据的删除作。因为topics里有对books的引用,同样books里有对topics的引用,那删除如何
作?以删除books中的数据为例,首先想到的是要删除每个key为"book:id:"的数据,但执行此作前需要先遍历topics中的所有key
为"topic:id:books"的,并从中删除待删除图书的id,当然也要从books中key为"book:list"的列表中删除此id。如
果想删除一个topic,作也很类似:从topics中删除所有key为"topic:id:"信息之前,需要先遍历books中的key
为"books:id:topics"的topic
id集,并从中删除待删除topic的id,同时从"topic:list"列表中也删除此id。同样的作对于authors一样适用。
探索之五:Back home
我们学习了Redis中的数据类型及作命令,还有一些其他有趣的东西。 是不是还有几段记忆深刻的故事呢:
通过对String数据执行INCR命令解决自增主键问题
通过含义丰富的key:"user:username:id"处理用户登录场景
通过set实现数据间的多对多关联
c7h16的同分异构体(c7h16的同分异构体图)
大家好我是琪琪,c7h16的同分异构体,关于c7h16的同分异构体图很多人还不知道,那么现在让我们一起来看看吧! 1、书写同分异构体的注意事项:(烷烃的)CH3CH2CH2CH2CH2CH2CH3 庚烷(CH3)2CHCH2CH2CH2···
电压电流功率(欧姆定律三个公式)
电压电流功率计算公式是什么? 电流电压功率计算公式如下: 电压电流功率(欧姆定律三个公式) 电压电流功率(欧姆定律三个公式) 电压电流功率(欧姆定律三个公式) 1、电流公式 电流(I)=功率(···
三国志11韩遂 三国志11韩遂智力
三国志11的150个历史武将都有谁 廖立 凌 凌统 霊帝 刘巴 刘备 刘辟 刘表 刘禅 刘谌 三国志11-在野武将及未发现武将出现时间 三国志11韩遂 三国志11韩遂智力 三国志11韩遂 三国志11韩遂智力 六,21···