redis入門筆記

字符串類型

賦值、取值操做

SET key value  // 爲一個鍵賦值
GET key  // 獲取一個鍵值
複製代碼

遞增數字

  • 條件
    • 存儲的字符串是整數
INCR key
複製代碼

當要操做的鍵不存在時默認鍵值爲0,第一次遞增後結果是1 當鍵值不是整數時,redis會提示錯誤javascript

實踐

鍵值命名

首先要解決的一個問題是如何給鍵命名,通常狀況下,命名最佳實踐爲:java

對象類型:對象ID:對象屬性 備註:對於多個單次推薦使用"."分隔 例如使用user:1:friends來存儲ID爲1的用戶的好友列表redis

生成自增id

其次,咱們須要對文章生成一個自增的id,那麼此時咱們可使用INCR指令,對一個鍵進行自增,每次發佈新文章時,都從該鍵內取一個新增的值。 咱們可使用對象類型的命名方法,將鍵值命名爲posts:count,每次新增文章咱們都使用INCR命令,從該鍵值內取值。json

存儲文章數據

咱們使用JavaScript內的JSON方法來對文章內的諸多元素進行序列化,將其變爲字符串形式的數據,存儲在redis中。 此時,咱們寫一個發佈文章的僞JavaScript代碼bash

// 得到文章的新ID
const postId = INCR posts:count
// 將博客文章的數據序列化爲JSON
const jsonPost = JSON.stringify({ title, content, author, time })

// 將序列化後的字符串存入一個字符串類型的鍵中
SET post:postId:data, jsonPost
複製代碼

散列類型

賦值、取值操做

HSET key field value
HGET key field
// 設置多個字段使用下面指令,如:HMSET key field1 value1 field2 value2
HMSET key filed value [field value ...]
// 獲取多個字段使用下面命令,如:HMGET key field1 field2
HMGET key field [field ...]
// 若是想獲取全部字段和字段值,可是不知道鍵中有哪些字段時,使用以下指令
HGETALL key 
複製代碼

HSET不區分插入和更新操做,無需事先判斷字段是否存在來決定要執行的是插入操做仍是插入操做post

  • 執行插入操做(以前字段不存在)時,完成後返回1
  • 執行更新操做(以前字段已存在)時,完成後返回0
  • 當鍵自己不存在時,HSET命令會自動建立

判斷字段是否存在

  • 是否爲原子操做:是
HEXISTS key field
複製代碼

使用該命令判斷字段是否存在,若是存在返回1,不存在返回0,若是鍵不存在也會返回0性能

當字段不存在時賦值

HSETNX key field value
複製代碼

HSETNX 中的NX標識if Not eXists 若是不存在ui

與HSET的區別是:若是字段已存在,該命令不執行任何操做編碼

增長數字

// 增長自定義的數字,如:HINCRBY student score 60
HINCRBY key field increment
複製代碼

若是student鍵不存在,自動建立該鍵,score字段默認爲0,並增長60spa

刪除字段

HDEL key field [field ...]
複製代碼

該命令能夠刪除一個或多個字段,返回值是被刪除的字段個數

實戰

存儲文章數據

前文提到的存儲文章沒法對文章內單個字段進行原子讀寫操做,而且若是隻想獲取標題也須要獲取所有數據後使用JSON解析,消耗資源。 若是還使用字符串類型存儲數據,那麼能夠設計以下結構:

鍵值
post:1:title 第一篇博文
post:1:author 趙玉成
post:1:time 2019年4月5日
post:1:content 這是個人第一篇博文

若是使用散列類型存儲文章內容,那麼能夠設計以下結構:

字段 字段值
post:1 title 第一篇博文
post:1 author 趙玉成
post:1 time 2019年4月5日
post:1 content 這是個人第一篇博文

咱們發現,散列類型存儲方式優勢以下:

  • 更加直觀,而且更容易維護(如獲取所有字段和值執行HGETALL,刪除一個文章元素只須要刪除一個鍵)
  • 存儲一樣的數據,散列類型每每比字符串類型更加節約空間

存儲別名

咱們爲文章增長一個惟一的別名,而且經過該縮略名查找到對應文章id 此時,咱們能夠增長一個散列類型鍵:slug.to.id 來存儲文章的別名對應的id,此時咱們發佈文章的僞代碼以下:

// 得到文章的新ID
const postId = INCR posts:count
// 判斷別名是否存在,若是不存在該別名,則直接使用
const isSlugAvailable = HSETNX slug.to.id, slug, postId

if (isSlugAvailable === 0) return

HMSET post:postId, title, title, content, content, slug, slug, author, author, time, time
複製代碼

下面的僞代碼實現了經過別名來獲取文章內容

const postId = HGET slug.to.id, slug
if (!postid) {
    console.log('文章不存在')
    return
}

const post = HGETALL post:postId
console.log(`文章標題:${post.title}`)
複製代碼

下面的僞代碼模擬了修改id爲1的文章別名

// 若是新的slug已經存在了,那麼將不會設置,而且返回0
const isSlugAvailable = HSETNX slug.to.id, newSlug, 1

if (isSlugAvailable === 0) return

// 獲取舊的別名
const oldSlug = HGET post:42, slug
// 設置新的別名
HSET post:1, slug, newSlug
// 刪除舊的別名
HDEL slug.to.id, oldSlug
複製代碼

列表類型

向列表兩端增長元素

// 向列表左邊增長元素,返回值表示增長元素後列表長度
LPUSH key value [value ...]
// 向列表右邊增長元素,返回值表示增長元素後列表長度
RPUSH key value [value ...]
複製代碼

從列表兩端彈出元素

// 從列表左邊彈出一個元素,返回值爲被移除的元素值
LPOP key
// 從列表右邊彈出一個元素,返回值爲被移除的元素值
RPOP key
複製代碼

以上操做能夠模擬棧和隊列操做: 若是把列表當作棧:搭配使用LPUSH、LPOP或RPUSH、RPOP 若是把列表當作隊列:搭配使用LPUSH、RPOP或RPUSH、LPOP

獲取列表中元素的個數

LLEN key
複製代碼

鍵不存在時返回0 複雜度O(1),由於redis會讀取現成的值

得到列表片斷

LRANGE key start top
複製代碼

得到索引從startstop之間全部元素,包含兩端元素 同時支持負索引,表示從右邊開始計數,「-1」表示從右邊第一個元素

若是start索引位置比stop索引位置靠後,返回空列表 若是stop大於實際的索引範圍,返回列表最右邊的元素

刪除列表中指定的值

LREM key count value
複製代碼

該指令刪除列表中前 count 個值爲 value 的元素,返回的是實際刪除的元素數量。

  • 當count > 0時,從列表左邊開始刪除前count個值爲value的元素
  • 當count < 0時,從列表右邊開始刪除前絕對值count個值爲value的元素
  • 當count爲0時,刪除全部值爲value的元素

實戰

存儲文章ID列表

實現一個文章分頁功能,咱們將使用列表類型鍵posts:list記錄文章列表 發佈新文章時,用LPUSH把新文章id加入列表中,刪除文章時使用LREM post:list 1 要刪除文章的id

使用LRANGE命令來實現文章分頁,僞代碼以下:

const postsPerPage = 10
const start = (currentPage - 1) * postsPerPage
const end = currentPage * postsPerPage - 1
const postsId = LRANGE posts:list, start, end

// 使用獲取到的id列表循環讀取文章
postsId.map(id => {
    let post = HGETALL post:id
    console.log(`文章標題:${post.title}`)
})
複製代碼

此時該文章列表是順序倒序的,新發布的文章在前面

此方法有如下問題:

  • 發佈時間不容易修改,每次變動文章內時間須要從新排序post:list
  • 列表類型經過鏈表實現,文章數量較多時,性能較差

存儲評論列表

當不容許修改本身發表的評論,且讀取評論時須要得到評論的所有數據(評論者姓名、評論時間、評論內容),所以適合將一條評論的各個元素序列化爲字符串後做爲列表類型鍵中的元素來存儲。 假設咱們存儲文章id1的評論,僞代碼以下:

const serializedComment = JSON.stringify({ author, time, contnet })

LPUSH post:1:comments, serializedComment
複製代碼

獲取時使用LRANGE便可

集合類型

增長/刪除元素

SADD key number [number ...]
SREM key number [number ...]
複製代碼

使用SADD時,若是鍵不存在則會自動建立,一個集合中不能有相同的元素,返回值是成功加入的元素數量 使用SREM時,返回值是刪除成功個數

獲取集合中的全部元素

SMEMBERS key
複製代碼

判斷元素是否在集合中

SISMEMBER key number
複製代碼

時間複雜度爲O(1),不管集合中有多少元素均可以極快返回結果

當值存在時返回1,不存在返回0

集合間運算

// 多個集合差集運算,即全部屬於A但不屬於B的元素構成的集合,多個鍵時,先計算A與B的差集,結果再去與C求差集
SDIFF key [key ...]
// 對多個集合執行交集運算
SINTER key [key ...]
// 對多個集合執行並集
SUNION key [key ...]
複製代碼

實踐

存儲文章標籤

因爲文章的每一個標籤都是不一樣的,而且對其沒有排列要求,所以咱們可使用集合類型來存儲文章標籤,對每篇文章使用的鍵名爲post:文章ID:tags 僞代碼以下

SADD post:1:tages, JavaScript, Frontend, Node
SREM post:1:tage, JavaScript
const tags = SMEMBERS post:1:tages
console.log(tages)
複製代碼

經過標籤搜索文章

若是但願列出某個標籤下的文章,在存儲時爲每一個標籤使用名爲tag:標籤名稱:posts的集合類鍵存儲有該標籤的文章id列表

若是但願獲取JavaScript標籤的文章時,只要使用SMEMBERS tag:JavaScript:posts便可,若是但願同時找到JavaScriptNode這兩個標籤的文章,只須要使用SMEMBERS tag:JavaScript:postsSMEMBERS tag:Node:posts取交集,藉助SINTER便可。

有序集合類型

與集合類型的類似點

  • 兩者都是有序的
  • 兩者均可以得到某一範圍的元素 與集合類型不通點
  • 列表類型經過鏈表實現,靠近兩端的數據速度極快,元素增多後,訪問中間數據變慢,所以適合例如:「新鮮事」這種訪問中間元素少的場景
  • 有序集合是使用散列表和調錶實現的,因此即便讀取位於中間部分的數據速度也很快,時間複雜度爲O(logN)
  • 列表中不能簡單的調整某個元素的位置,可是有序集合能夠(經過更改這個元素的分數)
  • 有序集合要比列表類型更耗費內存

增長元素

  • 是否支持無窮大: 是
ZADD key score member [score member ...]
複製代碼

若是該元素已經存在,會使用新的分數替換原有的元素。 分數能夠是整數,也能夠是雙精度浮點數。

inf:表示無窮,該指令支持無窮大 如:ZADD test +inf a

返回值是新加入到集合中的元素個數(不包含以前已經存在的元素)

得到元素的分數

ZSCORE key member
複製代碼

得到排名在某個範圍的元素列表

ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]
複製代碼

ZRANGE會按照分數從小到大的順序返回start到stop之間的全部元素(包含兩端的元素),若是索引爲負數則表明從後向前查找(-1表示最後一個元素) 若是須要得到元素的分數,則須要再命令尾部加上WITHSCORES參數 該命令的時間複雜度爲O(log m+n),n爲有序集合的基數,m爲返回元素個數, 若是分數相同按照字典順序進行排列,中文如按照UTF-8的編碼,則是按照該編碼進行排序。 ZREVRANGE則是與ZRANGE排序相反。

得到指定分數範圍的元素

  • 是否支持無窮大:是
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
複製代碼

按照元素分數從小到大的順序返回分數在min到max之間的元素,包含min和max。若是不包含端點值,能夠在分數前加上「(」符號

WITHSCORES與ZRANGE一致

LIMIT offset count表示在得到的元素列表基礎上向後偏移offset個元素,而且只取前count個元素

例如:獲取分數高於60分的從第二我的開始的3我的,而且帶上分數 ZRANGEBYSCORE scoreboard (60 +inf WITHSCORES LIMIT 1 3

增長某個元素的分數

ZINCRBY key increment member
複製代碼

正數爲增長,負數爲減分 若是指定元素不存在,則會自動建立分數爲0的元素,再執行操做 返回值是更改後的分數

實戰

實現按照點擊量排序

建立一個有序集合鍵,該鍵中以文章id爲元素,文章的點擊量爲分數。該鍵的命名爲posts:page.view,每次用戶訪問一篇文章時,經過ZINCRBY posts:page.view 1 文章ID更新訪問量

下面的僞代碼實現了按照點擊量的順序顯示文章列表:

const postsPerPage = 10
const start = (currentPage - 1) * postsPerPage
const end = currentPage * postsPerPage - 1
const postId = ZREVRANGE posts:page.view, start, end

postId.map(id => {
    let postData = HGETALL post:id
    console.log(`文章標題:${postData}`)
})
複製代碼

按時間排序文章

將元素設置爲文章的id,元素的分數設置爲Unix時間,當文章有更新時,更新分數便可。 藉助ZREVRANGEBYSCORE命令能夠輕鬆得到指定時間的文章列表

相關文章
相關標籤/搜索