数据库扩展策略:只读副本、分片等
数据库性能是 78% Web 应用程序扩展问题的瓶颈。 应用程序可以轻松地水平扩展,但数据库无法横向扩展。您选择的数据库扩展策略决定了您的应用程序是为 100 个用户还是 100,000 个用户提供可接受的性能。
本指南涵盖了完整的数据库扩展策略,从延迟扩展需求的简单优化到水平分片等高级技术。
要点
- 在添加基础设施之前优化查询并添加索引 --- 这解决了 60% 的数据库性能问题
- 读取副本是风险最低的扩展策略,可处理 80% 的读取密集型工作负载
- 一旦您的应用程序运行超过 10 个实例,连接池是强制性的
- 水平分片是最后的手段,会带来显着的应用程序复杂性
缩放阶梯
按此顺序缩放。每一步都比下一步更便宜且风险更小:
第 1 步:查询优化(免费)
在添加基础架构之前,请确保现有数据库性能最佳。
-- Find slow queries in PostgreSQL
SELECT
calls,
mean_exec_time::numeric(10,2) AS avg_ms,
total_exec_time::numeric(10,2) AS total_ms,
query
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;
常见优化:
- 为经常过滤的列添加缺失的索引
- 将
SELECT *替换为特定的列列表 - 使用
EXPLAIN ANALYZE识别大表上的顺序扫描 - 为多列WHERE子句添加复合索引
- 使用键集分页而不是
OFFSET实现分页
-- Bad: OFFSET pagination (scans all skipped rows)
SELECT * FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 10000;
-- Good: Keyset pagination (index-only scan)
SELECT * FROM orders
WHERE created_at < '2026-03-15T10:00:00Z'
ORDER BY created_at DESC
LIMIT 20;
第 2 步:垂直缩放 ($)
增加现有数据库服务器上的 CPU、RAM 和存储。这会赢得时间并且需要零应用程序更改。
| 实例大小 | vCPU | 内存 | 连接 | 每月费用 (RDS) |
|---|---|---|---|---|
| db.t3.medium | db.t3.medium | 2 | 4GB | 100 |
| db.r6g.large | 2 | 16GB | 200 | 200 175 美元 |
| db.r6g.xlarge | 4 | 32GB | 400 | 350 美元 |
| db.r6g.2xlarge | db.r6g.2xlarge | 8 | 64GB | 800 |
大多数应用程序在 64 GB RAM 和 8 个 vCPU 时达到其限制。除此之外,垂直扩展的成本变得过高。
步骤 3:连接池 ($)
Application (50 pods x 20 connections = 1,000 connections)
|
v
PgBouncer (25 database connections, transaction pooling)
|
v
PostgreSQL (25 active connections, manageable)
PgBouncer配置:
[databases]
app = host=db.example.com port=5432 dbname=production
[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
pool_mode = transaction
default_pool_size = 25
max_client_conn = 1000
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
步骤 4:读取副本 ($$)
只读副本处理 SELECT 查询,从主数据库卸载 60-90% 的数据库负载。
架构:
Write queries --> Primary database
|
Replication (async)
|
+----+----+
| |
Read queries --> Replica 1 Replica 2
应用程序级路由(Drizzle ORM 示例):
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const primaryPool = new Pool({ connectionString: process.env.DATABASE_URL });
const replicaPool = new Pool({ connectionString: process.env.DATABASE_REPLICA_URL });
export const primaryDb = drizzle(primaryPool);
export const replicaDb = drizzle(replicaPool);
// In service code:
// Write operations use primaryDb
async createOrder(data: OrderInput) {
return primaryDb.insert(orders).values(data).returning();
}
// Read operations use replicaDb
async getOrders(organizationId: string) {
return replicaDb.select().from(orders)
.where(eq(orders.organizationId, organizationId))
.orderBy(desc(orders.createdAt));
}
复制延迟注意事项:异步复制会引入延迟(通常为 10-100 毫秒)。写入后,立即从副本读取可能会返回过时的数据。使用主数据库进行同一用户流中写入之后的读取。
步骤 5:缓存 ($$)
Redis 缓存完全消除了重复的数据库查询。
async getProduct(id: string): Promise<Product> {
const cacheKey = `product:${id}`;
// Check cache first
const cached = await this.redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// Cache miss: query database
const product = await this.db.select().from(products)
.where(eq(products.id, id))
.limit(1);
// Cache for 5 minutes
await this.redis.setex(cacheKey, 300, JSON.stringify(product[0]));
return product[0];
}
缓存失效策略:写入时失效。当产品更新时,删除缓存密钥。使用缓存旁模式(应用程序管理缓存)而不是直写模式(数据库管理缓存)。
步骤 6:水平分片 ($$$)
分片根据分片键将数据分布到多个数据库实例。
| 分片策略 | 描述 | 最适合 |
|---|---|---|
| 基于哈希 | 散列分片键,均匀分布 | 均匀数据分布 |
| 基于范围的 | 为分片分配范围(例如,A-M、N-Z) | 时间序列、地理数据 |
| 以租户为基础 | 每个租户/组织一个分片 | 多租户 SaaS |
何时分片:
- 单个数据库超过1 TB并且还在不断增长
- 写入吞吐量超出单个主节点的处理能力
- 垂直扩展成本超过 2,000 美元/月且没有上升空间
何时不进行分片:
- 您尚未完成步骤 1-5
- 您的数据适合单个 500 GB 数据库
- 跨分片查询在您的应用程序中很常见
PostgreSQL 特定的优化
分区(分片之前)
PostgreSQL 表分区将大表拆分为较小的物理表,同时维护单个逻辑表:
-- Partition orders by month
CREATE TABLE orders (
id UUID PRIMARY KEY,
organization_id UUID NOT NULL,
created_at TIMESTAMP NOT NULL,
total DECIMAL(10,2)
) PARTITION BY RANGE (created_at);
CREATE TABLE orders_2026_01 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE orders_2026_02 PARTITION OF orders
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE orders_2026_03 PARTITION OF orders
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
分区可将大型表上的时间范围查询的查询性能提高 10-100 倍,因为 PostgreSQL 仅扫描相关分区。
吸尘和维护
-- Check table bloat
SELECT
schemaname,
relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric / greatest(n_live_tup, 1) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
为高写入表积极配置 autovacuum:
ALTER TABLE orders SET (
autovacuum_vacuum_threshold = 100,
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_threshold = 50,
autovacuum_analyze_scale_factor = 0.02
);
监控数据库性能
跟踪这些指标以了解何时以及如何扩展:
| 公制 | 工具 | 警报阈值 |
|---|---|---|
| 查询延迟 (P95) | pg_stat_语句 | >500 毫秒 |
| 活跃连接 | pg_stat_activity | > 最大值的 80% |
| 缓存命中率 | pg_stat_数据库 | <95% |
| 复制滞后 | pg_stat_replication | pg_stat_replication >1 秒 |
| 表膨胀 | pg_stat_user_tables | pg_stat_user_tables |
| 磁盘 I/O 等待 | iostat/CloudWatch | >20ms |
缓存命中率低于 95% 是您需要更多内存的最有力指标。增加 shared_buffers 和 effective_cache_size 通常比添加只读副本更便宜且更快。
查询性能跟踪
-- Enable pg_stat_statements (postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'
-- Find the top 10 most time-consuming queries
SELECT
queryid,
calls,
mean_exec_time::numeric(10,2) AS avg_ms,
total_exec_time::numeric(10,2) AS total_ms,
rows,
query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
每周查看前 10 个查询。即使优化单个频繁执行的查询也可以将数据库总体负载减少 10-30%。
常见问题
我们如何知道何时需要扩展?
监控三个指标:查询延迟 P95(500 毫秒时发出警报)、连接利用率(80% 时发出警报)和 CPU 利用率(持续 70% 时发出警报)。如果您经常达到这些阈值,请进入扩展阶梯的下一步。不要预先优化——当数据告诉你时进行扩展。
读取副本或缓存 --- 哪个先?
从缓存开始。 Redis 缓存实现起来更简单,消除了更多负载(缓存命中完全跳过数据库),并且成本更低。当缓存命中率已超过 80% 但主数据库仍面临缓存未命中和写入操作的压力时,请添加只读副本。
数据库扩展如何与 Odoo 配合使用?
Odoo 专门使用 PostgreSQL。从查询优化开始(Odoo 生成复杂的报告查询)。当并发用户数超过 50 时,添加 PgBouncer 以进行连接池。使用只读副本来报告查询(配置 Odoo 的 --db-replica 选项)。 ECOSIRE 提供 Odoo 性能优化,包括数据库调整。
托管数据库(RDS/Cloud SQL)值得溢价吗?
是的,对于大多数企业来说。托管数据库处理自动备份、修补、故障转移和监控。与自行管理的 PostgreSQL 相比,成本溢价 30-40%,但您节省的工程时间可以抵消这一点。大规模部署是个例外,其中大型实例的成本溢价超过了兼职 DBA 的成本。
接下来会发生什么
数据库扩展是更广泛的基础设施扩展策略的组成部分。将其与静态资产的 CDN 优化、应用程序 Pod 的 Kubernetes 自动扩展 以及 负载测试 结合起来,以在实际条件下验证您的扩展决策。
联系 ECOSIRE 获取数据库优化咨询,或参阅我们的 DevOps 指南 了解完整的基础架构路线图。
由 ECOSIRE 发布——帮助企业充满信心地扩展数据基础设施。
作者
ECOSIRE Research and Development Team
在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。