文档 / 搜索 / LingCode Cloud / 最佳实践
📘 参考 ● 生产环境指南 更新于 2026-06-11

最佳实践与生产环境指南

一句话:LingCode Cloud 原型迭代很快,在开发期也很宽容——但有几个习惯,决定了你做出来的是一个 demo,还是一个能真正上线的后端。按「一次读一张表」来做数据建模,分页与计数都别全表扫描,为你用来过滤的列加索引,在 anon key 能触达的每个地方打开行级安全,把私有密钥放进保险库、把服务端逻辑放进函数,并守住所在套餐的配额。然后跑一遍上线前检查清单

这些规则大多源自同一个事实:客户端 SDK 对话的是一层轻量的表级 CRUD 网关,而不是直接连 Postgres。正是这一点,决定了你该如何建模、如何读取,以及安全该放在哪里。下面每一节都是一段简短的为什么,加上具体的操作指引,并附上相关指南链接,方便你深入。

1. 按表级 CRUD 建模

运行时数据 API 一次只操作一张表——不支持 JOIN,不支持 upsert,每页最多 200 行。如果你设计了一套规范化的 schema,再试图在客户端用扇出请求把它拼回去,那么每个界面都会因为每个关联而多花一次往返。请改为按你的读取路径来建模。

-- 一个读取路径视图:在数据库里 join 一次,像查表一样 select() 它
create view order_lines_expanded as
select l.id, l.order_id, l.qty, p.name as product_name, p.price
from   order_lines l
join   products p on p.id = l.product_id;

完整的查询构造器接口,以及「不支持 JOIN/upsert」这条规则,见数据库指南

2. 分页与计数都别全表扫描

select() 最多返回 200 行。早点用上分页——它不是一项优化,而是一道上限。按一个已建索引的列排序,并用 .limit().range() 翻页。

const { data } = await lingcode
  .from('posts')
  .order('created_at', { ascending: false })  // 已建索引的列
  .range(0, 49)                               // 前 50 条
  .select();

计数才是陷阱。不要靠把每一页都取回来再把长度加起来来计数——那等于在每个视图上对整张表做一次 O(n) 扫描。应该这样:

关于过滤、排序和范围的更多内容,见数据库指南

3. 为你过滤和排序的列加索引

没有索引的过滤或排序会扫描整张表。这在 50 行时看不出来,到了 50,000 行就是致命的。在迁移里,为每一个会在规模上用来过滤或排序的列,以及每一个外键列,都加上索引。

create index if not exists idx_posts_author    on posts (author_id);
create index if not exists idx_posts_created_at on posts (created_at desc);

pgvector 列在表变大后需要一个向量索引——见向量检索指南。而且你不必靠手工去找这些:advisors 扫描器会替你标出缺失的索引和未建索引的外键。上线前先跑一遍——见用 advisors 扫描后端。迁移的基础知识见数据库指南

4. 安全检查清单

anon key 会随你的浏览器打包产物一起发出去,所以服务端才是安全真正发生的唯一地方。把数据库当成默认怀有敌意的,让 RLS 对每一次读和写都把关。

完整的策略模型见安全指南

5. 服务端逻辑与密钥

如果一次调用需要私有密钥,它就不能跑在浏览器里——任何打开开发者工具的人都能看到那个密钥。厂商 API 调用以及任何携带密钥的逻辑,都该放进读取 ctx.secrets函数里:

// 函数 —— 密钥永远不会到达客户端
export default async (req, ctx) => {
  const key = ctx.secrets.STRIPE_SECRET_KEY;
  // ...在服务端调用厂商,只返回安全的部分...
};

不过函数是短生命周期的。有两种情况应该放进托管的 Worker 路由,而不是函数:

函数指南托管指南

6. 配额与成本

每个套餐都有上限,在生产环境里撞上某条上限,比提前规划要难受得多。上线前先弄清你的上限:

后端会在存储达到 80%95% 时发出告警——盯住这些警告,别等到被硬性截停。又因为函数是短生命周期的(≤30s),任何更长的任务都必须卸载出去——拆分它、排队它,或者把它挪到托管的 Worker 上。各套餐上限列在定价页

7. 迁移规范

环境之间的 schema 漂移,是「在我的后端上能跑」这句话背后悄无声息的杀手。让迁移成为 schema 变更的唯一途径——永远不要临时手点着改结构。

create table if not exists invites (
  id         uuid primary key default gen_random_uuid(),
  email      text not null,
  created_at timestamptz not null default now()
);
create index if not exists idx_invites_email on invites (email);

迁移的具体机制见数据库指南

8. 性能

三个习惯能在不增加额外基础设施的前提下让应用保持响应迅速:

上线前检查清单

在把一个应用切到生产之前,确认:

  • anon key 能触达的每张表都开启了 RLS,并且按命令写好了策略
  • advisors 跑出来是干净的——没有缺失的索引,没有未建索引的外键,没有被遗漏未上锁的表。
  • 每个热点过滤和排序都有对应的索引
  • 密钥都在保险库里——客户端代码里任何地方都没有私有密钥。
  • 每个远程配置开关都设了默认值,这样缺一个值也绝不会让应用崩掉。
  • 在表、对象、存储、函数、函数挂钟时长以及每天邮件数上,你都在套餐配额之内