数据库服务器选型指南

结论: 数据库服务器的资源优先级是 磁盘 IO > 内存 > CPU,选型时先按 OLTP/OLAP 定负载类型,再按数据量和 QPS 定内存与介质。OLTP 场景必须上 NVMe SSD(4K 随机写 IOPS 应在 3 万以上),内存以"热数据集能全部装进 Buffer Pool"为标准,CPU 反而是三者中最容易过剩的一项。

数据库是整条业务链路里最不容易横向扩展、也最容易被"配错"的一环。很多团队把 Web 层加到了八台机器,数据库却还跑在一台 4 核 8 GB 的机械盘云主机上,然后花大力气优化代码——方向从一开始就错了。

这篇文章给一套完整的数据库服务器选型方法:先分清负载类型,再定存储引擎,最后按量化指标定配置。

OLTP 和 OLAP:选型前必须先回答的问题

结论:OLTP(在线事务处理)要求低延迟高 IOPS,OLAP(在线分析处理)要求高吞吐大扫描,两者对配置的要求几乎相反。

维度OLTP(交易型)OLAP(分析型)
典型场景订单、支付、用户资料、库存报表、BI、用户行为分析、数仓
查询特征简单 SQL、走索引、单行或小范围大范围扫描、聚合、多表 JOIN
单次耗时要求1~50 ms秒级~分钟级可接受
关键资源随机 IOPS、内存命中率顺序吞吐、CPU 并行、大内存
磁盘要求NVMe SSD,低延迟大容量 SSD/HDD 混合,高吞吐
代表产品MySQL 8.4、PostgreSQL 16、OracleClickHouse、Doris、Greenplum
典型配置8~16 核 / 64~256 GB / NVMe32 核+ / 256 GB+ / 大容量存储

绝大多数业务系统(电商、SaaS、ERP、论坛)都是 OLTP,只有报表和数仓才需要 OLAP。如果一台机器上既要跑交易又要跑大报表,报表查询会把 Buffer Pool 冲掉、把 IOPS 打满,导致交易超时——这是最常见的架构事故之一。正确做法是读写分离:交易走主库,报表走只读从库或独立的分析库。

主流数据库怎么选?

结论:没有"最好的数据库",只有"最匹配数据模型的数据库"。

数据库数据模型最适合的场景不适合的场景内存建议磁盘建议
MySQL 8.4关系型Web 业务、电商、SaaS,生态最成熟复杂分析、JSON 重度操作热数据 1.2~1.5 倍NVMe SSD
PostgreSQL 16关系型 + 扩展GIS、复杂查询、JSONB、时序(TimescaleDB)极简 KV 高并发同上,shared_buffers 8~16 GB 起NVMe SSD
Redis 7/8内存 KV缓存、会话、排行榜、分布式锁持久化主存储(成本极高)按数据量 × 1.5(含碎片)SSD 即可,AOF 盘要稳
MongoDB 7文档型半结构化数据、快速迭代的业务强事务、复杂 JOIN热索引必须全在内存NVMe SSD
ClickHouse列式日志分析、BI 大宽表单行更新、事务越大越好大容量 SSD/HDD
Elasticsearch 8倒排索引全文检索、日志检索作为主数据存储堆内存 ≤ 31 GB/节点NVMe SSD

一个实用的选型判断顺序:

  1. 数据之间有关系、需要事务 → MySQL 或 PostgreSQL。
  2. 只是键值缓存、能容忍丢失 → Redis。
  3. 结构经常变、字段不确定 → MongoDB。
  4. 数据量上亿、只做统计不做事 → ClickHouse / Doris。
  5. 要搜"包含某个词" → Elasticsearch。

CPU、内存、磁盘 IO 的权重怎么排?

结论:给数据库配机器,按 磁盘 IO → 内存 → CPU 的顺序分配预算,不要反过来。

磁盘 IO(权重最高)

数据库的写入是 WAL(Write-Ahead Log)+ 随机脏页刷盘,都是 4K~16K 的小随机写。机械硬盘的 4K 随机写 IOPS 通常不足 200,SATA SSD 约 2 万~5 万,NVMe SSD 可达 10 万~30 万。对一台日写入千万行的库来说,这个差距就是"能不能扛住"的区别。

判断标准:4K 随机写 IOPS ≥ 3 万、写延迟 p99 < 1 ms(NVMe 通常 0.1~0.3 ms)。低于这个水平,先用 fio 测,别急着加 CPU。

内存(权重第二)

核心原则是热数据集要能全部装进 Buffer Pool。InnoDB 的 innodb_buffer_pool_size 一般设为物理内存的 60%~75%(专用数据库服务器),PostgreSQL 的 shared_buffers 设 25% 左右(其余交给操作系统 Page Cache)。

估算方法:

text
所需内存 ≈ 热数据量(近 7~30 天频繁访问的表与索引)× 1.3

举例:库总大小 200 GB,其中近 30 天活跃的表加索引约 40 GB,则内存建议 64 GB,Buffer Pool 设 40 GB 左右。

CPU(权重最低,但也不能太弱)

数据库的查询执行、并发连接处理、压缩与校验都吃 CPU。经验配比:每 1000 QPS 约需 1~2 核(简单主键查询取低值,复杂 JOIN 取高值)。多数中小业务 8~16 核足够,真正的瓶颈往往出现在磁盘上。

基准测试:用数字验证而不是猜

结论:上线前跑一次 sysbench OLTP + fio,你就知道这台机器的真实上限。

bash
# 安装 sysbench(Ubuntu 24.04)
sudo apt update && sudo apt install -y sysbench fio

# 1) 准备测试数据:16 张表,每表 100 万行
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password='<密码>' \
  --mysql-db=sbtest --tables=16 --table-size=1000000 --threads=8 prepare

# 2) 读写混合压测:64 并发,持续 5 分钟
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 --mysql-user=root --mysql-password='<密码>' \
  --mysql-db=sbtest --tables=16 --table-size=1000000 \
  --threads=64 --time=300 --report-interval=10 run

# 3) 只读与只写分别测,定位瓶颈
sysbench oltp_read_only --threads=64 --time=120 --mysql-db=sbtest run
sysbench oltp_write_only --threads=64 --time=120 --mysql-db=sbtest run

# 4) 清理
sysbench oltp_read_write --mysql-db=sbtest --tables=16 cleanup

重点看输出里的 queries performed 中的 95% latency(应低于 50 ms)和 transactions 的 QPS。如果 95% 延迟突然飙高,同时 iostat 显示 %util 接近 100%,说明磁盘到顶了。

磁盘独立压测(模拟 InnoDB 的 16K 页随机写):

bash
# 模拟数据库页大小 16K 的随机读写(70% 读 / 30% 写)
fio --name=db --ioengine=libaio --direct=1 --rw=randrw --rwmixread=70 \
    --bs=16k --numjobs=8 --iodepth=64 --runtime=120 --time_based \
    --group_reporting --filename=/var/lib/mysql/fio.test --size=20G

# 单独测 fsync 延迟(决定事务提交速度,双写与 WAL 都依赖它)
fio --name=fsync --ioengine=sync --rw=write --bs=16k --fdatasync=1 \
    --size=2G --numjobs=1 --runtime=60 --time_based --group_reporting

fsync 那一项尤其重要:MySQL 的 innodb_flush_log_at_trx_commit=1 时每次事务提交都要刷盘,单线程 fsync 延迟 2 ms 就意味着单连接 TPS 上限约 500。NVMe 通常能把这项压到 0.2 ms 以内。

关键参数(MySQL 8.4,/etc/mysql/mysql.conf.d/mysqld.cnf):

ini
[mysqld]
# 内存:专用数据库服务器设为物理内存的 60%~75%
innodb_buffer_pool_size = 40G
innodb_buffer_pool_instances = 8

# 磁盘 IO:SSD/NVMe 适当调高 IO 能力参数
innodb_io_capacity = 20000
innodb_io_capacity_max = 40000
innodb_flush_neighbors = 0        # SSD 上关闭邻接页合并
innodb_flush_method = O_DIRECT    # 绕过 Page Cache,避免双重缓存

# 持久性与性能的平衡
innodb_flush_log_at_trx_commit = 1   # 金融类保持 1;可容忍秒级丢失可设 2
sync_binlog = 1

# 连接与线程
max_connections = 500
thread_cache_size = 64
table_open_cache = 4000

改完用 sudo systemctl restart mysql 生效,并用 SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%'; 确认参数已加载。

主从、备份与高可用怎么规划?

结论:单库不是"以后再说"的方案,数据量超过 50 GB 或 QPS 超过 2000 就该规划主从与备份。

三种典型形态:

方案适用规模优点代价
单机 + 定时备份小业务、内部系统最简单、成本最低RTO 小时级,可能丢最近一次备份后的数据
主从复制(一主一从/一主多从)日 PV 十万级读写分离、从库可用于报表与备份主库故障需人工或工具切换,有秒级延迟
主从 + 自动故障切换(MHA/Orchestrator/Patroni)核心业务RTO 秒级~分钟级架构复杂,需演练

备份策略的"3-2-1 原则":至少 3 份副本、2 种不同介质、1 份异地。

bash
# 逻辑备份(适合 100 GB 以下,可单库单表恢复)
mysqldump -u root -p --single-transaction --quick --routines --triggers \
  --set-gtid-purged=OFF mydb | gzip > /backup/mydb-$(date +%F).sql.gz

# 物理备份(大库首选,需安装 Percona XtraBackup 8.x)
xtrabackup --backup --target-dir=/backup/full-$(date +%F) \
  --user=root --password='<密码>'

# 校验备份可用性:定期恢复演练,备份不演练等于没备份
systemctl stop mysql
xtrabackup --prepare --target-dir=/backup/full-2026-01-01
xtrabackup --copy-back --target-dir=/backup/full-2026-01-01
chown -R mysql:mysql /var/lib/mysql && systemctl start mysql

主从状态检查:

sql
-- 从库上执行,Seconds_Behind_Master 应稳定为 0 或小数值
SHOW REPLICA STATUS\G

-- 主库上查看连接与慢查询
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
bash
# 慢查询分析:找出最耗时的 SQL
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
# 或按平均耗时排序
pt-query-digest /var/log/mysql/mysql-slow.log | head -50

常见误区 / 排错提示

  • 误区一:数据库和 Web 服务挤在一台机器上。 两者抢内存和 IO,任何一方出峰值都会拖垮另一方。数据量上 GB 后务必拆分部署。
  • 误区二:Buffer Pool 设太小或太大。 太小导致大量物理读;太大导致系统内存不足触发 OOM。专用服务器取 60%~75% 是安全区间。
  • 误区三:只看磁盘容量不看 IOPS。 一块 4 TB 的机械盘容量够但 IOPS 不足 200,跑数据库必然卡。容量和 IOPS 要同时满足。
  • 误区四:把 max_connections 调到几千。 连接数高不等于性能好,大量并发连接会引发线程争用和内存暴涨。用连接池(如 HikariCP、ProxySQL)控制到 50~200 实际并发更合理。
  • 排错提示:数据库突然变慢。 按这个顺序查:SHOW PROCESSLIST 看是否有锁等待或大查询 → iostat -x 1 看 %util 与 await → free -h 看是否用了 swap → 慢查询日志看是否有新上线的 SQL 没走索引。

常见问题(FAQ)

MySQL 和 PostgreSQL 该选哪个?

生态和团队熟悉度优先。Web 业务、需要大量现成方案和运维工具选 MySQL 8.4;需要 GIS、复杂查询优化器、JSONB 或更严格的标准符合性选 PostgreSQL 16。两者在常规 OLTP 场景下性能差异不大,不要为了微小性能差切换技术栈。

数据库服务器内存多大合适?

以"热数据集能全部装进 Buffer Pool"为准来定,而不是按总数据量直接套。判断依据是先在 MySQL 里统计库的真实大小(SELECT table_schema, SUM(data_length+index_length)/1024/1024/1024 AS GB FROM information_schema.tables GROUP BY table_schema),再取近 7~30 天频繁访问的表与索引作为热数据,按"热数据量 × 1.3"估算——多出的三成留给连接缓冲、临时表与排序区。经验配比是:热数据 50 GB 以内配 16~32 GB,50~200 GB 配 64~128 GB,200 GB 以上配 128~512 GB 并考虑分库分表。配好后把 innodb_buffer_pool_size 设为物理内存的 60%~75%,再用 SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%' 观察物理读占总读的比例,接近 0 才算够用。堆到远超热数据量的内存收益递减明显,不如把预算花在 NVMe 上。

数据库必须用 NVMe 吗?

QPS 低于 500、数据量小于 20 GB 的轻负载用 SATA SSD 也可以;但只要写入频繁、并发上百,NVMe 的收益非常明显——随机写 IOPS 相差 5~10 倍,事务提交延迟相差一个数量级。核心业务库直接上 NVMe 是最省事的选择。

Redis 需要单独一台服务器吗?

作为缓存时可以与 Web 同机(小规模),但生产环境建议独立部署:Redis 是单线程模型,一旦被慢命令(如 KEYS *、大 key 删除)阻塞会影响同机所有服务;且 Redis 内存占用需要与业务内存隔离计算,避免互相挤占。

数据库做读写分离值不值?

读多写少(读写比 > 5:1)且主库 CPU/IO 已接近瓶颈时很值。做法是把报表、列表页、搜索等读流量切到从库。但要注意主从延迟,写入后立刻读取的场景(如提交订单后跳转详情页)必须走主库或用"写后读主"策略。

备份多久做一次合适?

按 RPO(可容忍丢失的数据量)决定:核心交易库建议每日全量 + binlog 实时增量(可做到秒级恢复点);内容类库可每日全量 + 每小时增量。无论频率如何,每月必须做一次恢复演练,验证备份可用性。