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ù)保存在磁盤中。
在創(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)。
為了連接和操作 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ù)類型有五種:字符串(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)系。
字符串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 value
或redis.mset key1 value1 key2 value2
設(shè)置一個或多個key,獲取時通過redis.get key
或redis.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,后面會有介紹。
更多建議: