
redis入門指南第二版讀書(shū)筆記
作者:李子驊
讀者:張劍
Redis是以高性能著稱的內(nèi)存數(shù)據(jù)庫(kù),通常用來(lái)做緩存服務(wù)器。通常人們把它當(dāng)作memcached的替代品。
同時(shí)圍繞Redis也形成了很多產(chǎn)品工具鏈,Redis的命令也成了一種協(xié)議,有些模仿者使用同樣的命令但是在某些方面有所側(cè)重。
通過(guò)replication/persistentce和client-side sharding等特性,用戶可以很方便地將Redis擴(kuò)展成數(shù)百GB數(shù)據(jù)每秒處理上百次請(qǐng)求的系統(tǒng)。
Redis的5種數(shù)據(jù)結(jié)構(gòu)
我們把Redis歸類為內(nèi)存NOSQL Key—Value數(shù)據(jù)庫(kù)。
Redis可以存儲(chǔ)key與五種不同類型的value。
Value的五種類型分別是String,List,Set,HashSet,Zset。
特別說(shuō)明:
String? ? 中存放的是一個(gè)元素,可改變或追加其內(nèi)容(json對(duì)象可以壓縮為二進(jìn)制字符串保存)
List Set? 中存放的是一組元素,List以雙向鏈表方式存儲(chǔ)元素,Set是無(wú)序不可重復(fù)的元素集合
Hash? ? ? 中存放的是一組key-value元素,可看成Map的集合
Sorted Set 中存放的是一組元素,不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類型的score
1.簡(jiǎn)單類型? String
get,set,del
2.鏈表類型? List? 可重復(fù) 有序
lpush和rpush? 加入到list的頭和尾
lpop和rpop? ? 從list的頭和尾出棧
lindex? ? ? ? 根據(jù)指定位置獲取item
lrange? ? ? ? 獲取某個(gè)范圍內(nèi)的items
eg:
lrange list-key 0 -1? ? 取出list-key中的所有values
利用List可以有幾種變化,比如可以當(dāng)作堆棧先進(jìn)后出,也可以當(dāng)作隊(duì)列先進(jìn)先出
lpush和lpop結(jié)合使用就是堆棧
lpush和rpop結(jié)合使用就是隊(duì)列
3.Set類型? Set? ? 不可重復(fù) 無(wú)序
sadd? ? ? ? ? 添加元素
smembers? ? ? 返回所有元素
sismember? ? ? 查看元素是否在set中
srem? ? ? ? ? 如果存在,則刪除該元素
說(shuō)明:
以上3種類型存放的都是元素
4.Hash 類型? HashSet? 存放 key-value 組
hset? ? ? ? ? 在Hash表中存放key-value
hget? ? ? ? ? 在Hash表中根據(jù)指定key獲取value
hgetall? ? ? 返回Hash表中的所有key-value組
hdel? ? ? ? ? 如果存在,根據(jù)key刪除value
eg:
可以一次性設(shè)置多個(gè)key-value組
hset hash_set_demo1 sub-key1 value1 sub-key2 value2
hget hash_set_demo1 sub-key1
hgetall hash_set_demo1
hdel hash_set_demo1 sub-key2
5.Sorted Sets類型? ? zset 排序的Set
zset-key? ? ? zset
element? ? ? score
Jack? ? ? ? 80
Cooper? ? ? 100
zadd? ? ? ? ? 添加一個(gè)元素到zset中
zrange? ? ? ? 從zset中獲取排序之后的多個(gè)元素
zrangebyscore? 獲得按分?jǐn)?shù)排序后的元素
zrem? ? ? ? ? 如果存在,則從zset中刪除元素
eg:
zadd zset-key 60 Jack 80 Cooper 100 David
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "Jack"
2) "60"
3) "Cooper"
4) "80"
5) "David"
6) "100"
127.0.0.1:6379> zrange zset-key 0 -1
1) "Jack"
2) "Cooper"
3) "David"
127.0.0.1:6379> zrangebyscore zset-key 85 100 withscores
1) "David"
2) "100"
127.0.0.1:6379> zrem zset-key Jack
(integer) 1
127.0.0.1:6379> zrem zset-key Cooper
(integer) 0
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "David"
2) "100"
Redis的核心內(nèi)容
1.? 5種數(shù)據(jù)類型的核心命令
String類型
存放3種類型的value
字節(jié)(數(shù)組)值
整數(shù)
浮點(diǎn)數(shù)
說(shuō)明:
整數(shù)有自增和自減操作
incr/decr? 整數(shù)自加1(自減1)
incrby/decrby key byNum? 整數(shù)自增(自減)指定值byNum
incrbyfloat key amount? ? 浮點(diǎn)數(shù)自增指定值amount
List類型? 棧和隊(duì)列類型,允許兩頭進(jìn)行出棧和入棧
lpush和rpush? 加入到list的頭和尾
lpop和rpop? ? 從list的頭和尾出棧
lindex? ? ? ? 根據(jù)指定位置獲取item
lrange key start end? ? ? 獲取某個(gè)范圍內(nèi)的items
ltrim? key start end? ? ? 剔除list中索引不在start ~ end 之間的元素
Set類型? ? 存放無(wú)序不可重復(fù)的元素
sadd key item? 添加元素
srem key item? 刪除元素
sismember key item? 查看item是否在set中
scard key? ? ? 返回set中元素?cái)?shù)量
smembers? ? ? 返回set中所有元素
srandmember? key [count]? 從set中返回一個(gè)或count個(gè)隨機(jī)元素
spop key? ? ? 從set中刪除并返回一個(gè)隨機(jī)元素
smove? source-key dest-key? item? 將item元素從source-key 移到dest-key
兩個(gè)set類型的操作
sdiff? key1 [key2...]? 返回在key1中而不再其他key中的元素
sdiffstore
sinter key1 [key2...]? 返回所有key都有的元素
sinterstore
sunion key1 [key2...]? 返回多個(gè)key中的并集
sunionstore
Hash類型? 存放若干key-value,看做存放map的集合
hmget hash key [key...]? ? 從hash中根據(jù)多個(gè)key獲取對(duì)應(yīng)的value
hmset hash key value [key value ...]? 在hash中設(shè)置多個(gè)key及其對(duì)應(yīng)的value
hdel? hash key [key...]? ? 從hash中根據(jù)key刪除
hlen? hash key? ? ? ? ? ? ? 返回hash中的map元素?cái)?shù)量
hexists? hash key? ? 對(duì)應(yīng)的key是否存在hash中
hkeys? hash? ? ? ? ? 獲取hash中所有的key
hvals? hash? ? ? ? ? 獲取hash中所有的value
hgetall hash? ? ? ? ? 獲取hash中所有的key-value鍵值對(duì)
hincrby hash key? ? ? 如果key中存儲(chǔ)的value是int,則value+1
Sorted Set類型? ? zset? 存放排序的元素
zadd key-name score member [score member ...] 增加一個(gè)元素,并給元素一個(gè)對(duì)應(yīng)的score
zrem key-name member [member ...]? 刪除一個(gè)或多個(gè)元素
zcard key-name? 返回zset中元素?cái)?shù)量
zincrby key-name incrment member
zcount key-name min max 返回score在min與max之間的元素,保護(hù)member 與其 對(duì)應(yīng)的 value
zrank key-name member? 返回member元素在zset中的位置
zscore key-name? ? ? ? 返回zset中所有元素?cái)?shù)量
zrange key-name start stop? 返回score在min與max之間的元素
2.? 事務(wù)
和一般的關(guān)系數(shù)據(jù)庫(kù)類似,redis也支持事務(wù)。
但是redis的事務(wù)還是有所區(qū)別:
a.不能回滾,
b.即使其中一個(gè)命令有了運(yùn)行錯(cuò)誤(非語(yǔ)法錯(cuò)誤),也不會(huì)影響到同一事務(wù)中其他命令的執(zhí)行。
c.事務(wù)中的每個(gè)命令的執(zhí)行結(jié)果都是最后一起返回的,所以無(wú)法將前一條命令的結(jié)果作為下一條命令的參數(shù)
結(jié)合WATCH命令
WATCH 命令可以監(jiān)控一個(gè)或多個(gè)鍵,一旦其中有一個(gè)鍵被修改(或刪除),之后的事務(wù)就不會(huì)執(zhí)行。監(jiān)控一直持續(xù)到 EXEC 命令
redis> MULTI
OK
redis> SADD "user:1:following" 2
QUEUED
redis> SADD "user:2:followers" 1
QUEUED
redis> EXEC
1) (integer) 1
2) (integer) 1
3.過(guò)期時(shí)間
過(guò)期時(shí)間經(jīng)常在訪問(wèn)頻率控制和實(shí)現(xiàn)緩存中使用
EXPIRE session:29e3d 900
4.消息通知
利用消息通知,可以實(shí)現(xiàn)任務(wù)隊(duì)列,優(yōu)先級(jí)隊(duì)列,發(fā)布/訂閱模式
這里就用到了list的阻塞彈出命令,不過(guò)下面沒(méi)有提到在消息通知和任務(wù)隊(duì)列里面常用的彈出推入命令(RPOPLPUSH)。
a.任務(wù)隊(duì)列
BRPOP命令和RPOP命令相似,唯一的區(qū)別是當(dāng)列表中沒(méi)有元素時(shí)BRPOP命令會(huì)一直阻塞住連接,直到有新元素加入。
示例代碼:
loop
# 如果任務(wù)隊(duì)列中沒(méi)有新任務(wù),BRPOP 命令會(huì)一直阻塞,不會(huì)執(zhí)行 execute()。
$task = BRPOP queue, 0
# 返回值是一個(gè)數(shù)組(見(jiàn)下介紹),數(shù)組第二個(gè)元素是我們需要的任務(wù)。
execute($task[1])
b.優(yōu)先級(jí)隊(duì)列
BRPOP 命令可以同時(shí)接收多個(gè)鍵,其完整的命令格式為 BLPOP key [key …] timeout,如 BLPOP queue:1 queue:2 0。意義是同時(shí)檢測(cè)多個(gè)鍵,如果所有鍵都沒(méi)有元素則阻塞,如果其中有一個(gè)鍵有元素則會(huì)從該鍵中彈出元素。
示例代碼:
loop
$task =BRPOP queue:confirmation.email,
queue:notification.email, 0
execute($task[1])
這時(shí)一旦發(fā)送確認(rèn)郵件的任務(wù)被加入到 queue:confirmation.email 隊(duì)列中,無(wú)論queue: notification.email還有多少任務(wù),消費(fèi)者都會(huì)優(yōu)先完成發(fā)送確認(rèn)郵件的任務(wù)。
c.發(fā)布/訂閱模式
通過(guò)兩個(gè)命令實(shí)現(xiàn)發(fā)布/訂閱模式
PUBLISH
SUBSCRIBE
5.管道pipeline
管道和事務(wù)類似,不過(guò)管道的主要目的是為了減少來(lái)回通信次數(shù),也就節(jié)省了來(lái)回的通信時(shí)間。
Redis 的底層通信協(xié)議對(duì)管道(pipelining)提供了支持。通過(guò)管道可以一次性發(fā)送多條命令并在執(zhí)行完后一次性將結(jié)果返回,當(dāng)一組命令中每條命令都不依賴于之前命令的執(zhí)行結(jié)果時(shí)就可以將這組命令一起通過(guò)管道發(fā)出。管道通過(guò)減少客戶端與 Redis 的通信次數(shù)來(lái)實(shí)現(xiàn)降低往返時(shí)延累計(jì)值的目的
6.排序
ALPHA參數(shù)
LIMIT參數(shù)
DESC/ASC參數(shù)
by參數(shù)
get參數(shù)
下面是sort在各種數(shù)據(jù)結(jié)構(gòu)的使用實(shí)例
$array_tag_ruby_posts = array(
'2', '11', '6', '12'
// , '26', '28'
);
$redis->del('tag:ruby:posts');
$redis->lpush('tag:ruby:posts', $array_tag_ruby_posts);
echo '----------sort1--------';
echo '
';
print_r($redis->lrange('tag:ruby:posts', 0, -1));
$option1_sort = array(
//? ? 'by' => 'weight_*',
//? ? 'get' => array('value_*', '#'),
'sort' => 'desc',
//? ? 'alpha' => true,
'limit' => array(1, 3),
'store' => 'result');
$redis->sort('tag:ruby:posts', $option1_sort);
echo '
';
//print_r($redis->type('result'));
print_r($redis->lrange('result', 0, -1));
echo '
----------sort2--------
';
$array_sortbylist = array(2, 1, 3);
$redis->del('sortbylist');
$redis->lpush('sortbylist', $array_sortbylist);
print_r($redis->lrange('sortbylist', 0, -1));
echo '
';
$redis->set('itemscore:1', 50);
$redis->set('itemscore:2', 100);
$redis->set('itemscore:3', -10);
$option2_sort = array(
'by' => 'itemscore:*',
//? ? 'get' => array('value_*', '#'),
'sort' => 'desc',
//? ? 'alpha' => true,
//? ? 'limit' => array(1, 3),
'store' => 'result2'
);
$redis->sort('sortbylist', $option2_sort);
print_r($redis->lrange('result2', 0, -1));
echo '
----------sort3--------
';
$redis->del(array('post:2', 'post:6', 'post:11', 'post:12', 'post:26', 'post:28'));
$post2 = array(
'title' => 'Windows 8 app designs',
'time' => '1352620100'
);
$post6 = array(
'title' => 'RethinkDB - An open-source distributed database built with love',
'time' => '1352620000'
);
$post11 = array(
'title' => 'The Nature of Ruby',
'time' => '1352619200'
);
$post12 = array(
'title' => 'Uses for cURL',
'time' => '1352619600'
);
$redis->hmset('post:2',$post2);
$redis->hmset('post:6',$post6);
$redis->hmset('post:11',$post11);
$redis->hmset('post:12',$post12);
$option3_sort = array(
'by' => 'post:*->time',
'get' => array('post:*->title','post:*->time', '#'),
'sort' => 'desc',
//? ? 'alpha' => true,
//? ? 'limit' => array(1, 3),
'store' => 'result3'
);
$redis->sort('tag:ruby:posts',$option3_sort);
print_r($redis->lrange('result3',0,-1));
7.節(jié)省空間
a.命名簡(jiǎn)化
b.內(nèi)部編碼優(yōu)化
本書(shū)里面沒(méi)有的內(nèi)容
1.Redis HyperLogLog起始版本:2.8.9
Redis HyperLogLog是用來(lái)做基數(shù)統(tǒng)計(jì)的算法。優(yōu)點(diǎn)是,在輸入元素的數(shù)量或者體積非常非常大時(shí),計(jì)算基數(shù)所需的空間總是固定的、并且是很小的。
在 Redis 里面,每個(gè) HyperLogLog 鍵只需要花費(fèi) 12 KB 內(nèi)存,就可以計(jì)算接近 2^64 個(gè)不同元素的基 數(shù)。這和計(jì)算基數(shù)時(shí),元素越多耗費(fèi)內(nèi)存就越多的集合形成鮮明對(duì)比。
注:因?yàn)镠yperLogLog只會(huì)根據(jù)輸入元素來(lái)計(jì)算基數(shù),而不會(huì)存儲(chǔ)輸入元素本身,因此不會(huì)返回輸入的各個(gè)元素。
基數(shù)是什么? 對(duì)于["abc", "abc", "2", "3"],基數(shù)是["abc", "2", "3"],個(gè)數(shù)是3.
HyperLogLog通過(guò)下面三個(gè)命令:
pfadd key ele [ele2 ...]:添加指定元素到HyperLogLog中,
pfcount key: 返回給定HyperLogLog的基數(shù)估算值
pfmerge destkey srckey [srckey2....]:講多個(gè)HyperLogLog合并到一個(gè)第一個(gè)HyperLogLog中