您的位置:首页 > 理论基础 > 数据结构算法

Redis各种数据结构介绍以及相应的业务场景应用

2015-09-09 21:15 681 查看
1.redis现存的数据结构

二进制安全的 字符串 string

二进制安全的 字符串列表 list of string

二进制安全的 字符串集合 set of string,换言之:它是一组无重复未排序的element。可以把它看成Ruby中的 hash–其key等于element,value都等于’true‘。

有序集合sorted set of string,类似于集合set,但其中每个元素都和一个浮点数score(评分)关联。element根据score排序。可以把它看成Ruby中的 hash–其key等于element,value等于score,但元素总是按score的顺序排列,无需额外的排序操作。

2.Redis 键的使用原则

Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值。

关于key的几条规则:

太长的键值不是个好主意,例如1024字节的键值就不是个好主意,不仅因为消耗内存,而且在数据中查找这类键值的计算成本很高。

太短的键值通常也不是好主意,如果你要用”u:1000:pwd”来代替”user:1000:password”,这没有什么问题,但后者更易阅读,并且由此增加的空间消耗相对于key object和value object本身来说很小。当然,没人阻止您一定要用更短的键值节省一丁点儿空间。

最好坚持一种模式。例如:”object-type:id:field”就是个不错的注意,像这样”user:1000:password”。我喜欢对多单词的字段名中加上一个点,就像这样:”comment:1234:reply.to”。

3.字符串类型

常用的操作命令有:set,get,incr,incrby以及getset等

set和get的例子:

$ redis-cli set mykey "my binary safe value"

OK

$ redis-cli get mykey

my binary safe value

字符是redis里最基本的类型,但是也可以实现一些有趣的操作

incr和incrby的例子:

$ redis-cli set counter 100

OK $ redis-cli incr counter

(integer) 101

$ redis-cli incr counter

(integer) 102

$ redis-cli incrby counter 10

(integer) 112

incr是原子操作,这也就意味着多个客户端对同一个key发出incr命令,也绝不会导致竞争的情况。

另一个比较有用途的命令是getset命令,意思是它为key设置新值返回旧值。

应用场景:你的系统每当有新用户访问时就用INCR命令操作一个Redis key。你希望每小时对这个信息收集一次。你就可以GETSET这个key并给其赋值0并读取原值。

4.列表类型

一般意义上讲,列表就是有序元素的序列:10,20,1,2,3就是一个列表。但用数组实现的List和用Linked List实现的List,在属性方面大不相同。

Redis lists基于Linked Lists实现。这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的。用LPUSH 命令在十个元素的list头部添加新元素,和在千万元素list头部添加新元素的速度相同。

那么,坏消息是什么?在数组实现的list中利用索引访问元素的速度极快,而同样的操作在linked list实现的list上没有那么快。

Redis Lists用linked list实现的原因是:对于数据库系统来说,至关重要的特性是:能非常快的在很大的列表上添加元素。另一个重要因素是,正如你将要看到的:Redis lists能在常数时间取得常数长度。

常用操作命令:LPUSH LPOP RPUSH LRANGE LLEN等

LPUSH 命令可向list的左边(头部)添加一个新元素,而RPUSH命令可向list的右边(尾部)添加一个新元素。最后LRANGE 命令可从list中取出一定范围的元素

$ redis-cli rpush messages "Hello how are you?"

OK

$ redis-cli rpush messages "Fine thanks. I‘m having fun with Redis"

OK

$ redis-cli rpush messages "I should look into this NOSQL thing ASAP"

OK

$ redis-cli lrange messages 0 2

1. Hello how are you?

2. Fine thanks. I‘m having fun with Redis

3. I should look into this NOSQL thing ASAP

注意LRANGE 带有两个索引,一定范围的第一个和最后一个元素。这两个索引都可以为负来告知Redis从尾部开始计数,因此-1表示最后一个元素,-2表示list中的倒数第二个元素,以此类推。

应用场景:list可被用来实现聊天系统。还可以作为不同进程间传递消息的队列。关键是,你可以每次都以原先添加的顺序访问数据。这不需要任何SQL ORDER BY 操作,将会非常快,也会很容易扩展到百万级别元素的规模。

例如在评级系统中,比如社会化新闻网站 reddit.com,你可以把每个新提交的链接添加到一个list,用LRANGE可简单的对结果分页。

在博客引擎实现中,你可为每篇日志设置一个list,在该list中推入进博客评论,等等。

5.集合

Redis集合是未排序的集合,其元素是二进制安全的字符串。SADD命令可以向集合添加一个新元素。和sets相关的操作也有许多,比如检测某个元素是否存在,以及实现交集,并集,差集等等

$ redis-cli sadd myset 1

(integer) 1

$ redis-cli sadd myset 2

(integer) 1

$ redis-cli sadd myset 3

(integer) 1

$ redis-cli smembers myset

1. 3

2. 1

3. 2

检测元素是否存在的命令是sismember key

例如:

$ redis-cli sismember myset 3

(integer) 1

$ redis-cli sismember myset 30

(integer) 0

应用场景:

适合表现对象之间的关系。可以用集合类型实现标签功能。

下面是一个简单的方案:对每个想加标签的对象,用一个标签ID集合与之关联,并且对每个已有的标签,一组对象ID与之关联。

例如假设我们的新闻ID 1000被加了三个标签tag 1,2,5和77,就可以设置下面两个集合:

$ redis-cli sadd news:1000:tags 1

(integer) 1

$ redis-cli sadd news:1000:tags 2

(integer) 1

$ redis-cli sadd news:1000:tags 5

(integer) 1

$ redis-cli sadd news:1000:tags 77

(integer) 1

$ redis-cli sadd tag:1:objects 1000

(integer) 1

$ redis-cli sadd tag:2:objects 1000

(integer) 1

$ redis-cli sadd tag:5:objects 1000

(integer) 1

$ redis-cli sadd tag:77:objects 1000

(integer) 1

要获取一个对象的所有标签,如此简单:

$ redis-cli smembers news:1000:tags

1. 5

2. 1

3. 77

4. 2

例如我们也许想获得一份同时拥有标签1, 2, 10和27的对象列表。这可以用SINTER命令来做,他可以在不同集合之间取出交集。因此为达目的我们只需:

$ redis-cli sinter tag:1:objects tag:2:objects tag:10:objects tag:27:objects

... no result in our dataset composed of just one object ;) ...

6.有序集合

它和集合类型一个重要的区别是带有一个关联的score,以及一个类似LRANGE的操作可以返回有序元素,此操作只能作用于有序集合,它就是,ZRANGE 命令。

添加几个数据作为例子:

$ redis-cli zadd hackers 1940 "Alan Kay"

(integer) 1

$ redis-cli zadd hackers 1953 "Richard Stallman"

(integer) 1

$ redis-cli zadd hackers 1965 "Yukihiro Matsumoto"

(integer) 1

$ redis-cli zadd hackers 1916 "Claude Shannon"

(integer) 1

$ redis-cli zadd hackers 1969 "Linus Torvalds"

(integer) 1

$ redis-cli zadd hackers 1912 "Alan Turing"

(integer) 1

对有序集合来说,按生日排序返回这些黑客易如反掌,因为他们已经是有序的。有序集合是通过一个dual-ported 数据结构实现的,它包含一个精简的有序列表和一个hash table,因此添加一个元素的时间复杂度是O(log(N))。这还行,但当我们需要访问有序的元素时,Redis不必再做任何事情,它已经是有序的了:

$ redis-cli zrange hackers 0 -1

1. Alan Turing

2. Claude Shannon

3. Alan Kay

4. Richard Stallman

5. Yukihiro Matsumoto

6. Linus Torvalds

无论如何,我想反向对这些元素排序,这次就用 ZREVRANGE 代替 ZRANGE 吧:

$ redis-cli zrevrange hackers 0 -1

1. Linus Torvalds

2. Yukihiro Matsumoto

3. Richard Stallman

4. Alan Kay

5. Claude Shannon

6. Alan Turing

1)区间操作

有序集合之能不止于此,他能在区间上操作。例如获取所有1950年之前出生的人。我们用 ZRANGEBYSCORE 命令来做:

$ redis-cli zrangebyscore hackers -inf 1950

1. Alan Turing

2. Claude Shannon

3. Alan Kay

我们请求Redis返回score介于负无穷到1950年之间的元素(两个极值也包含了)。

也可以删除区间内的元素。例如从有序集合中删除生日介于1940到1960年之间的黑客。

$ redis-cli zremrangebyscore hackers 1940 1960

(integer) 2

ZREMRANGEBYSCORE 这个名字虽然不算好,但他却非常有用,还会返回已删除的元素数量。

更详细内容请参考

十五分钟介绍 Redis数据结构:http://blog.nosqlfan.com/html/3202.html?ref=rediszt
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: