
關注【搜狐技術産品】,第一時間擷取技術幹貨
作者丨大個女
摘要:如果你是第一次開始要使用redis,那麼請閱讀該篇文章。該篇文章系統性的介紹redis的特點及常見使用場景,通過實際操作着重介紹了String、Set、Hash、SortedSet、List常見資料結構指令的使用,以及在實際開發中如何更好更高效的使用redis。
概述
Redis是一個開源的、基于記憶體的結構化存儲媒介,可用作資料庫、緩存、消息中間件。
它支援strings、hash、set、list、sorted set、bitmaps、hyperloglogs、geo資料結構。
另外,Redis還提供了如下的功能:
- 支援鍵過期,可以用來實作緩存系統
- 提供釋出訂閱功能,用來實作消息系統
- 提供簡單的事務功能,在一定程度上保證事務特性
- 提供pipeline,支援一次将多個指令請求發送到服務端,相比較一個指令一個指令執行,減少了網絡開銷的時間
- 支援記憶體資料持久化至硬碟(RDB或者AOF)
- 1主N(N>=1)從模式下的高可用解決方案Sentinel
- 3.0+ 以上版本提供的分布式redis cluster
Redis使用場景
- 緩存
幾乎所有的大型系統都會用到緩存,合理地使用緩存不僅可以加速資料的通路速度,同時也可以降低後端資料源的壓力。
- 計數器應用
比如說某些網站需要對一篇文章的閱讀量需要進行計數,參照資料結構:incr/incrby(原子性).
- 擷取最新N條資料
比如擷取某使用者最新釋出的N條文章,這個時候就可以考慮使用redis,參照資料結構:List(lpush+ltrim).
- 排行榜
基于不同次元的資料的排序,參照資料結構sortedset和list .
- 隊列系統
Redis高性能揭秘
- 單線程結構,避免了多線程環境下的線程間切換(當然單線程也有弊端,自己思考下)
- 基于epoll的IO多路複用模型,将網路上的可讀、可寫等事件轉化成了内部的FileEvent事件
- 純記憶體通路
資料結構及常用指令
首先,Redis 針對 strings、list、set、zset、hash 這5種資料結構每種結構底層都對應有至少2種以上的内部編碼實作,這樣做一來是為了友善内部編碼重構時不會影響外部資料結構的操作,另一方面是在鍵對應的資料量較小時節省記憶體。
redis 有0~15 個資料庫,預設為db.
以下操作在redis-server:V3.2.8 ,用戶端:redis-cli上執行.
基礎指令
- 檢視目前db的所有鍵
keys *
時間複雜度O(n),該指令不建議在生産環境使用,當key較多的時候會阻塞redis,建議使用替代指令scan、hscan、sscan、zscan。
2.檢視目前db鍵總數
Dbsize
3、檢查鍵是否存在,存在傳回1、不存在傳回0
exists $key
存在則傳回1、不存在傳回0
4、删除鍵
del $key
通用指令傳回結果為删除成功的鍵個數,删除不存在的key傳回0,支援一次删除多個鍵
5、鍵過期
Expire $key $seconds
當為key設定了過期時間,超過過期時間後,鍵會被删除;另可使用ttl指令檢視對應的剩餘過期時間,傳回值有以下3種:
- -2 鍵不存在
- -1 鍵未設定過期時間
- 自然數,鍵的剩餘過期時間
類似的為key設定過期時間的指令還有pexpire、expireat、pexpireat.
6、鍵的類型
Type $key
當鍵不存在時,傳回none.
7、檢視鍵對應的内部編碼
object encoding $key
字元串
Redis裡鍵都是字元串類型,官方給出的值最大不超過512MB,但是實際使用中建議字元串長度最好不超過1K。
這裡,重點介紹下常用的幾個指令.
1、設定一個字元串
Set $key $value [expiration EX seconds |PX milliseconds] [NX|XX]
NX表示鍵不存在時,寫入該key,XX表示鍵存在時寫入(實際就是更新操作).
這裡要注意下,如果第一次set了一個key并設定了過期時間,那麼再次針對該key執行set操作的話,該key之前的過期時間會被清空.
2、擷取鍵的值
Get $key
傳回對應鍵的值,傳回nil如果鍵不存在
3、批量SET
Mset $key $val [$key $val …]
該操作是原子性的.
這裡同樣要注意,如果mset中的某個鍵存在于目前DB中的話,會用新的值替換掉舊的值,過期時間也會被清除(如果存在的話);如果想要在鍵不存在的情況下,批量寫入,請使用msetnx 指令.
4、批量get
Mget $key [$key …]
當給定的鍵不存在或者鍵類型不是字元串的話,對應鍵傳回特殊值nil.
使用批量操作,可以提高開發效率,節省來回的網絡時間,但是要注意,批量操作的指令數不宜過多,過多會阻塞redis.
5、計數
Incr/incrby/incrbyfloat、decr/decrby
當鍵對應的val為整數時,執行成功.
哈希
- 寫入值
Hset $key $field $val
當新增一個field時,傳回結果為1,當更新了一個已經存在的field時,傳回0.
2、擷取field值
hget $key $field
field存在,傳回對應的值,反之傳回nil.
3、批量設定或者擷取field
hmset $key $field $val [$field $val …]
hmget $key $field [$field …]
4、删除field
hdel $key $field
5、擷取鍵的所有鍵值對/鍵/值
hgetall $key
hkeys $key
hvals $key
6、field是否存在
hexists $key $field
鍵不存在或者field不存在時,傳回0.
清單
redis的清單是雙端連結清單,可以再兩端進行push、pop操作,一個清單最多可以存2^32-1 個元素。
- 插入元素
lpush $key val [val …] 向清單左側(頭部)插入元素
rpush $key val [val …] 向清單右側(尾部)插入元素
2、删除元素
lpop $key 從清單頭部删除一個元素
rpop $key 從清單尾部删除一個元素
3、擷取清單元素個數
llen $key
4、擷取指定範圍内的元素
lrange $key $start $end
$start、$end額外說明:0表示清單的第一個元素、1表示第2個元素,-1表示清單的最後一個元素,-2表示倒數第二個元素等等。
5、清空清單
ltrim $key $start $end
嚴格滿足 $start、$end >0 && $start >$end.
6、阻塞式的删除清單元素
blpop $key [$key …] $timeout
brpop $key [ $key …] $timeout
如果逾時,則傳回nil.
7、清單間的移除增加操作
rpoplpush $source $destination
将$source清單的最後一個元素從source移除後添加到destination的頭部,若$source為空,則不進行任何操作.
該指令也有阻塞式的操作brpoplpush,詳情參見https://redis.io/commands/brpoplpush.
基于清單以上操作,我們可以通過清單的指令組合來實作一些常用資料結構,例如:lpush+lpop 實作棧,lpush+rpop實作隊列等.
集合
- 添加元素
sadd $key $member [ $member …]
2、擷取集合中元素個數
scard $key
3、擷取集合中的所有元素
smembers $key
4、判斷一個元素是否在集合内
sismember $key $member
5、删除集合中的元素
spop $key [$count]
6、集合間求交集、并集等
sinter $key [$key …]
sinterstore $destination $key [$key …]
sunion $key [$key …]
sunionstore $destination $key [$key …]
sdiff $key [$key …]
sdiffstore $destination $key [$key …]
有序集合
有序集合中的每一個元素有一個score作為排序依據,切記有序集合中的元素不能重複,但是score可以重複。
- 添加元素
zadd $key [nx|xx] [ch] [incr] $score $member [$score $member …]
2、傳回有序集合内元素個數
zcard $key
3、計算某個成員的分數
zscore $key $member
4、計算某個成員的排名
zrank $key $member 分數從低到高排名
zrevrank $key $member 分數從高到低排名
5、指定排名範圍内的成員
zrange $key $start $end [withscores]
zrevrange $key $start $end [withscores]
6、有序集合内的元素删除
zrem $key $member [$member …]
7、删除指定分數範圍内的元素
zremrangebyscore $key $min $max
常用配置項(config)
- 慢查詢相關
slowlog-log-slower-than 定義當指令處理時間超過多少,該指令會被記錄在慢查詢清單中
slowlog-max-len 定義慢查詢清單的大小
2、連接配接空閑多久會被server關閉回收掉
timeout 定義目前連接配接空閑多久會被server主動關閉掉。
3、輸出緩沖區配置
client-output-buffer-limit:該配置項定義了針對不同類型的用戶端的輸出緩沖區的大小(hard limit 和 soft limit 兩部分),例如:normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60 :對于normal節點來說,輸出緩沖區沒有限制;對slave節點來說,當輸出緩沖區大于256MB或在60s内持續大于64MB,該連接配接會被server端主動關閉掉,這個線上上使用的時候一定要注意根據業務量進行修改。這裡額外說下,這裡的輸出緩沖區指的是輸出緩沖區的變長緩沖區即動态連結清單部分。
4、最大連接配接數
maxclients:定義redis server 允許的最大用戶端連接配接數,當連接配接數超過該值時,會報類似 ‘-ERR max number of clients reached’ 錯誤.
性能測試
Redis-server所在伺服器參數:Rhel 6.5 ,記憶體 256G,CPU: Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz
此處隻是為了測試Redis的處理性能,是以redis-benchmark與redis server 運作在同一台伺服器上,測試結果如下:
set操作壓測
get操作壓測
lpush操作壓測
sadd操作壓測
spop操作壓測
實際環境中,ops不會高于上述測試結果,除了server端的指令處理時間外,還有網絡時間和請求排隊時間(一次請求的耗時=網絡時間+請求排隊時間+服務端的指令處理時間)
使用建議
鍵命名規範化關于鍵命名的定義,大衆推薦使用“[業務名]:對象名:id:[屬性]’’,切勿盲目遵守該約定導緻鍵名太長,在該約定基礎上,保證鍵名含義清晰的情況下盡可能的短。
冷熱資料分離建議将高頻熱資料存儲在Redis中,對于一些不太經常通路的資料可以存儲在mysql等使用硬碟的存儲媒介中。
不同業務的資料分開存儲redis預設有16個db,可以将不同業務的資料存儲在不同的db中,或者部署多組不同的redis來提供給不同業務使用。
當緩存用時設定過期時間redis做緩存用時,給key設定過期時間,過期時間根據實際業務進行評估,切勿随意給一個很長的時間。
大value建議壓縮後存儲實際使用中,如果碰到一些無法進行資料結構拆分的大key不得不存儲在redis中時,建議對這類value進行壓縮後存儲。
避免生産環境使使用keys等耗時指令在資料量keys比較多的情況下,執行keys指令會在一段時間内阻塞redis,這樣導緻其他指令的請求處理逾時,尤其是在高OPS的情況下更要避免使用該類操作。
謹慎使用Hash、Set、SortedSet資料結構的全量操作例如,對于hgetall操作,當hash中隻有幾十個或者幾百個field的時候,hgetall的響應時間很快,完全可以滿足業務低延遲的需求,但是當field數增長到到幾萬個或者幾十萬個的時候,這個時候再去執行hgetall響應時間勢必會增加。
合理使用pipeline一次set操作的響應時間=請求網絡傳輸時間+請求排隊時間+server處理時間+響應網絡傳輸時間,那麼10次set的響應時間則為10次請求網絡傳輸時間+請求排隊時間+server端指令處理時間+10次響應網絡傳輸時間,但是當使用pipeline時,10次set請求封裝成一次網絡請求到server端,同理響應也是一次網絡傳輸,節省了大量的RTT(這裡隻是舉例,多個值的set 操作redis預設有支援,可自己檢視官方文檔)。
關注【搜狐技術産品】公衆号,第一時間擷取技術幹貨!