社区/学习指南/小程序·云开发高级教程

连接Redis数据库

Redis 是一个开源高性能基于 key-value 的 NoSQL 数据库,支持多种类型的数据结构,如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted set)等而且对数据的操作都是原子性的。Redis 运行在内存中,所以具有极高的读写速度,同时也支持数据的持久化,将内存中的数据保存在磁盘中。

11.8.1 Redis 与私有网络

1、Redis 应用场景

  • 计数器:因为 Redis 操作是原子性的,通过原子递增或递减来做高并发用户的数据计数,比如点赞数、收藏数、分享数、商品抢购时的库存量、商品文章总数、评论数量等

  • 排行榜:Redis 支持集合和有序集合的数据结构,且运行在内存中,因此可以存储一些类似于排行榜的数据,比如最近、最热、点击率最高、活跃度最高、评论最多等等的文章、商品、用户等;

  • 哈希表:用户粉丝列表、用户点赞列表、用户收藏列表、用户关注列表等;

  • 自动排序:存储时间戳,随着时间的变化,按照用户关注用户的最新动态列表等自动排序;

  • 会话缓存:使用 Redis 进行会话缓存,将 web session 存放在 Redis 中。

  • 全页缓存 FPC:可以将服务端渲染结果的缓存在 Redis 中;

  • 记录用户操作信息:用户是否点赞、用户是否收藏、用户是否分享等;

2、创建 Redis

在创建了上海可用区的私有网络之后(可以参考上一节的内容),我们可以购买腾讯云在上海可用区的 Redis 服务,网络类型找到你创建的私有网络以及相应的子网即可。

在腾讯云网页云开发控制台中,找到需要配置的云函数,比如函数名为 redis,点击右上角编辑进入配置界面,在函数配置界面中,修改网络配置为 Redis 所在的同一私有网络子网。

11.8.2 使用 ioredis 操作 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 };
};

11.8.3 Redis 数据类型和数据的存储

Redis 常用的数据类型有五种:字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted set),而 JavaScript 和云开发数据库的数据类型主要有字符串(String)、数字(Number)、布尔值(Boolean)、数组(Array)、对象(Object)。当我们要将云数据库或 JavaScript 的数组和对象这种比较复杂的数据类型存储到 Redis 时,应该怎么做呢?下面我们只粗略讨论一下 Redis 与 JavaScript 以及云开发数据库之间的关联关系。

1、Redis 常用数据类型

字符串 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 valueredis.mset key1 value1 key2 value2设置一个或多个 key,获取时通过redis.get keyredis.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,后面会有介绍。

本文出自 李东bbsky