# 2. 数据类型与应用场景 #### 1. 如何学习 redis有好多数据类型,有这么多数据类型,我们不可能每个都记得完完全全。但是我们必须知道它有哪些数据类型,每个数据类型是怎样的,有什么作用。redis的每一个数据类型都有一大堆命令,这些命令也不需要记,要用的时候来查就好了。[Redis 命令参考](http://redisdoc.com/)这个网站就可以来查找每个命令的意义和用法。 #### 2. 数据类型的介绍 在memcached中,存的是健值对,也就是说健(key)和值(value)都是string(字符串)类型。而redis的value是可以有很多种类型,比如字符串(string),列表(list),集合(set)等。 redis的key就是字符串类型。key有一些规则如下: - 不能太长,也不能太短。太长浪费空间,太短不容易区分; - 尝试用冒号来分隔。例如"user:14"代表数据库中id为14的user的那条记录,这样能跟数据库的对应起来; - 最大能存512MB; 下面来介绍每种数据类型。 ##### 2.1 Redis Strings(字符串) 这种也是memcached唯一有的。这个很简单理解,值跟健一样,都是字符串。 我们下面会用`redis-cli`这个客户端工具来演示每一种操作。 ``` 127.0.0.1:6379> set mykey somevalue OK 127.0.0.1:6379> get mykey "somevalue" 127.0.0.1:6379> ``` 很简单,set是设置键`mykey`的值为`somevalue`,get是返回键`mykey`的值。 假如我要对已存在的mykey做set操作,会发生什么呢。 ``` 127.0.0.1:6379> set mykey another OK 127.0.0.1:6379> get mykey "another" ``` 这样会替换掉之前,如果不想让他替换掉呢,有值就返回之前,而不做任何操作。 这种需求,redis也是提供了命令的,也就不需要自己在程序里做判断。 ``` 127.0.0.1:6379> get mykey "anothor" 127.0.0.1:6379> set mykey newvalue nx (nil) 127.0.0.1:6379> get mykey "anothor" ``` 这里想说的是一般的需求redis都是有提供命令的,只要在[Redis 命令参考](http://redisdoc.com/)查找,一般都能找到的。 上面说的存字符串,很常用,最简单的就可以用于存储html片段(HTML fragments),后绪会介绍结合rails轻易实现这种功能。 redis还可以用于存储整型类的字符串。比如: ``` 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这个指令。它是一个原子(atomic)操作。原子性是ACUD的一种,ACUD即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。原子的意思就是最小的,不可分割。打个比方,在同一个时间,两个客户端访问`counter`的值,不可能一个得到100,一个得到101。 其他关于string的命令可自行查找。 ##### 2.2 Redis Lists(列表) 列表相当于数组,只不过这是redis实现的高性能的列表,你往一个很多的列表插入一个元素也是耗费很少的时间。 往列表的头部插值,用LPUSH命令,往尾部插值,用RPUSH,返回列表的值用LRANGE命令。比如: ``` > rpush mylist A (integer) 1 > rpush mylist B (integer) 2 > lpush mylist first (integer) 3 > lrange mylist 0 -1 1) "first" 2) "A" 3) "B" ``` 假如只往头部插入或取出数据,就能形成一个堆栈。往头部取出数据,往尾部插入数据,就形成了一个队列。所以使用list可以构建队列系统。 ##### 2.3 Redis Hashes(哈希) hash就是健值对,跟编程语言所说的哈希是一样的。也就是说,值可以存为结构自由的散列表。 ``` > hmset user:1000 username antirez birthyear 1977 verified 1 OK > hget user:1000 username "antirez" > hget user:1000 birthyear "1977" > hgetall user:1000 1) "username" 2) "antirez" 3) "birthyear" 4) "1977" 5) "verified" 6) "1" ``` 这种hash可以用于对关系型数据库的映射,比如上面的例子,mysql中的users表有一条id为1000的记录,那就可以存到redis中的hash里。 ##### 2.4 Redis Sets(集合) 集合里面存储的内容是字符串(string),所有的字符串是唯一的,且是没有顺序的。所以就没有头和尾的区别了。集合可以用于存储唯一性的列表,比如文章被收藏的用户的ID列表、网站线上的用户的ID等。 集合之间可以取交集,取并集。两个集合取共同的部分,就是取交集,而取并集就是合起来,去掉重复的。 ``` > sadd myset 1 2 3 (integer) 3 > smembers myset 1. 3 2. 1 3. 2 ``` 这种可以用于社交领域,比如两个用户取相同的好友,那就是他们各自好友的集合的交集。在微博应用中,经常要计算你和你的朋友共同关注了哪些人,如果用传统的关系型数据库来做,可能一般的做法是用joins语句,假如表很大,这个可能很费性能,但是用redis,就可以事先把他们各自关注的人的id存到set里,再用这两个set取交集,就能取到共同关注的人的id列表。 ##### 2.5 Redis Sorted sets(排序集合) 排序集合跟集合(set)和哈希(hash)都有点像。对于集合来说,它不同的是,每个元素是有顺序的,是按照一定规则排过序。这个规则就是以健的形式存储,有点像hash。比如下面的例子,黑客按照出生的年来排序。 ``` > zadd hackers 1940 "Alan Kay" (integer) 1 > zadd hackers 1957 "Sophie Wilson" (integer 1) > zadd hackers 1953 "Richard Stallman" (integer) 1 > zadd hackers 1949 "Anita Borg" (integer) 1 > zadd hackers 1965 "Yukihiro Matsumoto" (integer) 1 > zadd hackers 1914 "Hedy Lamarr" (integer) 1 > zadd hackers 1916 "Claude Shannon" (integer) 1 > zadd hackers 1969 "Linus Torvalds" (integer) 1 > zadd hackers 1912 "Alan Turing" (integer) 1 ``` 获取排序集合的内容。 ``` > zrange hackers 0 -1 1) "Alan Turing" 2) "Hedy Lamarr" 3) "Claude Shannon" 4) "Alan Kay" 5) "Anita Borg" 6) "Richard Stallman" 7) "Sophie Wilson" 8) "Yukihiro Matsumoto" 9) "Linus Torvalds" ``` 排序集合可用于排行榜应用,因为排行版的内容肯定是唯一的,且有顺序,可以先按照条件排好顺序,比如文章可按阅读量来排。还能用于自动完成,当你在一个搜索框输入时,会提示有哪些可以选择的,提示的那些数据就可以事先存到redis中,先排好顺序且是唯一的。 ##### 2.6 Bitmaps > Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type 官方的定义是这样的。我们通过例子来解释它,比如进行网站活跃用户统计工作。 用户可能是存在于users表中,每个用户都有一个id。 假如有很两个用户,id分别为10和11,现在这两个用户,id为10的用户是活跃用户,id为11的是不活跃用户。 ``` 127.0.0.1:6379> setbit user 10 1 (integer) 0 127.0.0.1:6379> setbit user 11 0 (integer) 0 127.0.0.1:6379> getbit user 10 (integer) 1 127.0.0.1:6379> getbit user 11 (integer) 0 ``` `setbit`指令中最后传的1或0就相当于true或者false。这样算存好了。那我要取所有的活跃用户,可以这样。 ``` 127.0.0.1:6379> bitcount user (integer) 1 127.0.0.1:6379> setbit user 100 1 (integer) 0 127.0.0.1:6379> bitcount user (integer) 2 ``` 后绪的文章会对各种场景进行详解。 完结。