如何遷移到PlanetScale的無服務(wù)器數(shù)據(jù)庫?
譯文【51CTO.com快譯】作為一名開發(fā)者,我一直嘗試通過試驗(yàn)和構(gòu)建來學(xué)習(xí)新技術(shù)。我最近對無服務(wù)器數(shù)據(jù)庫頗感興趣,該技術(shù)有望加快部署、增強(qiáng)可擴(kuò)展性及改善開發(fā)者體驗(yàn)。我在測試多個(gè)產(chǎn)品后,決定將個(gè)人網(wǎng)站由使用Firebase和Redis改用PlanetScale,這是建立在MySQL和Vitess上的新的無服務(wù)器數(shù)據(jù)庫平臺(tái),是為YouTube 提供支持而開發(fā)的開源平臺(tái)。
我選擇PlanetScale的原因如下:
- 數(shù)據(jù)庫分支:我可以使用心智模型與Git相同的數(shù)據(jù)庫。每次更改我的數(shù)據(jù)庫模式,我都打開部署請求。然后,我可以將這些更改合并回到主數(shù)據(jù)庫分支中。
- Prisma支持:與Prisma結(jié)合使用,可以很輕松地處理數(shù)據(jù)庫遷移。
- 無連接:由于PlanetScale是無服務(wù)器,因此可以承受數(shù)千個(gè)同時(shí)連接。幾乎可以把這認(rèn)為是無連接,因?yàn)槲也恍枰獡?dān)心池化或其他常見的反對意見。
- 10秒內(nèi)部署:我在測試13個(gè)數(shù)據(jù)庫后,發(fā)現(xiàn)PlanetScale部署速度最快。使用它幾周并監(jiān)控性能后,我看到API平均在大約150ms內(nèi)解析(見下面的結(jié)果)。
Firebase和Redis
我的網(wǎng)站以前使用Google Firebase和Redis用于實(shí)時(shí)博文閱讀量和留言簿。決定選擇這些技術(shù)主要是想學(xué)習(xí)一下。Firebase和Redis(借助Upstash)都很易于上手,無需思考即可擴(kuò)展,在無服務(wù)器環(huán)境下運(yùn)行順暢。但是我想轉(zhuǎn)而使用基于SQL的數(shù)據(jù)庫(MySQL或PostgreSQL),以獲得新的學(xué)習(xí)體驗(yàn)。
重建SQL
我發(fā)現(xiàn)自己在SQL有內(nèi)置功能的地方編寫JavaScript。比如說,我使用Firebase獲取 JSON對象閱讀量,然后將這些值相加以找到總的閱讀量。
- const snapshot = await db.ref('views').once('value');
- const views = snapshot.val();
- const allViews = Object.values(views).reduce((total, value) => total + value);
- With SQL, you can use SUM() instead.
- SELECT SUM(count) as total
- FROM views;
排序同樣如此。以前我使用JavaScript sort,現(xiàn)在使用ORDER BY。雖然Firebase確實(shí)有類似功能,但我并不使用它。
- SELECT * FROM guestbook
- ORDER BY updated_at DESC;
SQL是成熟的技術(shù)。它已存在多年,會(huì)繼續(xù)存在多年。我在以前的工作中用過它,但仍覺得可以更深入地理解它。我也喜歡使用PostgreSQL(推薦Supabase),強(qiáng)烈推薦考慮該解決方案。
我還堅(jiān)信使用自己推薦的工具。如果我沒有實(shí)際動(dòng)手編寫代碼、在生產(chǎn)環(huán)境中運(yùn)行應(yīng)用程序,就很難有把握地向別人推薦產(chǎn)品。我使用PlanetScale Vercel Integration后,大為驚喜。只需點(diǎn)擊幾下鼠標(biāo),我就可以部署整個(gè)全棧應(yīng)用程序。正如開頭提到的,數(shù)據(jù)庫遷移與我的心智模型相一致。
遷移現(xiàn)有數(shù)據(jù)
可能有更好的方法來遷移,但我將數(shù)據(jù)遷移到PlanetScale的自創(chuàng)解決方案如下:
- 從我的Firebase實(shí)時(shí)數(shù)據(jù)庫導(dǎo)出JSON數(shù)據(jù)
- 使用HVALS和TablePlus,從我的Redis集群導(dǎo)出JSON數(shù)據(jù)
- 在PlanetScale中創(chuàng)建新的數(shù)據(jù)庫分支,用于添加表的模式遷移
- 創(chuàng)建兩個(gè)新的API路由,負(fù)責(zé)加載JSON數(shù)據(jù)和INSERT INTO(插入到)MySQL
- 在數(shù)據(jù)庫分支上驗(yàn)證API正確處理和遷移數(shù)據(jù)
- 創(chuàng)建擁有新模式更改的部署請求,并將其合并到main中
- 最后,點(diǎn)擊API,將JSON數(shù)據(jù)遷移到main
- 完畢!
下面是我使用的兩個(gè)腳本,供參考。
- import db from 'lib/planetscale';
- import guestbookData from 'data/guestbook';
- export default async function handler(req, res) {
- const toISOString = (unixTimestampInMs) =>
- new Date(unixTimestampInMs).toJSON().slice(0, 19).replace('T', ' ');
- let query = `INSERT INTO guestbook (email, updated_at, body, created_by)
- VALUES `;
- const escapeStr = (str) =>
- str
- .replace(/\\/g, '\\\\')
- .replace(/\$/g, '\\$')
- .replace(/'/g, "\\'")
- .replace(/"/g, '\\"');
- guestbookData.forEach((item, key) => {
- var value = JSON.parse(item['value']);
- query += `("${
- value.email ? `${value.email}` : 'not@provided.com'
- }", "${toISOString(value.updated_at)}", "${escapeStr(value.body)}", "${
- value.created_by
- }")`;
- if (key === guestbookData.length - 1) {
- query += ';';
- } else {
- query += ', ';
- }
- });
- const [rows] = await db.query(query);
- return res.status(201).json(rows[0]);
- }
- import db from 'lib/planetscale';
- import viewsData from 'data/views';
- export default async function handler(req, res) {
- let query = `INSERT INTO views (slug, count)
- VALUES `;
- const slugs = Object.keys(viewsData['views']);
- slugs.forEach((slug, key) => {
- const count = viewsData['views'][slug];
- query += `("${slug}", ${count})`;
- if (key === slugs.length - 1) {
- query += ';';
- } else {
- query += ', ';
- }
- });
- const [rows] = await db.query(query);
- return res.status(201).json(rows[0]);
- }
以下是我的PlanetScale模式,用于跟蹤博文閱讀量和留言簿留言。
- CREATE TABLE `views` (
- `slug` varchar(128) NOT NULL,
- `count` bigint NOT NULL DEFAULT '1',
- PRIMARY KEY (`slug`)
- )
- CREATE TABLE `guestbook` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `email` varchar(256) NOT NULL,
- `body` varchar(500) NOT NULL,
- `created_by` varchar(256) NOT NULL,
- `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
- `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
- PRIMARY KEY (`id`)
- )
結(jié)果
我一直使用Checkly來監(jiān)控連接到PlanetScale的生產(chǎn)級API的性能。Checkly讓我可以設(shè)置警報(bào),那樣出現(xiàn)停運(yùn)或性能降到接受的閾值以下時(shí)發(fā)出警報(bào)。迄今為止,我發(fā)現(xiàn)我的Next.js API Routes在us-east的Vercel上部署為無服務(wù)器函數(shù)時(shí)延遲約150ms。
PlanetScale性能小結(jié)
如下圖所示,與我之前的Firebase實(shí)現(xiàn)相比,響應(yīng)時(shí)間顯著加快(請注意我何時(shí)進(jìn)行切換)。 此外,擁有一項(xiàng)而不是兩項(xiàng)服務(wù)可以清理代碼,需要較少的環(huán)境變量即可連接到每項(xiàng)服務(wù)。
原文標(biāo)題:How to Migrate to PlanetScale’s Serverless Database,作者:Lee Robinson
【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】