數據是無價的,咱們一般會把重要的業務數據存放在數據庫中,並須要對數據庫作定時的自動備份工做,防止數據異常丟失,形成沒法挽回的損失。html
小程序雲開發提供了方便的雲數據庫供咱們直接使用,雲開發使用了騰訊雲提供的雲數據庫,擁有完善的數據保障機制,無需擔憂數據丟失。可是,咱們仍是不可避免的會擔憂數據庫中數據的安全,好比不當心刪除了數據集合,寫入了髒數據等。shell
還好,雲開發控制檯提供了數據集合的導出,導入功能,咱們能夠手動備份數據庫。不過,老是手動備份數據庫也太麻煩了點,全部重複的事情都應該讓代碼去解決,下面咱們就說說怎麼搞定雲開發數據庫自動備份。數據庫
經過查閱微信的文檔,能夠發現雲開發提供了數據導出接口databaseMigrateExportjson
POST https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=ACCESS_TOKEN
複製代碼
經過這個接口,結合雲函數的定時觸發功能,咱們就能夠作數據庫定時自動備份了。梳理一下大體的流程:小程序
調用微信的接口須要 access_token,因此咱們首先要獲取 access_token。經過文檔瞭解到使用 auth.getAccessToken 接口能夠用小程序的 appid 和 secret 獲取 access_token。api
// 獲取 access_token
request.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`,
(err, res, body) => {
if (err) {
// 處理錯誤
return;
}
const data = JSON.parse(body);
// data.access_token
}
);
複製代碼
獲取 access_token 後,就可使用 databaseMigrateExport
接口導出數據進行備份。安全
databaseMigrateExport
接口會建立一個數據庫導出任務,並返回一個 job_id,這個 job_id 怎麼用咱們下面再說。顯然數據庫的數據導出並非同步的,而是須要必定時間的,數據量越大導出所要花費的時間就越多,我的實測,2W 條記錄,2M 大小,導出大概須要 3~5 S。微信
調用 databaseMigrateExport
接口須要傳入環境 Id,存儲文件路徑,導出文件類型(1 爲 JSON,2 爲 CSV),以及一個 query 查詢語句。app
由於咱們是作數據庫備份,因此這裏就導出 JSON 類型的數據,兼容性更好。須要備份的數據能夠用 query 來約束,這裏仍是很靈活的,既能夠是整個集合的數據,也能夠是指定的部分數據,這裏咱們就使用 db.collection('data').get()
備份 data 集合的所有數據。同時咱們使用當前時間做爲文件名,方便之後使用時查找。async
request.post(
`https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${accessToken}`,
{
body: JSON.stringify({
env,
file_path: `${date}.json`,
file_type: '1',
query: 'db.collection("data").get()'
})
},
(err, res, body) => {
if (err) {
// 處理錯誤
return;
}
const data = JSON.parse(body);
// data.job_id
}
);
複製代碼
在建立號數據庫導出任務後,咱們會獲得一個 job_id,若是導出集合比較大,就會花費較長時間,這時咱們可使用 databaseMigrateQueryInfo 接口查詢數據庫導出的進度。
當導出完成後,會返回一個 file_url
,便可如下載數據庫導出文件的臨時連接。
request.post(
`https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${accessToken}`,
{
body: JSON.stringify({
env,
job_id: jobId
})
},
(err, res, body) => {
if (err) {
reject(err);
}
const data = JSON.parse(body);
// data.file_url
}
);
複製代碼
獲取到文件下載連接以後,咱們能夠將文件下載下來,存入到本身的雲存儲中,作備份使用。若是不須要長時間的保留備份,就能夠不用下載文件,只須要將 job_id 存儲起來,當須要恢復備份的時候,經過 job_id 查詢到新的連接,下載數據恢復便可。
至於 job_id 存在哪,就看我的想法了,這裏就選擇存放在數據庫裏。
await db.collection('db_back_info').add({
data: {
date: new Date(),
jobId: job_id
}
});
複製代碼
雲函數支持定時觸發器,能夠按照設定的時間自動執行。雲開發的定時觸發器採用的 Cron
表達式語法,最大精度能夠作的秒級,詳細的使用方法能夠參考官方文檔:定時觸發器 | 微信開放文檔
這裏咱們配置函數天天凌晨 2 點觸發,這樣就能夠天天都對數據庫進行備份。在雲函數目錄下新建 config.json
文件,寫入以下內容:
{
"triggers": [
{
"name": "dbTrigger",
"type": "timer",
"config": "0 0 2 * * * *"
}
]
}
複製代碼
最後,貼出能夠在雲函數中使用的完整代碼,只須要建立一個定時觸發的雲函數,並設置好相關的環境變量便可使用
注意,雲函數的默認超時時間是 3 秒,建立備份函數時,建議將超時時間設定到最大值 20S,留有足夠的時間查詢任務結果。
/* eslint-disable */
const request = require('request');
const cloud = require('wx-server-sdk');
// 環境變量
const env = 'xxxx';
cloud.init({
env
});
// 換取 access_token
async function getAccessToken(appid, secret) {
return new Promise((resolve, reject) => {
request.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`,
(err, res, body) => {
if (err) {
reject(err);
return;
}
resolve(JSON.parse(body));
}
);
});
}
// 建立導出任務
async function createExportJob(accessToken, collection) {
const date = new Date().toISOString();
return new Promise((resolve, reject) => {
request.post(
`https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${accessToken}`,
{
body: JSON.stringify({
env,
file_path: `${date}.json`,
file_type: '1',
query: `db.collection("${collection}").get()`
})
},
(err, res, body) => {
if (err) {
reject(err);
}
resolve(JSON.parse(body));
}
);
});
}
// 查詢導出任務狀態
async function waitJobFinished(accessToken, jobId) {
return new Promise((resolve, reject) => {
// 輪訓任務狀態
const timer = setInterval(() => {
request.post(
`https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${accessToken}`,
{
body: JSON.stringify({
env,
job_id: jobId
})
},
(err, res, body) => {
if (err) {
reject(err);
}
const { status, file_url } = JSON.parse(body);
console.log('查詢');
if (status === 'success') {
clearInterval(timer);
resolve(file_url);
}
}
);
}, 500);
});
}
exports.main = async (event, context) => {
// 從雲函數環境變量中讀取 appid 和 secret 以及數據集合
const { appid, secret, backupColl, backupInfoColl } = process.env;
const db = cloud.database();
try {
// 獲取 access_token
const { errmsg, access_token } = await getAccessToken(appid, secret);
if (errmsg && errcode !== 0) {
throw new Error(`獲取 access_token 失敗:${errmsg}` || '獲取 access_token 爲空');
}
// 導出數據庫
const { errmsg: jobErrMsg, errcode: jobErrCode, job_id } = await createExportJob(access_token, backupColl);
// 打印到日誌中
console.log(job_id);
if (jobErrCode !== 0) {
throw new Error(`建立數據庫備份任務失敗:${jobErrMsg}`);
}
// 將任務數據存入數據庫
const res = await db.collection('db_back_info').add({
data: {
date: new Date(),
jobId: job_id
}
});
// 等待任務完成
const fileUrl = await waitJobFinished(access_token, job_id);
console.log('導出成功', fileUrl);
// 存儲到數據庫
await db
.collection(backupInfoColl)
.doc(res._id)
.update({
data: {
fileUrl
}
});
} catch (e) {
throw new Error(`導出數據庫異常:${e.message}`);
}
};
複製代碼
若是你想要了解更多關於雲開發CloudBase相關的技術故事/技術實戰經驗,請掃碼關注【騰訊云云開發】公衆號~