# 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
```
后緒的文章會對各種場景進行詳解。
完結。