Neon 是一个现代化的 Serverless PostgreSQL 数据库服务,提供自动扩缩容、按需计费和分支管理等特性。它将传统 Postgres 的强大功能与云原生架构结合,让开发者能够像使用无服务器函数一样使用数据库。本文将介绍 Neon 的核心概念、使用方法和实际应用场景。
什么是 Neon
Neon 是一个完全托管的 Serverless PostgreSQL 服务,采用存储与计算分离的架构,为开发者提供按需付费、自动扩缩容和分支管理等现代化特性。
核心特性
Serverless 架构
Neon 采用 Serverless 设计,数据库实例可以在无请求时自动暂停,有请求时快速恢复。这种设计大幅降低了空闲时的成本,开发者只需为实际使用的计算资源付费。
typescript
// 连接 Neon 数据库,即使实例处于暂停状态
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({
connectionString: process.env.DATABASE_URL, // Neon 会自动唤醒实例
});
const result = await pool.query("SELECT NOW()");
console.log(result.rows[0]); // 首次查询可能需要几秒钟唤醒
自动扩缩容
Neon 支持根据负载自动调整计算资源。在流量高峰时自动扩容,流量降低时自动缩容,无需手动干预。
分支管理
Neon 支持为数据库创建分支(Branch),类似 Git 分支管理代码。每个分支都是独立的数据库实例,可以用于开发、测试或预览环境。
bash
# 使用 Neon CLI 创建分支
neonctl branches create --project-id my-project --name feature-branch
# 每个分支有独立的连接字符串
postgresql://user:pass@feature-branch-ep-xxx.us-east-2.aws.neon.tech/main
技术架构
Neon 采用存储与计算分离(Storage-Compute Separation)架构:
- 存储层:基于云对象存储,提供持久化和高可用性
- 计算层:运行 PostgreSQL 实例,负责查询处理
- 代理层:管理连接、路由请求、实现自动唤醒
这种架构使得计算资源可以独立扩缩容,而存储容量按需增长。
与传统 PostgreSQL 对比
| 特性 | 传统 PostgreSQL | Neon |
|---|---|---|
| 部署方式 | 需要自行管理服务器 | 完全托管的 Serverless |
| 扩容方式 | 手动垂直扩容,需停机 | 自动扩缩容,无停机 |
| 成本模式 | 固定成本,即使空闲也计费 | 按需计费,空闲时自动暂停 |
| 分支管理 | 需手动备份恢复 | 内置分支功能,秒级创建 |
| 存储容量 | 受限于磁盘大小 | 弹性存储,按需增长 |
| 兼容性 | 原生 PostgreSQL | 完全兼容 PostgreSQL |
快速开始
本章介绍如何快速上手 Neon,从注册账号到执行第一个查询。
创建项目
- 访问 Neon Console 并注册账号(支持 GitHub、Google 登录)
- 点击 "Create a project" 创建新项目
- 选择区域(Region)和 PostgreSQL 版本
- 系统会自动创建一个默认数据库
main
获取连接字符串
创建项目后,在项目面板中可以看到连接字符串:
bash
# 连接字符串格式
postgresql://username:password@ep-xxx.region.aws.neon.tech/dbname?sslmode=require
连接字符串包含以下信息:
- username:数据库用户名
- password:自动生成的密码
- ep-xxx:Endpoint ID(计算实例标识)
- region:数据中心区域
- dbname:数据库名称
连接数据库
使用 Node.js
typescript
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
async function main() {
const client = await pool.connect();
try {
// 创建表
await client.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100) UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
)
`);
// 插入数据
await client.query("INSERT INTO users (name, email) VALUES ($1, $2)", ["Alice", "alice@example.com"]);
// 查询数据
const result = await client.query("SELECT * FROM users");
console.log(result.rows);
} finally {
client.release();
}
}
main();
使用 psql 命令行
bash
# 使用 psql 连接
psql "postgresql://username:password@ep-xxx.region.aws.neon.tech/main?sslmode=require"
# 执行查询
main=> SELECT version();
main=> \dt -- 列出所有表
使用其他客户端
Neon 完全兼容 PostgreSQL,可以使用任何支持 PostgreSQL 的客户端工具:
- GUI 工具:pgAdmin, DBeaver, TablePlus
- ORM:Prisma, TypeORM, Sequelize, Drizzle
- 语言驱动 :
pg(Node.js),psycopg2(Python),pgx(Go)
核心功能
Neon 提供了分支管理、自动扩缩容、自动暂停和时间点恢复等核心功能,让数据库管理更加灵活高效。
分支管理
Neon 的分支(Branch)功能类似 Git,可以为数据库创建多个独立的副本。每个分支都是完整的数据库实例,拥有独立的连接字符串和计算资源。
创建分支
bash
# 从主分支创建新分支
neonctl branches create \
--project-id my-project \
--name dev-feature-x
# 从指定分支创建
neonctl branches create \
--project-id my-project \
--parent main \
--name staging
使用场景
- 功能开发:每个功能分支对应一个数据库分支,避免开发环境相互干扰
- 测试环境:为测试创建独立分支,测试完成后直接删除
- 数据探索:复制生产数据到分支进行分析,不影响生产环境
创建分支的速度极快(通常几秒钟),因为 Neon 采用写时复制(Copy-on-Write)技术,只在数据修改时才真正复制数据。
自动扩缩容
Neon 支持根据工作负载自动调整计算资源。可以设置最小和最大 CPU 单位(CU),Neon 会在这个范围内动态调整。
typescript
// 通过 API 配置自动扩缩容
import { createClient } from "@neondatabase/api-client";
const client = createClient({ apiKey: process.env.NEON_API_KEY });
await client.updateProject({
projectId: "my-project",
project: {
settings: {
// 设置 0.25 到 2 CU 的自动扩缩容范围
autoscaling_limit_min_cu: 0.25,
autoscaling_limit_max_cu: 2,
},
},
});
工作机制
- 流量增加时,Neon 自动增加 CPU 和内存资源
- 流量降低时,自动缩减资源以节省成本
- 扩缩容过程对应用透明,不会中断连接
自动暂停
当数据库在配置的时间内没有活动时,Neon 会自动暂停计算实例。暂停后不再计费,但数据仍然保留在存储层。
typescript
// 配置自动暂停延迟(单位:秒)
await client.updateProject({
projectId: "my-project",
project: {
settings: {
suspend_timeout_seconds: 300, // 5 分钟无活动后暂停
},
},
});
自动唤醒
当有新的连接请求时,Neon 会自动唤醒暂停的实例:
typescript
// 连接暂停的数据库,会触发自动唤醒
const client = await pool.connect();
// 首次查询可能需要几秒钟等待唤醒
const result = await client.query("SELECT NOW()");
// 后续查询正常执行
唤醒时间通常在 1-3 秒,对于大多数应用场景可以接受。
时间点恢复
Neon 支持时间点恢复(Point-in-Time Recovery, PITR),可以将数据库恢复到过去某个时刻的状态。
bash
# 恢复到指定时间点(创建新分支)
neonctl branches create \
--project-id my-project \
--name recovery-branch \
--timestamp "2024-12-08T10:30:00Z"
应用场景
- 误操作恢复:执行了错误的 DELETE 或 UPDATE 操作,可以恢复到操作前的状态
- 数据审计:查看某个时间点的历史数据
- 回滚测试:测试环境出问题时,快速回滚到之前的状态
保留期限取决于订阅计划(免费版保留 7 天,付费版可达 30 天)。
使用 API Client
@neondatabase/api-client 是 Neon 官方提供的 TypeScript/JavaScript SDK,用于通过代码管理 Neon 项目、数据库、分支等资源。
安装和配置
安装依赖
bash
npm install @neondatabase/api-client
# 或
pnpm add @neondatabase/api-client
获取 API Key
- 登录 Neon Console
- 进入 "Account settings" → "API keys"
- 点击 "Generate new API key" 创建密钥
- 保存密钥到环境变量
bash
# .env
NEON_API_KEY=your_api_key_here
初始化客户端
typescript
import { createClient } from "@neondatabase/api-client";
const client = createClient({
apiKey: process.env.NEON_API_KEY,
});
常用操作
列出所有项目
typescript
async function listProjects() {
const response = await client.listProjects();
response.projects.forEach((project) => {
console.log(`ID: ${project.id}`);
console.log(`Name: ${project.name}`);
console.log(`Region: ${project.region_id}`);
});
}
创建新项目
typescript
async function createProject() {
const project = await client.createProject({
project: {
name: "my-new-project",
region_id: "aws-us-east-2", // 选择区域
pg_version: 16, // PostgreSQL 版本
},
});
console.log("Project created:", project.id);
console.log("Connection string:", project.connection_uris[0].connection_uri);
}
管理分支
typescript
async function manageBranches(projectId: string) {
// 列出所有分支
const branches = await client.listProjectBranches({ projectId });
// 创建新分支
const newBranch = await client.createProjectBranch({
projectId,
branch: {
name: "feature-branch",
parent_id: branches.branches[0].id, // 从主分支创建
},
});
console.log("Branch created:", newBranch.branch.id);
// 删除分支
await client.deleteProjectBranch({
projectId,
branchId: newBranch.branch.id,
});
}
获取项目详情
typescript
async function getProjectInfo(projectId: string) {
const project = await client.getProject({ projectId });
console.log("Project info:", {
name: project.project.name,
region: project.project.region_id,
createdAt: project.project.created_at,
settings: project.project.settings,
});
}
管理 Endpoints
Endpoint 是 Neon 中的计算实例,每个分支可以有多个 Endpoint。
typescript
async function manageEndpoints(projectId: string, branchId: string) {
// 列出分支的所有 Endpoints
const endpoints = await client.listProjectBranchEndpoints({
projectId,
branchId,
});
// 更新 Endpoint 配置
await client.updateProjectEndpoint({
projectId,
endpointId: endpoints.endpoints[0].id,
endpoint: {
autoscaling_limit_min_cu: 0.25,
autoscaling_limit_max_cu: 2,
suspend_timeout_seconds: 300,
},
});
}
实际应用场景
Neon 的 Serverless 特性和分支管理功能,使其在多种场景下都能发挥独特优势。
开发环境:独立数据库分支
传统开发中,多个开发者共享同一个开发数据库容易产生冲突。使用 Neon,可以为每个开发者创建独立的数据库分支。
typescript
// 脚本:为开发者创建个人分支
import { createClient } from "@neondatabase/api-client";
async function setupDeveloperBranch(developerName: string) {
const client = createClient({
apiKey: process.env.NEON_API_KEY!,
});
// 从主分支创建开发者分支
const branch = await client.createProjectBranch({
projectId: process.env.NEON_PROJECT_ID!,
branch: {
name: `dev-${developerName}`,
parent_id: mainBranchId,
},
});
console.log(`Branch for ${developerName}:`, branch.endpoints[0].host);
return branch;
}
// 开发者 Alice 获得独立分支
await setupDeveloperBranch("alice");
优势
- 每个开发者有独立的数据库环境,互不干扰
- 可以自由修改 schema 和数据,不影响他人
- 创建和删除分支成本极低
预览部署:PR 临时数据库
在 CI/CD 流程中,为每个 Pull Request 创建临时数据库分支,实现真正的预览环境。
typescript
// GitHub Actions 工作流示例
// .github/workflows/preview.yml
async function handlePullRequest(prNumber: number, action: "opened" | "closed") {
const client = createClient({ apiKey: process.env.NEON_API_KEY! });
if (action === "opened") {
// PR 创建时,创建数据库分支
const branch = await client.createProjectBranch({
projectId: process.env.NEON_PROJECT_ID!,
branch: {
name: `pr-${prNumber}`,
parent_id: mainBranchId,
},
});
// 将连接字符串注入到预览部署
const connectionString = branch.connection_uris[0].connection_uri;
await deployPreview(prNumber, connectionString);
// 在 PR 中添加评论
await addCommentToPR(prNumber, `Preview deployed with database branch: ${branch.id}`);
} else if (action === "closed") {
// PR 合并或关闭时,删除分支
const branches = await client.listProjectBranches({
projectId: process.env.NEON_PROJECT_ID!,
});
const prBranch = branches.branches.find((b) => b.name === `pr-${prNumber}`);
if (prBranch) {
await client.deleteProjectBranch({
projectId: process.env.NEON_PROJECT_ID!,
branchId: prBranch.id,
});
}
}
}
优势
- 每个 PR 都有独立的预览环境和数据库
- 自动化创建和清理,无需手动管理
- 数据隔离,测试互不影响
生产环境:弹性应对流量波动
对于流量有明显峰谷的应用,Neon 的自动扩缩容可以显著降低成本。
typescript
// 配置生产环境的自动扩缩容
async function configureProduction() {
const client = createClient({ apiKey: process.env.NEON_API_KEY! });
await client.updateProjectEndpoint({
projectId: process.env.NEON_PROJECT_ID!,
endpointId: productionEndpointId,
endpoint: {
// 低流量时使用 0.5 CU
autoscaling_limit_min_cu: 0.5,
// 高峰时最多扩展到 4 CU
autoscaling_limit_max_cu: 4,
// 30 分钟无活动后暂停(适用于非 24/7 应用)
suspend_timeout_seconds: 1800,
},
});
}
适用场景
- 内部工具:工作时间访问频繁,下班后自动暂停
- 营销活动:活动期间流量激增,自动扩容应对
- 定时任务:只在特定时间运行,其他时间暂停节省成本
测试环境:快速复制生产数据
使用时间点恢复功能,快速复制生产数据到测试环境进行调试和验证。
bash
# 从生产分支的最新状态创建测试分支
neonctl branches create \
--project-id my-project \
--parent production \
--name test-reproduce-bug
# 从昨天的数据创建分支,用于复现历史问题
neonctl branches create \
--project-id my-project \
--parent production \
--timestamp "2024-12-07T10:00:00Z" \
--name test-yesterday-data
使用场景
- Bug 复现:使用生产数据在测试环境中复现问题
- 性能测试:使用真实数据规模测试查询性能
- 数据迁移:在测试分支上验证迁移脚本,确认无误后应用到生产
安全提示
如果生产数据包含敏感信息,应在测试分支上进行脱敏处理:
sql
-- 在测试分支上脱敏用户数据
UPDATE users
SET email = CONCAT('user', id, '@example.com'),
phone = NULL,
address = 'Test Address';
多区域部署
Neon 支持在多个云区域创建项目,实现地理分布式部署。
typescript
// 为不同地区的用户创建区域化数据库
async function createRegionalProjects() {
const client = createClient({ apiKey: process.env.NEON_API_KEY! });
const regions = [
{ name: "us-east", region_id: "aws-us-east-2" },
{ name: "eu-west", region_id: "aws-eu-west-1" },
{ name: "ap-south", region_id: "aws-ap-southeast-1" },
];
for (const region of regions) {
await client.createProject({
project: {
name: `myapp-${region.name}`,
region_id: region.region_id,
pg_version: 16,
},
});
}
}
优势
- 降低延迟,用户连接到最近的数据中心
- 满足数据合规要求(如 GDPR 要求数据存储在欧盟)
- 提高可用性,某个区域故障时可切换到其他区域