Redis数据类型和抽象介绍

翻译参考自:http://redis.io/topics/data-types-intro
Redis不是简单的key-value存储,实际上它是一个数据结构服务器(data strutures server),它支持不同的数值类型。即key-value中,value不仅仅局限于string,它可以有更复杂的数据结构:

  • 二进制安全的string
  • List:一个链表,链表中的元素按照插入顺序排列。
  • Sets:string集合,集合中元素唯一,没有排序
  • Sorted set: 和Sets类似,但是它把元素和一个score关联,score为一个float数值。元素根据score排序,因此可以检索某一范围内的元素,例如找出前10或倒数前10的元素。
  • Hashed:由field和value组成的map,field和value都是string类型。和Python和Ruby中的hashes类似。
  • Bit arrayss(bitmaps):可以使用命令,像处理bit数组一样处理string的值,例如可以设置和清空独立的位,设置所有位为1,找出第一个0或1.
  • HyperLogLogs:这是一个概率性(probabilistic)的数据结构,用来估计集合的基数(cardinality)。不用害怕,它实际没有看上去那么难。

学习这些数据类型如何使用、如何解决给定的问题可以通过command reference。这篇博客是个速成课,来学习Redis的数据类型和它们最常用的命令。本节所有的例子,都使用redic-cli utility,它是简单易用的命令模式来发送命令到Redis Server。

Redis keys

Redis的keys是二进制安全的,这意味着任何二进制序列都可以作为key。空的string也可以作为可以。
关于可以的几个准则:
不宜使用太长的key。例如一个1024字节的key不仅耗费内存多,而且查找这个key显然更加耗时。即使需要查看一个长的value是否存在,先hash它是比较好的做法(例如使用SHA1)。这样可以节省内存和带宽。
不宜使用太短的key。例如把“u1000flw”作为key没太多意义,可以使用“user:1000:flowers”,这样更加易读,多占用的内存和总内存相比,可以忽略。当然短的key可以节省内存,但是你需要找一个平衡点。
尽量使用一定格式。例如“object-type:id”是一个好的例子:”user:1000“。在多个单词组成的file中,可以使用点和破折号,例如”comment:1234:reply.to”,”comment:1234:reply-to”。
key的最大大小为512M

Redis Strings

String类型是Redis中最简单的类型,可以结合key来使用。Memcached中只有String类型,因此新人来使用String很容易适应。
Redis的key是String类型,当你的value也是String时,就把一个String映射到另一个String上了。对于换成HTML段和页,String类型非常有用。
使用redis-cli来尝试一下

127.0.0.1:6379> set mykey somevalue
OK
127.0.0.1:6379> get mykey
"somevalue"

set和get命令是设置/获取string的值。set给一个存在的key赋值时,会替换掉旧值。
values可以是任何类型的string,例如可以存储jpeg图像,value最大不能超过512M。
set命令可以添加额外参数。例如当key存在时,可以赋值失败,或者相反,当key存在时才可以赋值。

127.0.0.1:6379> set mykey newval nx
(nil)
127.0.0.1:6379> set mykey newval xx
OK

string是基value的基本类型,可以在上面执行一些有意思的操作,例如加原子操作。

127.0.0.1:6379> set counter 100
OK
127.0.0.1:6379> incr counter
(integer) 101
127.0.0.1:6379> incr counter
(integer) 102
127.0.0.1:6379> incrby counter 50
(integer) 152

incr命令把string转换为整数,然后给它加1,随后把新值赋给它。类似的命令还有incrby、decr、decrby。
incr是原子的,这就意味着即使多个client在同时使用一个key,也不会引起竟态。例如当client1和client2同时给value加1时(旧值为10),它们不会同时读到10。value的最终值一定是12.
还有一些操作string的命令。getset是给key赋新值并返回旧值。
可以一次性给多个key赋值,可以获取多个key的值。

127.0.0.1:6379> mset a 10 b 20 c 30
OK
127.0.0.1:6379> mget a b c
1) "10"
2) "20"
3) "30"
127.0.0.1:6379> 

修改和查询key
exists可以查询key是否存在,0表示不存在,1表示存在。del删除key和其对应的value,删除成功返回1,失败返回0。

127.0.0.1:6379> set mykey hello
OK
127.0.0.1:6379> exists mykey
(integer) 1
127.0.0.1:6379> del mykey
(integer) 1
127.0.0.1:6379> exists mykey
(integer) 0

type命令获取value的类型

127.0.0.1:6379> set mykey x
OK
127.0.0.1:6379> type mykey
string
127.0.0.1:6379> del mykey
(integer) 1
127.0.0.1:6379> type mykey
none

Redis超时:限制key的存活时间
可以设置key的超时时间,当超时后会销毁key,就像我们使用了del式样。
关于Redis超时的几个信息:
1、精度上可以使用秒和毫秒。
2、最小分分辨率为1毫秒。
3、超时信息被复制和持久话到磁盘上了,the time virtually passes when your Redis server remains stopped (this means that Redis saves the date at which a key will expire).

127.0.0.1:6379> set key some-value
OK
127.0.0.1:6379> expire key 5
(integer) 1
127.0.0.1:6379> get key
"some-value"
127.0.0.1:6379> get key
(nil)

第二次使用get时,已经超时。可以使用ttl来查看key的剩余生命

127.0.0.1:6379> set key 100 ex 10
OK
127.0.0.1:6379> ttl key
(integer) 8
127.0.0.1:6379> ttl key
(integer) 5
127.0.0.1:6379> ttl key
(integer) 2
127.0.0.1:6379> ttl key
(integer) -2

如果超时使用毫秒时,使用pexpire和pttl。

Redis Lists

解释List数据类型,需要一点点理论,IT人员经常使用list。
list可以看作一个有序元素的序列:10,20,1,2,3是一个list。使用数组和链表来实现List有很大差别。
Redis中使用链表来实现List,这样在表头和表尾插入使用常量时间。
如果使用数组,使用下表访问元素,比链表快很多。
Redis使用链表来实现,因为对于一个数据库系统来说,在一个长list中添加元素的时间更加关键,它需要更短的时间。另一个原因是存储优势,稍后将会看到在常量时间内可以取常数长度。
当访问速度是系统的关键是,这时还有另外一种数据结构可以使用:sorted sets。
使用Redis Lists:
lpush在list的左边插入元素,rpush在list的右边插入元素,lrange提取list中的元素

127.0.0.1:6379> rpush mylist A
(integer) 1
127.0.0.1:6379> rpush mylist B
(integer) 2
127.0.0.1:6379> lpush mylist first
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"

lrange有2个索引,显示两个索引之间的元素。索引可以为负,-1表示最后一个元素。
可以使用命令一次添加多个元素:

127.0.0.1:6379> rpush mylist 1 2 3 4 5 "foo bar"
(integer) 9
127.0.0.1:6379> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"
4) "1"
5) "2"
6) "3"
7) "4"
8) "5"
9) "foo bar"

Redis List可以pop元素。pop元素操作会获取元素,之后从list删除它。可以从左边/右边删除元素

127.0.0.1:6379> del mylist
(integer) 1
127.0.0.1:6379> rpush mylist a b c
(integer) 3
127.0.0.1:6379> rpop mylist
"c"
127.0.0.1:6379> lpop mylist
"a"
127.0.0.1:6379> rpop mylist
"b"
127.0.0.1:6379> rpop mylist
(nil)

List的常用例子:
List在多任务环境中非常有用,2个比较典型的应用为:
1、在社交网络中,记住最后更新的用户。
2、进程之间的通信,例如生产者消费者模型。
例如,Ruby非常出名的两个库resque和sidekiq使用Redis list来执行背景任务。
Twittre社交网络使用Redis来取用户最新的“twitter”。

Capped lists
许多情况下,我们只是想存储最新的元素:社交更新、日志或其他。Redis允许我们把list当作capped collection使用:仅仅存储最新的N个条目,使用ltrim命令舍弃旧条目。
ltrim命令和lrange相似,它不是给出指定范围内的元素,而是存储最新的值,舍弃所有旧值。

127.0.0.1:6379> rpush mylist 1 2 3 4 5
(integer) 5
127.0.0.1:6379> ltrim mylist 0 2
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"

上面的ltrim命令告诉Redis List保留下标为0到2的元素,其他元素都会被舍弃。
注意:lrange的时间复杂度为O(N),访问靠近头和尾的小范围元素耗费常量时间。

List上的阻塞操作
List的独有特性可以用来实现队列,用来实现进程间通信。
设想一下,你想用一个进程网队列添加元素,用另一个进程去元素。这就是生产者/消费者模型,可以用下列步骤实现。
1、生产者用lpush添加元素。
2、消费者用rpop取元素。
有一种可能的情况就是,list为空,这是ropo会返回NULL。这种情况下,消费者需要等待一段时间再使用rpop。这叫做‘polling’,它不是好的做法因为:
1、强制让Redis和client处理无效命令。
2、在处理元素时加延迟,当工作进程收到NULL后,它等待一小段时间。为了使延迟更小,可以使等待时间更小,这样就造成了更大的可能性返回NULL。
一些Redis使用brpop和blpop,如果队列为空,它会阻塞。

>brpop task 5
1)"task"
2)"do_something"

上面意味着等待队列中的元素,如果超过5秒没有元素,也返回。如果指定为0,那么意味着永远等待。可以一次性等待多个list,当第一个list中收到元素时,会通知等待的客户端。
使用brpop注意的几点:
1、客户端以排序的形式被相应:多个客户但阻塞在List,有了第一个元素后,首先服务地一个客户端,依次类推。
2、与rpop相比,返回的值不相同。它是两个元素的数组,因为它包含了key的名字。因为brpop和blpop在等待多个list时会阻塞。
3、如果超时,返回NULL。
你应该了解更多的阻塞操作,我们建议你阅读以下内容:
1、建立安全队列或旋转队列,使用RPOPLPUSH
2、还有一个阻塞操作的变异,BRPOPLPUSH

Automatic creation and removal of keys
创建和移除key的原子操作:
到目前为止,在我们的例子中还没有创建空的list和移除空的list。Redis的一个责任为移除空的key和创建空的list用来添加元素,例如使用lpush。
这不是特殊的list,它适用于所有Redis数据类型–Sets、Sorted Sets、Hashes。
我们可以总结出三个准则:
1、当我们向聚合数据类型添加元素,如果key不存在,会在添加元素前创建空的聚合数据类型。
2、当从聚合数据类型移除元素,如果value为空,那么key会自动销毁。
3、调用只读命令例如llen(返回list的长度),或用写命令移除空key元素。会产生相同的效果,就像key持有一个空的聚合数据类型,等待被发现。
准则1的例子:

127.0.0.1:6379> del mylist
(integer) 1
127.0.0.1:6379> lpush mylist 1 2 3
(integer) 3

但是我们不能在存在的key上执行错误操作:

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> lpush foo 1 2 3
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type foo
string

准则2的例子:

127.0.0.1:6379> lpush mylist 1 2 3
(integer) 6
127.0.0.1:6379> exists mylist
(integer) 1
127.0.0.1:6379> lpop mylist
"3"
127.0.0.1:6379> lpop mylist
"2"
127.0.0.1:6379> lpop mylist
"1"
127.0.0.1:6379> exists mylist
(integer) 0

当所有元素移除后,key将销毁。
准则3的例子:

127.0.0.1:6379> del mylist
(integer) 0
127.0.0.1:6379> llen mylist
(integer) 0
127.0.0.1:6379> lpop mylist
(nil)

Redis Hashes

Redis hashes看起来非常像我们期待的hash,用field-value pair表示:

127.0.0.1:6379> hmset user:1000 username antirez birthyear 1977 verified 1
OK
127.0.0.1:6379> hget user:1000 username
"antirez"
127.0.0.1:6379> hget user:1000 birthyear
"1977"
127.0.0.1:6379> hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"

hashes很容易表示对象,hash中field的元素没有限制(除了内存),所以你可以以不同的方式在不同的应用中使用。
hmset命令给hash的多个field赋值,hget获取单个field,hmget和hget类似,但是它一次可以返回一个value数组。

127.0.0.1:6379> hmget user:1000 username birthyear no-such-field
1) "antirez"
2) "1977"
3) (nil)

有些可以操作单独field的命令,例如hincrby

127.0.0.1:6379> hincrby user:1000 birthyear 10
(integer) 1987
127.0.0.1:6379> hincrby user:1000 birthyear 10
(integer) 1997

可以在这里找到关于hash的所有操作命令:
http://redis.io/commands#hash

Redis Sets

Redis Sets是存放字符串的无序集合。sadd命令向集合中添加元素。还有一些其他操作,例如测试某个元素是否在集合内,插入,合并,求集合的差等。

127.0.0.1:6379> sadd myset 1 2 3
(integer) 3
127.0.0.1:6379> smembers myset
1) "1"
2) "2"
3) "3"

上面我添加了三个元素,然后告诉Redis返回所有元素。返回元素是无序的。每次调用返回命令时,Redis可能以任意顺序返回。
Redis可以测试某个元素是否存在:

127.0.0.1:6379> sismember myset 3
(integer) 1
127.0.0.1:6379> sismember myset 30
(integer) 0

“3”是set的元素,而“30”不是。
Sets很容易表示对象之间的关系,例如可以很容易地实现标签(tags)。一种实现方式是为每个对象建一个set。set中包含和对象相关联的tag的ID。假设我们想要标签news,如果我们的news的ID是1000,tags为1、2、5、77,这时可以建一个set关联news的id和news item。

127.0.0.1:6379> sadd news:1000:tags 1 2  5 77
(integer) 4

有时候,我想反转关联关系:news的tags关联一个tag

127.0.0.1:6379> sadd tag:1:news 1000
(integer) 1
127.0.0.1:6379> sadd tag:2:news 1000
(integer) 1
127.0.0.1:6379> sadd tag:5:news 1000
(integer) 1
127.0.0.1:6379> sadd tag:77:news 1000
(integer) 1

获取对象的所有tags

127.0.0.1:6379> smembers news:1000:tags
1) "1"
2) "2"
3) "5"
4) "77"

注意:上面的例子中,假设了你有另外一种数据结构,例如Redis hash,它映射了tag id和tag names。
还有一些不太重要的操作可以很容易通过Redis commands实现。例如,想要一个list上的所有元素和tags 1, 2, 10, 27关联,这时可以通过命令sinter实现。sinter命令可以在不同的set中执行插入。

127.0.0.1:6379> sinter tag1:news tag:2:news tag:5:news tag:77:news
(empty list or set)

除了插入,还有结合(union)、求差(difference)、随机取元素等操作。
提取元素的命令为spop,它很容易给特定问题建模。例如,为了实现一个基于web的扑克游戏,可以用set表示deck。设想我们使用单字符前缀, (C)lubs, (D)iamonds, (H)earts, (S)pades:

127.0.0.1:6379> sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3  H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6 S7 S8 S9 S10 SJ SQ SK
(integer) 52

现在我们想给每个玩家5张牌。spop命令随机移除一个元素,并返回给客户端,是实现这个做法的完美命令。
下一局,我们需要填充deck。所以在开始前,可以复制一份set存储到key game:1:deck。
使用sunionstore完成这个操作,这个命令完成不同set之间的合并操作,把结果存储到另外一个set。因为合并一个单独的set,我可以复制:

127.0.0.1:6379> sunionstore game:1:deck deck
(integer) 52

现在我准备给第一个玩家发牌:

127.0.0.1:6379> spop game:1:deck
"D3"
127.0.0.1:6379> spop game:1:deck
"D9"
127.0.0.1:6379> spop game:1:deck
"S9"
127.0.0.1:6379> spop game:1:deck
"C8"
127.0.0.1:6379> spop game:1:deck
"HQ"

……
scard命令用来显示set中剩余的元素

127.0.0.1:6379> scard game:1:deck
(integer) 47

当你仅仅是需要返回随机元素,而不删除它们,这时可以使用srandmember命令。

Redis Sorte sets

Sorted set是Set和Hash的混合数据结构。Sorted sets中元素唯一,所以有点像set。但是set中的元素是无序的。在sorted set中的元素和一个float point相关联,叫做score。这就是为什么像hash,在hash中每个元素映射到一个value。
在Sorted set中的元素是有序的。(它们不是按照要求排序,排序是数据结构的一种特性,代表Sorted sets)。按照如下规则排序:
1、如果A和B两个元素有不同的score,如果A.score > B.score那么A > B。
2、如果A和B有相同的score,如果A字符串字典大于B字符串,那么A > B。A和B字符串不会相等,因为sorted set中元素唯一。
让我们以几个例子开始,把几个黑客名字作为set的元素,它们的出生年作为score

127.0.0.1:6379> zadd hackers 1940 "Alan Kay"
(integer) 1
127.0.0.1:6379> zadd hackers 1957 "Sophie Wilson"
(integer) 1
127.0.0.1:6379> zadd hackers 1953 "Richard Stallman"
(integer) 1
127.0.0.1:6379> zadd hackers 1949 "Antia Borg"
(integer) 1
127.0.0.1:6379> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
127.0.0.1:6379> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
127.0.0.1:6379> zadd hackers 1916 "Claude Shannon"
(integer) 1
127.0.0.1:6379> zadd hackers 1969 "Linus Torvalds"
(integer) 1
127.0.0.1:6379> zadd hackers 1912 "Alan Turing"
(integer) 1

可以看出zadd命令类似sadd命令,但是zadd命令多一个参数,这个参数是score。zadd命令是变长的,可以添加多个score-value对。
可以以list形式返回sorted set中的元素,它们以出生年排序。
Implement note:Sorted set通过双移植(dual-port)数据结构实现:skip list和hash table。所以每次添加一个元素,耗时O(log(N))。使用Redis sorted set不用做任何事,它们已经排序了:

127.0.0.1:6379> zrange hackers 0 -1
 1) "Alan Turing"
 2) "Hedy Lamarr"
 3) "Claude Shannon"
 4) "Alan Kay"
 5) "Antia Borg"
 6) "Richard Stallman"
 7) "Sophie Wilson"
 8) "Yukihiro Matsumoto"
 9) "Linus Torvalds"

注意:0和-1都是索引。
如果想要逆序返回元素,可以使用zrevrange

127.0.0.1:6379> zrevrange hackers 0 -1
 1) "Linus Torvalds"
 2) "Yukihiro Matsumoto"
 3) "Sophie Wilson"
 4) "Richard Stallman"
 5) "Antia Borg"
 6) "Alan Kay"
 7) "Claude Shannon"
 8) "Hedy Lamarr"
 9) "Alan Turing"

添加withscore可以返回scores

127.0.0.1:6379> zrange hackers 0 -1 withscores
 1) "Alan Turing"
 2) "1912"
 3) "Hedy Lamarr"
 4) "1914"
 5) "Claude Shannon"
 6) "1916"
 7) "Alan Kay"
 8) "1940"
 9) "Antia Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"

Opertion on ranges
Sorted sets远远比上面的例子强大。它可以在指定范围操作。找出1950年前出生的人,可以使用rangebyscore:

127.0.0.1:6379> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Antia Borg"

上面的命令是让Redis返回负无穷到1950年出生的人。
移除指定范围内的元素,例如移除1940到1960年出生的人:

127.0.0.1:6379> zremrangebyscore hackers 1940 1960
(integer) 4

zremrangebyscore可能不是最好的命令,但是它非常有用,它返回移除元素的个数。
另外一个比较有用的命令是找出排名的名次。

127.0.0.1:6379> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Yukihiro Matsumoto"
5) "Linus Torvalds"
127.0.0.1:6379> zrank hackers "Yukihiro Matsumoto"
(integer) 3

zrerank可以得到以逆序排序的顺序,元素的排名。

Lexicographical scores

在Redis 2.8版本发布了一个新特性,允许以字典序的形式给元素排序,即假设sorted set中的所有元素的score相同,元素用函数Cmemcmp函数做比较。这将保证没有collation(规则?),每个Redis实例都会返回相同的结果。
操作Lexicographical范围的几个主要命令为:ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX 和 ZLEXCOUNT。
例如,我们来添加黑客,这次他们的score全部为0:

127.0.0.1:6379> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0  "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon" 0 "Linus Torvalds" 0 "Alan Turing"
(integer) 9

按照sorted的排序规则,它们按照字典序排序:

127.0.0.1:6379> zrange hackers 0 -1
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"

使用zrangebylex,我们取字典序的范围:

127.0.0.1:6379> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"

范围可以是inclusive或exclusive(取决于第一个字符),string的无穷大和负无穷大用+和-表示。更多内容可以参考文档。
这个特性很重要,因为它允许我们把sorted set当作通用的索引。例如,你想给一个128bit的无符号整数添加索引,你需要做的就是把元素添加到sorted set,给它们赋值相同的score,8字节的前缀由128bit数字以big endian组成。因为数字是big endian,当以字典序排序(以raw bytes order)时,实际按照数字大小排序,你可以要求128bit的范围空间,得到元素的值,舍弃元素前缀。
如果你想要在一个更加实用的demo中了解这个特性,参考Redis autocomplete demo。

Updating the score:leder boards
更新score:积分榜
Sorted set的scores在任何时候都可以更新。使用命令zadd可以更新元素的scores和位置,时间复杂度为O(log(N))。Sorted set可以适应大量的更新操作。
对这个特性的一个典型应用就是积分榜。例如在FaceBook的游戏中,根据用户得分来排名,可以展现前N名的用户。

Bitmaps

位图不是常规的数据类型,而是在string上面向位的一系列操作。因为string是二进制安全的,最大长度为512M,这样可以用用与2^32个不同的位。
位操作分类2类,常量时间的位操作,例如置0置1,或获取它的值;操作一组位,例如计算给定范围内位中1的个数。
位图的最大优势就是节省内存。例如在一个系统中,用户ID依次递增,可以使用位来表示用户是否想接收新信息,这样512M的位图可以记录40亿的信息。

位的设置和获取可以使用setbit和getbit命令:

127.0.0.1:6379> setbit key 10 1
(integer) 0
127.0.0.1:6379> getbit key 10
(integer) 1
127.0.0.1:6379> getbit key 11
(integer) 0

setbit命令第一个参数是位数,第二个参数是位数的值(0或者1)。当使用的位数超过现有string的长度时,会自动增加string的现有长度。
getbit只是返回特定位数的值。越界的位数被当作0.
操作一组位方面,有三个命令:
1、bitop命令是不同string间的bit-wise操作。它提供AND, OR和XOR,NOT。
2、bitcount计算1的个数。
3、bitpos找到第一个为0或1的位。
bitpos和bitcount都可以以字节操作string,而不是操作整个string。下面是使用bitcount的例子:

127.0.0.1:6379> setbit key 0 1
(integer) 0
127.0.0.1:6379> setbit key 100 1
(integer) 0
127.0.0.1:6379> bitcount key
(integer) 2

用户常常使用的位图为:
1、实时分析各个类型
2、内存节省存储,高效率操作boolean信息

例如你想知道你的网在被用户访问的峰值。你可以在一天开始时,从零开始计数,每当有用户访问时使用setbit命令设置一个位。使用bie的索引除以3600×24可以得到。

HyperLogLogs

HyperLogLog是一个概率型数据结构,用来计算唯一性(技术上来讲,这是估计set的基数)。一般来说,计算个数需要一定比例的内存,这个比例和计算元素个数相关。因为你要记住你之前计数过的元素,防止重复计数。有一系列算法以内存换精度:你可以以估计手段,限制一个标准误差,例如在Redis中,去查小于1%。这个算法的神奇在于你不用使用根计算元素个数来设定使用的内存,可以使用常量的内存。最坏情况下,12k的字节,如果HyperLogLog元素少,内存可以更少。
Redis中,HLLs(HyperLogLog)是不同的数据结构,它被强制看作Redis string,所以你可以用get来序列化一个HLL,set来反序列化它到服务器。
概念上讲,HLL API像使用Set来做相同的任务。你可以sadd每个观察的元素到set,使用scard来检查set中元素数量,它是唯一的因为sadd不会重复添加存在的元素。
当你没有给HLL添加元素时,因为数据结构仅仅包含状态,不包含实际元素,API是相同的:
1、每次你看到一个新元素,使用pfadd把它添加到计数器中。
2、每次你想检索到目前位置使用pfadd添加的元素的估计,使用pfcount。

127.0.0.1:6379> pfadd hll a b c d
(integer) 1
127.0.0.1:6379> pfcount hll
(integer) 4

使用这个数据结构的一个例子是用户每天计算唯一索引的次数。
Redis也可以在HLLs上执行union操作,参考这里。

其他值得注意的特性

在Redis中还有一些重要的API值得关注:
1、It is possible to iterate the key space of a large collection incrementally.
2、It is possible to run Lua scripts server side to win latency and bandwidth.
3、Redis is also a Pub-Sub server.

你可能感兴趣的:(redis,NoSQL,数据类型)