云開發(fā) 連接Redis數(shù)據(jù)庫

2020-07-21 17:55 更新

Redis 是一個開源高性能基于key-value的NoSQL 數(shù)據(jù)庫,支持多種類型的數(shù)據(jù)結(jié)構(gòu),如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted set)等而且對數(shù)據(jù)的操作都是原子性的。Redis運(yùn)行在內(nèi)存中,所以具有極高的讀寫速度,同時也支持?jǐn)?shù)據(jù)的持久化,將內(nèi)存中的數(shù)據(jù)保存在磁盤中。

一、Redis與私有網(wǎng)絡(luò)

1、Redis應(yīng)用場景

  • 計(jì)數(shù)器:因?yàn)镽edis操作是原子性的,通過原子遞增或遞減來做高并發(fā)用戶的數(shù)據(jù)計(jì)數(shù),比如點(diǎn)贊數(shù)、收藏數(shù)、分享數(shù)、商品搶購時的庫存量、商品文章總數(shù)、評論數(shù)量等

  • 排行榜:Redis支持集合和有序集合的數(shù)據(jù)結(jié)構(gòu),且運(yùn)行在內(nèi)存中,因此可以存儲一些類似于排行榜的數(shù)據(jù),比如最近、最熱、點(diǎn)擊率最高、活躍度最高、評論最多等等的文章、商品、用戶等;

  • 哈希表:用戶粉絲列表、用戶點(diǎn)贊列表、用戶收藏列表、用戶關(guān)注列表等;

  • 自動排序:存儲時間戳,隨著時間的變化,按照用戶關(guān)注用戶的最新動態(tài)列表等自動排序;

  • 會話緩存:使用Redis進(jìn)行會話緩存,將web session存放在Redis中。

  • 全頁緩存FPC:可以將服務(wù)端渲染結(jié)果的緩存在Redis中;

  • 記錄用戶操作信息:用戶是否點(diǎn)贊、用戶是否收藏、用戶是否分享等;

2、創(chuàng)建Redis

在創(chuàng)建了上海可用區(qū)的私有網(wǎng)絡(luò)之后(可以參考上一節(jié)的內(nèi)容),我們可以購買騰訊云在上??捎脜^(qū)的Redis服務(wù),網(wǎng)絡(luò)類型找到你創(chuàng)建的私有網(wǎng)絡(luò)以及相應(yīng)的子網(wǎng)即可。

在騰訊云網(wǎng)頁云開發(fā)控制臺中,找到需要配置的云函數(shù),比如函數(shù)名為redis,點(diǎn)擊右上角編輯進(jìn)入配置界面,在函數(shù)配置界面中,修改網(wǎng)絡(luò)配置為Redis所在的同一私有網(wǎng)絡(luò)子網(wǎng)。

二、使用ioredis操作redis

為了連接和操作 Redis 實(shí)例,我們需要一個 Redis 客戶端,推薦使用ioredis(類似的還有node_redis、tedis等)。使用開發(fā)者工具打開云函數(shù)目錄中的 package.json ,新增最新版ioredis 依賴,右鍵云函數(shù)目錄選擇在終端中打開輸入命令npm install安裝依賴::

"dependencies": {
  "wx-server-sdk":"latest",
  "ioredis":"latest"
}

然后在index.js里輸入以下代碼,里面涉及redis多個命令行,其中zadd命令是往redis里添加有序集合,zscore命令返回有序集合元素相應(yīng)的分?jǐn)?shù)值,zrevrank命令返回有序集合元素的排名(Redis有多種數(shù)據(jù)結(jié)構(gòu),不同的數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)的增刪改查都有著相應(yīng)的命令,這里就不多介紹了):

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV,
})
const Redis = require('ioredis')
const redis = await new Redis({
  port: 6379,
  host: '10.168.0.11', 
  family: 4, // 4 (IPv4) 或 6 (IPv6)
  password: 'cloudbase2020',//redis的密碼
  db: 0,
})
exports.main = async (event, context) => {
  await redis.zadd('Score',145,'user1')
  await redis.zadd('Score',134,'user2')
  await redis.zadd('Score',117,'user3')
  await redis.zadd('Score',147,'user4')
  await redis.zadd('Score',125,'user5')


  const score = await redis.zscore('Score','user3')
  console.log('用戶3的分?jǐn)?shù)',score)
  const rank = await redis.zrevrank('Score','user5')
  console.log('用戶5的排名',rank)
  return {'用戶3的分?jǐn)?shù)':score,'用戶5的排名':rank}
}

三、Redis數(shù)據(jù)類型和數(shù)據(jù)的存儲

Redis常用的數(shù)據(jù)類型有五種:字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted set),而JavaScript和云開發(fā)數(shù)據(jù)庫的數(shù)據(jù)類型主要有字符串(String)、數(shù)字(Number)、布爾值(Boolean)、數(shù)組(Array)、對象(Object)。當(dāng)我們要將云數(shù)據(jù)庫或JavaScript的數(shù)組和對象這種比較復(fù)雜的數(shù)據(jù)類型存儲到Redis時,應(yīng)該怎么做呢?下面我們只粗略討論一下Redis與JavaScript以及云開發(fā)數(shù)據(jù)庫之間的關(guān)聯(lián)關(guān)系。

Redis常用數(shù)據(jù)類型

字符串Strings

Redis的字符串是二進(jìn)制安全的,在傳輸數(shù)據(jù)時,保證二進(jìn)制數(shù)據(jù)的信息安全,也就是不被篡改、破譯等,也不會對這些數(shù)據(jù)進(jìn)行編碼、序列化等。字符串存儲的結(jié)構(gòu)為key:value,可以用于存儲JavaScript的字符串、數(shù)值類型,通常也用于存儲HTML的節(jié)點(diǎn)或者網(wǎng)頁。當(dāng)然也可以用于存儲圖片等,盡管一個key的存儲上限為512M,但是通常不建議存儲的值過長(比如不要超過1024 bytes,不然內(nèi)存成本和key的比對成本太高)也不建議太短(只是建議)。

我們還能給字符串的值設(shè)置過期時間,以及如果值為整數(shù)(Redis沒有專門的整數(shù)類型,所以key儲存的值在執(zhí)行原子操作命令時會被解釋為十進(jìn)制 64 位有符號整數(shù))可以對數(shù)值進(jìn)行類似于云開發(fā)數(shù)據(jù)庫的原子操作,比如INCR storage就是給字符串storage(表示商品庫存)原子增加1,而DECRBY storage 30,就是給庫存原子減少30。

我們可以在云函數(shù)里使用ioredis、node-redis等依賴,通過redis.set key valueredis.mset key1 value1 key2 value2設(shè)置一個或多個key,獲取時通過redis.get keyredis.mget key1 key2獲取redis數(shù)據(jù)庫中已有的key的值,字符串在redis的結(jié)構(gòu)如下:

SecretId "AKIDpZ9Wp1pyhFdqrioDF5dmDkMoQ7oVF2shUOE" //用于存儲一些key、token等數(shù)據(jù)
openId "oUL-m5FuRmuVmxvbYOGuXbuEDsn8"  //可以存儲云開發(fā)經(jīng)常用到的openID
storage 1017   //表示商品庫存為1017,執(zhí)行原子操作命令會被解釋為十進(jìn)制有符號(正負(fù))整數(shù)

關(guān)于字符串string的命令,有SET、GET、MSET、MGET、INCR、DECR、INCRBY、DECRBY等命令,具體可以閱讀Redis技術(shù)文檔。

散列哈希表Hashes

Redis的散列哈希表 Hashes是一個 string 類型的 field 和 value 的映射表,特別適合用于存儲JavaScript的對象,因此也是使用非常頻繁的一個數(shù)據(jù)類型。Redis 中每個 hash 可以存儲的鍵值對沒有上限(除非內(nèi)存的量不允許)。

當(dāng)我們使用JavaScript創(chuàng)建一個對象或者要往云開發(fā)數(shù)據(jù)庫里獲取/傳入數(shù)據(jù)時,就會涉及到如下的數(shù)據(jù)樣式(下面是一篇文章的數(shù)據(jù)),那我們應(yīng)該怎么把這樣的數(shù)據(jù)存儲到Redis呢?

{
  "title": "為什么狗會如此親近人類?",
  "id": 9717547,
  "url": "https://daily.zhihu.com/story/9717547",
  "image": "https://pic4.zhimg.com/v2-60f220ee6c5bf035d0eaf2dd4736342b.jpg",
  "body":  "<p>讓狗從兇猛的野獸變成忠實(shí)的愛寵...</p>"
}

我們可以使用Redis哈希表的hmset命令HMSET key field value,我們把key的值設(shè)置為post-${id},而對象里的屬性和值對應(yīng)的寫法如下:

hmset post-9717547 title "為什么狗會如此親近人類?" id 9717547 url "https://daily.zhihu.com/story/9717547" image "https://pic4.zhimg.com/v2-60f220ee6c5bf035d0eaf2dd4736342b.jpg" body "<p>讓狗從兇猛的野獸變成忠實(shí)的愛寵...</p>"

而當(dāng)我們要獲取哈希表的值以及要對哈希表里的數(shù)據(jù)進(jìn)行增刪改查時,相應(yīng)的操作命令如下(只是列舉了部分,更多內(nèi)容請查看技術(shù)文檔):

//HGETALL以列表形式返回哈希表的字段及字段值
hgetall post-9717547
//HMGET命令返回哈希表中一個或多個給定字段的值,比如獲取2個key title和id的值;HGET是只返回一個
hmget post-9717547 title id
hget post-9717547 body
//HMSET同時將多個鍵值對設(shè)置到哈希表中,比如我們同時設(shè)置兩個鍵值對,HSET是只設(shè)置一個;如果key相同就會覆蓋
hmset post-9717547 author "李東bbsky"  city "深圳"
hset post-9717547 position "雜役"

還有刪除哈希表字段的hdel、查看字段是否存在的hexists、為指定字段的整數(shù)原子添加增量(可以為正或負(fù))的hincrby、獲取字段數(shù)量的hlen、獲取所有字段的hkeys等等,這些具體可以看文檔??傊?,有了哈希表,我們就可以用來存儲一些簡單的對象(沒有嵌套和嵌套數(shù)組)了。

列表Lists

Redis的列表類型可以用來存儲多個有序的字符串,列表里的值是可以重復(fù)的,有點(diǎn)類似于JavaScript的數(shù)組(還是有很多不同的哦),主要的應(yīng)用場景是用戶最新的動態(tài)信息、最新博客、朋友圈最新動態(tài)。在Redis中,可以對列表兩端插入(push)和彈出(pop),也可以獲取指定范圍的元素列表以及指定索引下標(biāo)的元素等,可以充當(dāng)棧和隊(duì)列的角色。

//rpush在列表的尾部(右邊)添加一個或多個值,類似于數(shù)組方法里的push;lpush在列表的頭部(左邊)添加一個或多個值,類似于數(shù)組方法里的unshift
rpush code "Python" "JavaScript" "Java" "C++" "Golang" "Dart" "C" "C#"


//rpop移除并返回列表最后一個元素,類似于數(shù)組方法里的pop;lpop移除并返回列表第一個元素,類似于數(shù)組方法里的shift
rpop code


//llen返回列表的長度,有點(diǎn)類似于數(shù)組的屬性length
llen code


//lindex通過索引獲取列表中的元素,有點(diǎn)類似于數(shù)組的array[n]獲取數(shù)組第n+1位的元素
lindex code 3


//lrange返回列表中指定區(qū)間內(nèi)的元素,有點(diǎn)類似于數(shù)組方法里的slice
lrange code 2 5


//linsert key before|after pivot value,在列表的元素前或者后插入元素。當(dāng)指定元素不存在于列表中時,不執(zhí)行任何操作,如下方式是把SQL插入到Dart前,數(shù)組的slice方法可以在指定位置插入元素
linsert code before "Dart" "SQL"


//lset通過索引來設(shè)置元素的值,有點(diǎn)類似于數(shù)組的array[n]=""
lset code 4 "Go"

集合Sets

Redis的集合是字符串類型的無序集合,集合里的元素是無序且唯一的,不能出現(xiàn)重復(fù)的數(shù)據(jù)。Redis支持集合內(nèi)元素的增刪改查,還支持多個集合的交集、并集、差集以及跨集合移動元素,特別適合社交系統(tǒng)、電商系統(tǒng)、視頻App里等常見的打標(biāo)簽,比如你最感興趣的人、話題、項(xiàng)目等,網(wǎng)站和App會根據(jù)用戶的興趣點(diǎn)來推薦不同的內(nèi)容。

//sadd 將一個或多個成員元素加入到集合中,已經(jīng)存在于集合的成員元素將被忽略
sadd cloudbase "云函數(shù)" "云數(shù)據(jù)庫" "云存儲" "云接入" "云應(yīng)用" "云調(diào)用"


//smembers返回集合中的所有成員
smembers cloudbase


//scard返回集合中元素的數(shù)量
scard cloudbase


//srandmember返回集合中一個或多個隨機(jī)數(shù),spop移除集合中的指定的一個或多個隨機(jī)元素,移除后會返回移除的元素
srandmember cloudbase 2
spop cloudbase


//sismember判斷元素是否在集合中,在則返回1,不在返回0
sismember cloudbase "云調(diào)用"

Redis處理跨集合的命令如求并集sunion,存儲并集sunionstore,交集sinter、存儲交集sinterstore,差集sdiff、存儲差集sdiffstore,跨集合移動元素smove,等等這里就不一一舉例了。

有序集合Sorted sets

Redis的有序集合和集合一樣也字符串類型元素且元素不重復(fù)的集合,不同的是,有序集合多了一個排序?qū)傩詓core(分?jǐn)?shù)),也就是每個存儲元素由兩個值構(gòu)成,一個是元素值,一個是排序值。有序集合的元素是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。有序集合特別適合做排行榜系統(tǒng),比如點(diǎn)贊排名、銷量最多、播放最多、成績最好、分?jǐn)?shù)排名等。

下面我們把文章的閱讀量以及文章的id寫入到Redis的有序集合里,我們可以很方便的將文章按一些要求來排序:

//zadd命令用于將一個或多個元素及分?jǐn)?shù)值加入到有序集中。如果元素已經(jīng)存在,會更新這個元素的分?jǐn)?shù)值,并通過重新插入這個元素,來保證該元素在正確的位置上。
zadd read:rank 9932 post-323 3211 post-123 1234 post-77 987 post-33 532 post-21


//zrange把元素按分?jǐn)?shù)遞增來排序,0為第一位,-1為最后一位,0,-1會把所有元素都排序;而1,3則是取排序的第2、4位;zrevrange則是遞減
zrange read:rank 0 -1 withscores
zrange read:rank 1 3 withscores
zrevrange read:rank 1 3 withscores


//zcount顯示分?jǐn)?shù)score在 min 和 max 之間的元素的數(shù)量
zcount read:rank 1000 3000


//zrank返回有序集合指定元素的排名(排名以0為底),按分?jǐn)?shù)值遞增(從小到大)順序排列;zrevrank是從大到小
zrank read:rank post-323
zrevrank read:rank post-987

和連接MySQL一樣,建議在云函數(shù)中使用Redis時,把同一個Redis實(shí)例的增刪改查等操作都集中寫在一個云函數(shù)里,這樣會減少云函數(shù)冷啟動的概率以及減少對數(shù)據(jù)庫連接數(shù)的占用,而將增刪改查的處理集中到一個云函數(shù),我們可以使用到云函數(shù)路由tcb-router,后面會有介紹。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號