數據是無價的,咱們一般會把重要的業務數據存放在數據庫中,並須要對數據庫作定時的自動備份工做,防止數據異常丟失,形成沒法挽回的損失。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相關的技術故事/技術實戰經驗,請掃碼關注【騰訊云云開發】公衆號~