Redis 是一个开源高性能基于key-value的NoSQL 数据库,支持多种类型的数据结构,如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted set)等而且对数据的操作都是原子性的。Redis运行在内存中,所以具有极高的读写速度,同时也支持数据的持久化,将内存中的数据保存在磁盘中。
在创建了上海可用区的私有网络之后(可以参考上一节的内容),我们可以购买腾讯云在上海可用区的Redis服务,网络类型找到你创建的私有网络以及相应的子网即可。
在腾讯云网页云开发控制台中,找到需要配置的云函数,比如函数名为redis,点击右上角编辑进入配置界面,在函数配置界面中,修改网络配置为Redis所在的同一私有网络子网。
为了连接和操作 Redis 实例,我们需要一个 Redis 客户端,推荐使用ioredis(类似的还有node_redis、tedis等)。使用开发者工具打开云函数目录中的 package.json ,新增最新版ioredis 依赖,右键云函数目录选择在终端中打开输入命令npm install安装依赖::
"dependencies": {
"wx-server-sdk":"latest",
"ioredis":"latest"
}
然后在index.js里输入以下代码,里面涉及redis多个命令行,其中zadd命令是往redis里添加有序集合,zscore命令返回有序集合元素相应的分数值,zrevrank命令返回有序集合元素的排名(Redis有多种数据结构,不同的数据结构的数据的增删改查都有着相应的命令,这里就不多介绍了):
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的分数',score)
const rank = await redis.zrevrank('Score','user5')
console.log('用户5的排名',rank)
return {'用户3的分数':score,'用户5的排名':rank}
}
Redis常用的数据类型有五种:字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted set),而JavaScript和云开发数据库的数据类型主要有字符串(String)、数字(Number)、布尔值(Boolean)、数组(Array)、对象(Object)。当我们要将云数据库或JavaScript的数组和对象这种比较复杂的数据类型存储到Redis时,应该怎么做呢?下面我们只粗略讨论一下Redis与JavaScript以及云开发数据库之间的关联关系。
字符串Strings
Redis的字符串是二进制安全的,在传输数据时,保证二进制数据的信息安全,也就是不被篡改、破译等,也不会对这些数据进行编码、序列化等。字符串存储的结构为key:value
,可以用于存储JavaScript的字符串、数值类型,通常也用于存储HTML的节点或者网页。当然也可以用于存储图片等,尽管一个key的存储上限为512M,但是通常不建议存储的值过长(比如不要超过1024 bytes,不然内存成本和key的比对成本太高)也不建议太短(只是建议)。
我们还能给字符串的值设置过期时间,以及如果值为整数(Redis没有专门的整数类型,所以key储存的值在执行原子操作命令时会被解释为十进制 64 位有符号整数)可以对数值进行类似于云开发数据库的原子操作,比如INCR storage
就是给字符串storage(表示商品库存)原子增加1,而DECRBY storage 30
,就是给库存原子减少30。
我们可以在云函数里使用ioredis、node-redis等依赖,通过redis.set key value
或redis.mset key1 value1 key2 value2
设置一个或多个key,获取时通过redis.get key
或redis.mget key1 key2
获取redis数据库中已有的key的值,字符串在redis的结构如下:
SecretId "AKIDpZ9Wp1pyhFdqrioDF5dmDkMoQ7oVF2shUOE" //用于存储一些key、token等数据
openId "oUL-m5FuRmuVmxvbYOGuXbuEDsn8" //可以存储云开发经常用到的openID
storage 1017 //表示商品库存为1017,执行原子操作命令会被解释为十进制有符号(正负)整数
关于字符串string的命令,有SET、GET、MSET、MGET、INCR、DECR、INCRBY、DECRBY等命令,具体可以阅读Redis技术文档。
散列哈希表Hashes
Redis的散列哈希表 Hashes是一个 string 类型的 field 和 value 的映射表,特别适合用于存储JavaScript的对象,因此也是使用非常频繁的一个数据类型。Redis 中每个 hash 可以存储的键值对没有上限(除非内存的量不允许)。
当我们使用JavaScript创建一个对象或者要往云开发数据库里获取/传入数据时,就会涉及到如下的数据样式(下面是一篇文章的数据),那我们应该怎么把这样的数据存储到Redis呢?
{
"title": "为什么狗会如此亲近人类?",
"id": 9717547,
"url": "https://daily.zhihu.com/story/9717547",
"image": "https://pic4.zhimg.com/v2-60f220ee6c5bf035d0eaf2dd4736342b.jpg",
"body": "<p>让狗从凶猛的野兽变成忠实的爱宠...</p>"
}
我们可以使用Redis哈希表的hmset命令HMSET key field value
,我们把key的值设置为post-${id}
,而对象里的属性和值对应的写法如下:
hmset post-9717547 title "为什么狗会如此亲近人类?" id 9717547 url "https://daily.zhihu.com/story/9717547" image "https://pic4.zhimg.com/v2-60f220ee6c5bf035d0eaf2dd4736342b.jpg" body "<p>让狗从凶猛的野兽变成忠实的爱宠...</p>"
而当我们要获取哈希表的值以及要对哈希表里的数据进行增删改查时,相应的操作命令如下(只是列举了部分,更多内容请查看技术文档):
//HGETALL以列表形式返回哈希表的字段及字段值
hgetall post-9717547
//HMGET命令返回哈希表中一个或多个给定字段的值,比如获取2个key title和id的值;HGET是只返回一个
hmget post-9717547 title id
hget post-9717547 body
//HMSET同时将多个键值对设置到哈希表中,比如我们同时设置两个键值对,HSET是只设置一个;如果key相同就会覆盖
hmset post-9717547 author "李东bbsky" city "深圳"
hset post-9717547 position "杂役"
还有删除哈希表字段的hdel、查看字段是否存在的hexists、为指定字段的整数原子添加增量(可以为正或负)的hincrby、获取字段数量的hlen、获取所有字段的hkeys等等,这些具体可以看文档。总之,有了哈希表,我们就可以用来存储一些简单的对象(没有嵌套和嵌套数组)了。
列表Lists
Redis的列表类型可以用来存储多个有序的字符串,列表里的值是可以重复的,有点类似于JavaScript的数组(还是有很多不同的哦),主要的应用场景是用户最新的动态信息、最新博客、朋友圈最新动态。在Redis中,可以对列表两端插入(push)和弹出(pop),也可以获取指定范围的元素列表以及指定索引下标的元素等,可以充当栈和队列的角色。
//rpush在列表的尾部(右边)添加一个或多个值,类似于数组方法里的push;lpush在列表的头部(左边)添加一个或多个值,类似于数组方法里的unshift
rpush code "Python" "JavaScript" "Java" "C++" "Golang" "Dart" "C" "C#"
//rpop移除并返回列表最后一个元素,类似于数组方法里的pop;lpop移除并返回列表第一个元素,类似于数组方法里的shift
rpop code
//llen返回列表的长度,有点类似于数组的属性length
llen code
//lindex通过索引获取列表中的元素,有点类似于数组的array[n]获取数组第n+1位的元素
lindex code 3
//lrange返回列表中指定区间内的元素,有点类似于数组方法里的slice
lrange code 2 5
//linsert key before|after pivot value,在列表的元素前或者后插入元素。当指定元素不存在于列表中时,不执行任何操作,如下方式是把SQL插入到Dart前,数组的slice方法可以在指定位置插入元素
linsert code before "Dart" "SQL"
//lset通过索引来设置元素的值,有点类似于数组的array[n]=""
lset code 4 "Go"
集合Sets
Redis的集合是字符串类型的无序集合,集合里的元素是无序且唯一的,不能出现重复的数据。Redis支持集合内元素的增删改查,还支持多个集合的交集、并集、差集以及跨集合移动元素,特别适合社交系统、电商系统、视频App里等常见的打标签,比如你最感兴趣的人、话题、项目等,网站和App会根据用户的兴趣点来推荐不同的内容。
//sadd 将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略
sadd cloudbase "云函数" "云数据库" "云存储" "云接入" "云应用" "云调用"
//smembers返回集合中的所有成员
smembers cloudbase
//scard返回集合中元素的数量
scard cloudbase
//srandmember返回集合中一个或多个随机数,spop移除集合中的指定的一个或多个随机元素,移除后会返回移除的元素
srandmember cloudbase 2
spop cloudbase
//sismember判断元素是否在集合中,在则返回1,不在返回0
sismember cloudbase "云调用"
Redis处理跨集合的命令如求并集sunion
,存储并集sunionstore
,交集sinter
、存储交集sinterstore
,差集sdiff
、存储差集sdiffstore
,跨集合移动元素smove
,等等这里就不一一举例了。
有序集合Sorted sets
Redis的有序集合和集合一样也字符串类型元素且元素不重复的集合,不同的是,有序集合多了一个排序属性score(分数),也就是每个存储元素由两个值构成,一个是元素值,一个是排序值。有序集合的元素是唯一的,但分数(score)却可以重复。有序集合特别适合做排行榜系统,比如点赞排名、销量最多、播放最多、成绩最好、分数排名等。
下面我们把文章的阅读量以及文章的id写入到Redis的有序集合里,我们可以很方便的将文章按一些要求来排序:
//zadd命令用于将一个或多个元素及分数值加入到有序集中。如果元素已经存在,会更新这个元素的分数值,并通过重新插入这个元素,来保证该元素在正确的位置上。
zadd read:rank 9932 post-323 3211 post-123 1234 post-77 987 post-33 532 post-21
//zrange把元素按分数递增来排序,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显示分数score在 min 和 max 之间的元素的数量
zcount read:rank 1000 3000
//zrank返回有序集合指定元素的排名(排名以0为底),按分数值递增(从小到大)顺序排列;zrevrank是从大到小
zrank read:rank post-323
zrevrank read:rank post-987
和连接MySQL一样,建议在云函数中使用Redis时,把同一个Redis实例的增删改查等操作都集中写在一个云函数里,这样会减少云函数冷启动的概率以及减少对数据库连接数的占用,而将增删改查的处理集中到一个云函数,我们可以使用到云函数路由tcb-router,后面会有介绍。