關於redis那些你不得不知道的事兒(一)

前言

redis(全稱:Remote Dictionary Server,即遠程字典服務),近年來可以說是火遍了大江南北,它能幹的事兒是真不少。最基本的,做爲系統緩存我們可以使用它;我們還可以使用它維護一個高效的消息隊列。然而Redis能夠實現的功能遠遠不止於此,小小的鍵值對被玩出了無數的花樣(當然這和Redis本身提供的豐富功能也脫不了關係)。

如果你已經迫不及待的想要使用Redis了,那麼,這裏有一些關於Redis你不得不知道的事兒

本篇不着重闡述原理,而是從最基本的API使用上進行較爲詳細的介紹,方便快速代入使用場景

爲什麼要使用Redis

  • 最爲重要的一點就是Redis速度夠快,能夠明顯的提高系統性能。
  • Redis能夠非常簡單的實現特定的業務場景,比如,排行榜,好友關係等
  • 能夠實現簡單的消息隊列

速度

Redis速度快的最主要的原因是它是“完全”基於內存的數據庫(當然也提供了持久化),這使得 它避免了使用寫磁盤的格式對內存數據接口編碼的開銷(磁盤的數據依舊是要讀到內存中才能夠被使用,這裏面存在着很大的開銷),故而直接從內存讀取數據是要比讀磁盤要快非常多。

另外,其他次要一些原因還有:

  1. Redis是由C語言實現的(語言級別速度的保證(´・ω・`) )
  2. 其線程模型爲單線程,避免了線程切換的開銷
  3. 網絡方面使用epoll解決了高併發問題

根據官方說法,Redis最高能夠達到 10w OPS(operation per second)!

高效、豐富的數據結構

除了最基本的鍵值對,Redis提供了豐富的數據結構,包括

  1. 字符串(String)
  2. 哈希(Hash Tables)
  3. 列表(Linked List)
  4. 集合(Sets)
  5. 有序集合(Sorted Set)

其他還有一些基於上述結構實現的數據結構

  1. 位圖 (Bit Map)
  2. HyperLogLog(使用超小內存(12k)實現唯一值,本質是字符串)
  3. GEO (地理信息定位)

持久化

雖然內存只要一斷電就嗝屁,但是Redis也是提供持久化功能的,在內存上操作保證速度的同時,也提供了RDB和AOF兩種方式進行持久化,將數據的更新異步的保存到磁盤上,無需擔心數據的丟失了:)

其他

豐富的功能

  1. Redis支持多種編程語言
  2. 提供發佈訂閱,事務功能
  3. 支持編寫Lua腳本
  4. 支持pipline
  5. 支持主從複製,分佈式(Redis-Cluster),高可用(Redis-Sentinel)

簡單

使用簡約但不簡單!

Redis API

下面通過介紹Redis API來多方面的展現Redis的強大功能

字符串

字符串是redis最基本的結構,很多redis的高級數據結構底層都是由字符串實現的,下面來看看redis字符串的API。

字符串的使用場景很多,最爲基本的是作爲緩存,另外還有計數器(字符串提供的自增),分佈式鎖等等。

  1. get/del操作 O(1)
# 獲取key對應的value
get key
# 刪除key 
del key
  1. set操作 O(1)
# 設置一個值(無論是否存在)
set key value
# key不存在時才做設置
setnx key value
# key存在時才設置
set key value xx
  1. mget O(n)
# 批量獲取key值
mget key1 key2 key3

對於redis來說,所有的key都是字符串,value則類型多樣

對於value字符串來說,value可以是字符串,也可以是數字和二進制類型,這兩種類型是在redis內部自動轉換的,之後將要提到的位圖(bitmp)即是使用字符串的二進制形式所實現的。

單個字符串value的大小小於等於512MB,通常一個value限制在100k以內是比較合適的。

對於批量獲取操作mget來說,如果有需要多次取值的操作,不妨使用mget進行批量獲取,相比於多次get更加的高效。

  1. 整型操作
# key自增1,如果key不存在,自增後get(key)= 1
incr key
# 同上相反 ,自減
decr key
# key自增k,如果key不存在,自增後get(key)= k
incrby key k
# key 自減k,其他同incrby相反 
decrby key k

前面說的計數器,就可以用上面的自增API進行實現,非常的方便:)

  1. 其他一些操作
#  set key newvalue並返回舊的value
getset key newvalue
# 將value追加到舊的value
append key value
# 返回字符串長度(注意中文)
strlen key

# 增加key對應的值
incrbyfloat key value
# 獲取字符串指定下標所有的值
getrange key start end
# 設置,其他同上
setrange key index value

Hash

redis的hash是一個mapMap的結構,其結構長這樣
在這裏插入圖片描述
hash的key依舊是字符串,其value分爲兩個部分:fieldval,其中field的值不能相同,val值可以相同(如果理解哈希概念的話,這個地方應該不會陌生),你可以把它理解爲編程語言中的哈希表,字典等數據結構。其實redis本身就是一個大字典,hash相當於一個嵌套字典了

舉個🌰, 我們可以用hash來存儲一個用戶的相關信息,像這樣
在這裏插入圖片描述
下面我們來看看redis hash結構的相關API

# 獲取hash key對應的field的value
hget key field
# 設置hash key對應的field的value
hset key field value
# 刪除hash key對應的field的value
hdel key field
# 判斷hash key是否有field
hexists key field
# 獲取hash key field的數量
hlen key
# 批量獲取hash key的一批field對應的值
hmget key field1 field2 ... fieldN
# 批量設置hash key的一批field value
hmset key field1 value field2 value2...fieldN valueN
# 返回hash key 對應所有的field 和 value(謹慎使用), 數據量大時會阻塞
hgetall key
# 返回hash key對應所有field的value
hvals key
# 返回hash key對應的所有field
hkeys key
# 設置hash key對應的field的value(如field已經存在則失敗)
hsetnx key filed value
# hash key對應的field的value自增inCounter
hincrby key field intCounter
# hincrby浮點數版
hincrbyfloat key field floatCounter

使用hash保存複雜信息

涉及到複雜信息更新時,我們通常可以有限考慮使用redis的hash結構,從上面的用戶信息例子可以看出,hash結構存儲往往更加直觀,並且能夠節省內存(這是由其數據結構決定的),最關鍵的是,它可以部分更新。同樣的實現用戶信息存儲則必須全量更新纔行。

但是需要注意的是,我們可以爲redis數據設置過期時間,而hash結構的過期精度只支持到key級別,不能爲field單獨設置過期時間

list

直觀一點,就是一個列表結構,像這樣
在這裏插入圖片描述
你可以直接把它當成編程語言中的一個列表結構(儘管內部實現可能不一致):有序、內部元素可重複,可以在列表左邊和右邊插入和彈出元素。
看看redis list的API

# 從列表右端插入值(1-N個)
# e.g. rpush listkey c b a: 結果  c—>b—>a
rpush key value1 value2... valueN
# 和rpush相反
lpush
# 從列表左側彈出一個item
lpop key
# 與lpop相反
rpop
# 根據count值,從列表中刪除所有value相等的項
# count > 0,從左到右,刪除最多count個value相等的項
lrem key count value
# 在list指定的值前 | 後插入newValue
linsert key before | after value newValue
# 按照索引範圍修剪列表(start end爲需要保留的範圍)
ltrim key start end
# 獲取列表指定索引範圍所有item(包含end)
lrange key start end
# 獲取列表指定索引的item
lindex key
# 獲取列表長度
llen key
# 設置列表指定索引值爲newValue
lset key index newValue
# lpop阻塞版本,timeout是阻塞超時時間,timeout=0爲永遠不阻塞(對生產者消費者模型,消息隊列實現有幫助)
blpop key timeout
# 與blpop相反
brpop key timeout

通過使用redis的list API,我們可以非常方便的實現一些功能,例如

list的使用場景(棧、隊列、消息隊列…)

  • 使用 lpush + lpop 可以實現一個簡單的棧(後進先出)
  • 使用 lpush + rpop 可以實現一個簡單的隊列(先進先出)
  • 使用 lpush + ltrim 可以實現一個固定數量的列表
  • 使用 lpush + brpop 可以實現一個消息隊列

set 集合

我們直接類比到編程語言的集合數據結構(比如python的set),它長這樣
在這裏插入圖片描述
集合內部的元素是無序的且不重複的,多個集合直接我們可以進行一些類似並集,交集的操作,話不多說,直接來看看redis提供的set API

=====================集合內操作===========================
# 添加集合元素
sadd key value1 value2
# 刪除集合元素
srem key value1 value2
# 計算集合大小
scard key
# 判斷value是否在集合中(返回1表示存在)
sismember key value
# 從集合中隨機挑count個元素
srandmember key count
# 從集合中隨機彈出一個元素
spop key
# 獲取集合所有元素(小心使用, 可能會阻塞) 
smembers key
=====================集合間操作===========================
# 取value交集
sinter key1 key2
# 取所有不同value
sdiff  key1 key2
# 取所有value
sunion key1 key2
# 將上述集合間操作結果保存至destkey
sinter|sdiff|sunion + store destkey

set的使用場景(抽獎,點贊,共同關注…)

這是微博上隨便截取的一個抽獎微博截圖,如果讓我們用redis來實現一個抽獎,那麼,使用set就是一個非常好的選擇。
在這裏插入圖片描述
假設我們的有一個key叫users,他對應的value是一個set,裏面存儲了所有用戶的id,那麼,我們就可以使用spopsrandmember來很方便的實現抽獎功能。

其他的業務場景比如知乎文章的贊同,反對,我們可以使用集合的API來實現(對於每一篇文章,當用戶點贊時,調用sadd將用戶id推進集合當中)

還有類似於共同關注的功能,我們也可以很方便的使用集合的取交集和並集的操作來完成。

zset(有序集合)

zset同樣也是一個集合,但是與上節的set相比,zset是有序的,redis中的zset結構長這樣:
在這裏插入圖片描述
如果說set的value是一個個遊離的單獨元素的話,那麼zset就是給這些元素打上了一個序號(score)

老規矩,先來看看API

# 添加socre和value(可以是多對)
zadd key score1 value1 score2 value2
# 刪除元素(可以是多個)
zrem key value1 value2
# 返回元素的score
zscore key value
# 增加或減少元素的socre
zincrby key increSocre value
# 返回元素的總個數
zcard key
# 返回value的排名(類似索引id)
zrank key value
# 返回範圍內的 value score ,-1代表最後一個
zrange key start end [WITHSCORES]
# 返回指定分數範圍內的升序元素[分值]
zrangebyscore key minScore maxScore [WITHSCORES]
# 獲取有序集合內在指定分數範圍內的個數
zcount key minScore maxScore
# 刪除指定排名內的升序元素
zremrangebyrank key start end
# 刪除指定分數內的升序元素
zremrangebyscore key minScore maxScore

使用zset實現一個排行榜

在這裏插入圖片描述
這是一個微博的熱搜截圖,在這裏熱搜數據相當於一個zset,圖上的4685796, 4384536,4368888…就是zset的score(搜索量),具體的信息就是一個個value,zset內部會根據socore進行排序, 這裏的排名跟就是根據這個socre來的。你看,通過對zset API的靈活運用,一個基本的排名功能是不是就出來了呢:)

後記

本篇只是對redis中幾種典型的數據結構進行了一番介紹,在下一章中,我們還將進行一些redis高級特性的討論(比如redis的慢查詢管道pipline發佈訂閱位圖HyperLoglog以及GEO等等),這些特性會給我們在日常開發當中提供許多的便利~

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章