数据库扩展策略:只读副本、分片等

通过只读副本、水平分片、连接池和缓存策略来扩展数据库。涵盖 PostgreSQL、MySQL 和托管数据库服务。

E
ECOSIRE Research and Development Team
|2026年3月16日4 分钟阅读789 字数|

数据库扩展策略:只读副本、分片等

数据库性能是 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.mediumdb.t3.medium24GB100
db.r6g.large216GB200200 175 美元
db.r6g.xlarge432GB400350 美元
db.r6g.2xlargedb.r6g.2xlarge864GB800

大多数应用程序在 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&lt;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_replicationpg_stat_replication >1 秒
表膨胀pg_stat_user_tablespg_stat_user_tables
磁盘 I/O 等待iostat/CloudWatch>20ms

缓存命中率低于 95% 是您需要更多内存的最有力指标。增加 shared_bufferseffective_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 发布——帮助企业充满信心地扩展数据基础设施。

E

作者

ECOSIRE Research and Development Team

在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。

通过 WhatsApp 聊天