浅析小程序云原生数据库设计与应用
从软件工程的角度来看,软件开发经历了如下三个阶段:传统开发->敏捷迭代->Serverless。
传统的开发模式和敏捷迭代开发模式除了需要开发者编写核心的业务逻辑外,都不可避免地需要对后端的基础设施进行管控和优化。比如,一个应用的逻辑可以很简单,可一旦涉及到应用的发布部署,就需要开发者花费大量精力进行服务器、数据库、网络等基础设施的申请和搭建,还要考虑这些后端基础设施的稳定性、可用性和监控指标。这一切耗时耗力又与产品的核心功能无关,对于需要快速开发和试错的产品,传统的模式开发速度慢、部署和运维成本较高。
随着 Serverless 概念的火热,越来越多的开发者开始转向Serverless发展。“Serverless”并不是指后端没有服务器,而是将后端服务器及相关运维操作变得对上层应用开发者不可见和透明,使得用户无需关心后端的基础设施,直接通过云API一键接入云函数、云数据库和云存储来获取算力、数据库、存储等基础的后端能力。这种随用随取的开发模式,不但可以让开发者能更专注于自身的业务逻辑,还具有低成本、开发速度快以及免运维等诸多优势。
小程序需求 + Serverless理念 = 小程序云开发
基于此,小程序云开发以微信作为小程序前端运行的依托,同时又通过接入云函数、云数据库和云存储,来达到对后端基础设施的开箱即用。这些特性可以在很大程度上解放小程序开发者的生产力,降低开发的成本和难度。
云数据库作为小程序云开发的重要组件之一,应具备以下特性/能力:
- 安全性:对于数据库而言,数据安全是第一位;
- 易用性:开箱即用,用完即走,简单上手,免运维;
- 低成本:按量收费,精细化成本控制;
- 高性能:Nosql,支持高并发读写;
- 灵活性:(no-schema) 无固定的数据库表模式,当业务量扩展时可以非常方便的弹性伸缩。
围绕这5个主要需求,云开发数据库从架构设计等方面进行了相应的改造及优化:
最上面是小程序的接入客户端,中间部分是接入层,底层是数据库的存储层,当然,还有周边的管控、告警、备份、元数据管理等模块。
开发者通过云开发提供的SDK,可以在小程序中一键获取云数据库的登录态,然后将数据读写请求发送给接入层。接入层收到用户的读写请求后,由 keeper 和 agent 这两个无状态的模块对接入的读写请求进行相关处理。
其中 keeper 主要负载请求的鉴权、认证缓存,以及读写请求数的统计,是云数据库权限校验,负载均衡和计费功能实现的核心模块。
agent 模块主要有以下几个功能:1)维护接入层到底层数据库实例的连接池,通过复用已建立的连接来减少请求鉴权和连接创建的耗时;2)统计请求的并发数,对读写请求的 QPS 进行平滑处理,避免短时间的毛刺影响数据库性能和可用性;3)在热迁移切换数据库实例时,将请求挂起,切换后再将请求恢复,来实现热迁移过程对用户的全程无感知。
最后,读写请求通过了接入层,再接下来会到达存储层进行数据库实例的读写。首先,为了保证数据库的高可用,每个数据库实例都是跨机房的三副本存储,节点(机房)故障都可以自动恢复;其次,数据安全性方面,数据库副本之间通过 raft-like 的副本集协议来保证它们数据的最终一致性,数据库备份也支持用户将数据回档到任意时刻;最后,我们也充分利用云数据库的日志、监控、拨测等模块收集的信息,设计了自动化运维平台,对常见数据库异常(比如磁盘只读等)进行秒级探测和自动处理,尽可能保证用户业务平稳运行。
当然,围绕小程序云开发数据库,我们做了一系列优化,包括且不限于:全链路免鉴权、连接数控制、按量付费、跨机房容灾、自动FailOver、弹性扩缩容、数据库热迁移、智能DBA、查询优化等等。鉴于篇幅原因,就不在这里展开。
鉴于我们为数据库 API 提供了非常简单易用的封装,使得云开发数据库的使用方式非常简单,可以从小程序端或者云函数端访问。
小程序端:
// 应用 - 小程序端
const db = wx.cloud.database()
db.collection('goods')
.where({
category: 'food'
}}
.limit(100)
.get()
云函数端:// 应用 - 云函数端
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
db.collection('goods')
.where({
category: 'food'
}}
.limit(100)
.get()
下面通过一些实际例子介绍不同场景下云开发数据库的具体应用。
电商类型小程序
直接使用基础的数据库读写能力来查询商品列表:
const db = wx.cloud.database()
db.collection('goods')
.where({
category: 'food'
}}
.limit(100)
.get()
使用云数据库的聚合搜索能力对商品列表进行排序,模拟用户按"价格"、"评价"等不同纬度进行排序筛选:
const db = wx.cloud.database()
// 先根据评分降序排名,评分相同的,再根据价格升序排名
db.collection('goods')
.aggregation()
.sort({
rank: -1,
price: 1
})
.end()
对于一个订单而言,多个用户同时购买时,可以使用到云数据库的事务能力。将查询商品数量和下单放在一个事务里,保证操作的原子性。
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
db.runTransaction(async t => {
const doc = t.collection('goods').doc('apple')
const { data: price } = await doc.get()
if (apple.amount === 0) {
throw new Error('没有足够的苹果!')
}
await doc.update({
amount: apple.amount - 1
})
})
O2O类型小程序 利用云数据库提供的地理位置搜索能力来满足O2O类应用的"寻找附近的商家"之类的场景:
const _ = db.command
// 找出距离给定位置 1 公里到 5 公里范围内的记录
db.collection('restaurants')
.where({
location: _.geoNear({
geometry: db.Geo.Point(113.323809, 23.097732),
minDistance: 1000,
maxDistance: 5000
})
})
.get()
社交类型小程序
使用云数据提供的实时推送功能来轻而易举地实现一个多人实时聊天室。
const db = wx.cloud.database()
db.collection('messages')
.where({
roomId: 1234
})
.watch({
onSnapShot(docs) {
console.log(docs)
}
})
小程序云开发可以大大解放小程序开发者的生产力,降低开发的成本和难度。其中云数据库扮演了举足轻重的角色。云数据库针对小程序云开发的5大需求:安全性、易用性、低成本、高性能、灵活性,从数据库架构设计等方面做了诸多改造和优化,使得运输局可以更加贴合小程序的使用场景。
面向未来,在管控层我们也将提供更加细粒度的监控,更智能的DBA 和更高效的弹性伸缩能力;在数据库内核层,我们将封装更多的底层存储引擎能力暴露给 API 层,并深度结合小程序的使用场景来进行定制化开发,进一步提升事务的性能等等。
我们相信,云开发数据库将在 Serverless 理念的指导下不断完善自己,并帮助广大开发者共同发展。
- React Native项目实战之fetch请求并填充界面
- CocoaPods使用详解
- 2018年伊始,系统编程语言Rust为何令程序员感到兴奋?
- React Native项目实战之搭建美团个人中心界面
- React Native动画Animated详解
- iOS动画总结
- React Native之react-native-scrollable-tab-view详解
- iOS 本地轻量级数据存储之NSUserDefaults
- Android Studio配置Kotlin环境
- Android 应用安装过程分析
- React Native之TextInput组件实现联想输入
- React Native之轻量级存储AsyncStorage
- [先行者课程]--0312视差效果--课堂笔记
- Android 绿色应用公约
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Kubernetes v1.19.0 正式发布!
- 七夕节脱单“神助攻”!AI教你写情话
- 手把手教你移动端AI应用开发(三)——部署环节关键代码最详解读
- 一次完整的JVM堆外内存泄漏故障排查记录
- Python 爬虫进阶必备 | 某视频数据分析平台加密参数分析(终于我还是手把手扣了代码)
- Python 爬虫进阶必备 | 某视频平台 sign 加密参数分析
- 进击吧!Pythonista(3/100)
- begin backup导致的故障恢复全过程
- 通过历史控制文件恢复Oracle数据库,只需这10步
- python应用(2):写个python程序给自己用
- 基于Prometheus+Grafana监控SQL Server数据库
- 手把手教你用R语言读取CSV文件
- 6个案例手把手教你用Python和OpenCV进行图像处理
- Android 10(Q)/11(R) 分区存储适配
- Usual*** CMS 8.0代码审计