长期免费的数据库方案------零成本基建系列
做完前面几篇零成本 API 的整理后,数据库这一块花了更久。原因很简单:很多"免费数据库"的介绍只写了注册时送多少额度,却没写清楚额度会不会重置、闲置会不会暂停,或者把缓存服务也算成数据库。
| 服务 | 类型 | 当前免费规则 | 这次测试 |
|---|---|---|---|
| Cloudflare D1 | SQLite / SQL | 5 GB,总计每天 500 万行读取、10 万行写入 | 完整 CRUD 通过 |
| Supabase | PostgreSQL | 500 MB 数据库、2 个活跃项目 | 本机完整 CRUD 通过 |
| MongoDB Atlas M0 | 文档数据库 | 512 MB、共享实例 | 本机完整 CRUD 通过 |
| Firebase Firestore | 文档数据库 | 1 GiB、每天 5 万读 / 2 万写 / 2 万删 | 完整 CRUD 通过 |
| TiDB Cloud Starter | MySQL 兼容 | 前 5 个实例各有 5 GiB 行存储和每月 5000 万 RU | 能连接,但默认用户无建表权限 |
这里的"长期免费"指的是服务商目前有可反复使用的免费档,不是承诺永远不改价。个人博客、小工具、原型验证先用这些方案问题不大;业务跑起来以后,还是要盯一下控制台里的用量和邮件。
先按数据长什么样来选
如果数据是用户、订单、文章、权限这类有明确关系的东西,优先从 SQL 数据库里挑。以后要补查询、统计和关联,省得把原本简单的关系拆成一堆文档。
MongoDB Atlas 和 Firestore 更适合结构变化频繁的文档数据,例如聊天记录、表单内容、设备状态。Firestore 还和 Firebase Auth、移动端 SDK 接得很近;已经在 Android 或 Firebase 生态里做东西时,会省掉不少接线工作。
Redis 也常出现在免费数据库清单里。Upstash Redis 的免费档目前是 1 个库、256 MB、每月 50 万命令。它适合缓存、限流、短期状态,不能替代上面这些用来长期存业务数据的库,所以没有把它放进正文的主选项。
Cloudflare D1:已经在 Cloudflare 上时,少折腾一套账号
D1 是 SQLite,和 Cloudflare Workers、Pages Functions 放在同一个体系里。免费计划现在给 5 GB 总存储,每天 500 万行读取和 10 万行写入;流量不收费,配额在 UTC 零点重置。D1 定价说明 写得比较细。


我创建了一个临时数据库,依次执行建表、插入、查询、更新、删除,全部返回正常后立刻删库。D1 不提供给普通 Node 程序直接连的 TCP 地址,它更适合把查询放在 Worker 或 Pages Function 里:
js
export default {
async fetch(request, env) {
await env.DB.prepare(
'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)'
).run();
await env.DB.prepare('INSERT INTO notes (body) VALUES (?)')
.bind('D1 CRUD test')
.run();
const { results } = await env.DB.prepare('SELECT * FROM notes').all();
return Response.json(results);
}
};
已经在用 Cloudflare Pages 的网站,后面真要加一个留言、订阅或访问统计的小接口,D1 是我会先试的选项。数据量很大、查询很重,或者程序根本不跑在 Cloudflare 上,就别为了 D1 绕路。看过前面几期使用过Cloudflare并创建过token的可以在原来的token上增加D1的权限来复用这个token。

PostgreSQL:Supabase 能直接用
Supabase 免费项目给 500 MB PostgreSQL、共享 CPU、5 GB 出站流量和 2 个活跃项目;一周没有活动的免费项目可能会暂停,免费档也没有自动备份。Supabase 价格页 的限制比较直白。注册的死后注意选择Free。


这次用 Node 的 pg 连 Supabase,完整 CRUD 在本机 PowerShell 里通过。中间碰到过一个容易误判成网络故障的问题:项目直连地址是 IPv6 优先的,在没有 IPv6 的网络里会超时。换成控制台给的 Session Pooler(5432 端口)后才正常。官方的连接说明也把这两种连接方式分开写了。
js
import pg from 'pg';
const client = new pg.Client({ connectionString: process.env.SUPABASE_DATABASE_URL });
await client.connect();
await client.query('CREATE TABLE IF NOT EXISTS codex_db_test (id SERIAL PRIMARY KEY, body TEXT)');
const { rows: [note] } = await client.query(
'INSERT INTO codex_db_test (body) VALUES ($1) RETURNING id',
['Supabase CRUD test']
);
await client.query('SELECT body FROM codex_db_test WHERE id = $1', [note.id]);
await client.query('UPDATE codex_db_test SET body = $1 WHERE id = $2', ['updated', note.id]);
await client.query('DROP TABLE codex_db_test');
await client.end();
MongoDB Atlas 和 Firestore:文档数据各有各的顺手处
MongoDB Atlas 的 M0 仍是免费共享集群,容量 512 MB,最高 100 次操作/秒。连续 30 天没有连接的免费集群会自动暂停,需要时再恢复。Atlas 定价页和闲置集群说明都提到了这一点。注册创建相对简单,就不赘述了。

测试时用了独立集合,插入文档后做了查询、更新、删除,最后把集合删除。本机的结果是通过的:
js
import { MongoClient } from 'mongodb';
const client = new MongoClient(process.env.MONGODB_URI);
await client.connect();
const collection = client.db('codex_test').collection(`crud_${Date.now()}`);
await collection.insertOne({ body: 'MongoDB CRUD test', version: 1 });
await collection.findOne({ version: 1 });
await collection.updateOne({ version: 1 }, { $set: { version: 2 } });
await collection.deleteOne({ version: 2 });
await collection.drop();
await client.close();
Firestore 的免费额度是 1 GiB 存储、每天 5 万次读、2 万次写、2 万次删,每个项目只能有一个免费数据库。它的计量日按太平洋时间过零;如果打开 TTL、PITR 或备份等功能,则需要启用计费。Firestore 价格页把这些边界写得很清楚。



我用服务账号通过 Admin SDK 写入和删除了一个临时文档,CRUD 通过。下面这段是最小的写入、读取和删除:
js
import { cert, initializeApp } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
const app = initializeApp({ credential: cert(JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT_JSON)) });
const db = getFirestore(app);
const ref = db.collection('codex_tests').doc(`crud_${Date.now()}`);
await ref.set({ body: 'Firestore CRUD test', version: 1 });
await ref.get();
await ref.update({ version: 2 });
await ref.delete();
TiDB Cloud Starter:连上了,卡在权限上
TiDB Cloud 的免费 Starter 计划在 2025 年已经改名为 Serverless。一个组织前 5 个 Starter 实例各有 5 GiB 行存储、5 GiB 列存储和每月 5000 万 RU;额度用完且消费上限为 0 时,连接会被拒绝或限流,等下个自然月恢复,不会直接自动扣费。Starter FAQ有完整说明。

注册创建相对简单,注意选择Starter。


它用的是 MySQL 协议,公网连接要求 TLS。我这次的连接、TLS 和认证都成功了,真正执行 CREATE TABLE 时返回了下面这个错误:
text
CREATE command denied to user '3z7**********y71.root'@'%'
也就是说,问题不在连接串或防火墙,而是默认 <prefix>.root 用户没有建表权限。控制台里没看到 SQL Users 也不是漏找菜单:这个页面目前还是 public preview,要从右下角 ? 进入 Support Tickets 申请开通;默认 root 用户的角色不能修改。细节见 SQL Users 文档。
js
import mysql from 'mysql2/promise';
const connection = await mysql.createConnection({
uri: process.env.TIDB_CONNECTION_URL,
ssl: { rejectUnauthorized: true }
});
await connection.query('SELECT 1'); // 这一步通过
await connection.query('CREATE TABLE codex_db_test (id INT PRIMARY KEY)'); // 默认用户在这里被拒绝
await connection.end();
TiDB 的 MySQL 兼容性对已有项目很友好,但这次的结果不能写成"CRUD 已通过"。等 SQL Users 申请下来,创建一个有 DDL 权限的测试用户,再补一次完整测试。注意实际使用时公网白名单不要长期放 0.0.0.0/0,测试结束后应收回到自己的出口 IP。
怎么选
要给 Cloudflare 上的小功能加数据,选 D1;需要 PostgreSQL 又想顺手把登录、文件存储一起处理,选 Supabase;移动端项目已经在 Firebase 里,Firestore 更自然。MongoDB Atlas 适合明确想用文档模型的场景。免费额度能让人少花一笔钱,注意做好数据备份,切用且珍惜!
如果你觉得文章对你有用,可以关注我获取更多的零成本基建资讯。