在同一台物理机或虚拟机上部署和管理多个不同类型的数据库实例,是中小企业、开发测试环境甚至部分生产架构中非常常见的场景。虽然“资源隔离”和“高可用”通常建议物理分离,但在预算有限或资源利用率优先的场景下,混合部署是完全可行的。
核心挑战在于:端口冲突、资源争抢(CPU/内存/IO)、安全隔离以及运维复杂度。
下面我从实操层面、资源隔离策略和最佳实践三个维度,拆解如何优雅地搞定这件事。
一、 基础前提:端口与路径规划
这是最容易踩坑的地方。不同数据库默认端口可能相同(如 MySQL 和 PostgreSQL 都习惯用 5432 或 3306 的变体,或者 Redis 默认 6379),同类型多实例必须区分端口。
-
端口分配表:
- MySQL:
3306(主),3307(从/其他) - PostgreSQL:
5432(主),5433(其他) - Redis:
6379(主),6380(缓存集群节点) - MongoDB:
27017,27018 - Elasticsearch:
9200,9300 - 原则:所有服务端口必须在防火墙(iptables/firewalld)和安全组中明确开放,且互不重叠。
- MySQL:
-
数据目录隔离:
- 绝对不要把所有数据放在
/var/lib/mysql这种默认路径混在一起。 - 推荐结构:
/data/db/ ├── mysql_3306/ │ ├── data/ │ └── logs/ ├── postgresql_5432/ │ ├── data/ │ └── logs/ ├── redis_6379/ │ └── dump.rdb └── mongo_27017/ └── data/ - 每个实例拥有独立的 PID 文件、Socket 文件和日志文件。
- 绝对不要把所有数据放在
二、 操作系统层面的资源隔离(关键步骤)
如果所有数据库都跑在同一个 Linux 用户下,一个数据库吃光内存,其他数据库就会 OOM(Out of Memory)。必须利用 Linux 内核特性进行限制。
1. 使用 systemd 管理进程(现代 Linux 发行版标配)
不要手动写 shell 脚本启动数据库,要用 systemd。它不仅能开机自启,还能做资源控制。
以 MySQL 为例,创建一个 service 文件 /etc/systemd/system/mysql-custom.service:
[Unit]
Description=MySQL Custom Instance on Port 3307
After=network.target
[Service]
Type=simple
User=mysql
Group=mysql
# 指定配置文件和数据目录
ExecStart=/usr/sbin/mysqld --defaults-file=/data/db/mysql_3306/my.cnf
Restart=on-failure
LimitNOFILE=65535
# 【核心】资源限制
MemoryMax=2G # 限制最大内存为 2GB
MemoryHigh=1.5G # 超过 1.5GB 时开始压缩回收
CPUQuota=50% # 限制 CPU 使用率不超过 50%
IOWeight=100 # 限制 IO 权重(需启用 iocontroller)
对 PostgreSQL、Redis 等也做类似配置。注意:Redis 本身支持单线程,但如果配置了 AOF/RDB 持久化或 Cluster 模式,也需要关注其内存峰值。
2. cgroups v2 进阶(可选但推荐)
如果你使用的是较新的 CentOS 8 / Ubuntu 20.04+ / Debian 11+,系统默认使用 cgroups v2。你可以更精细地划分 IO 带宽。
例如,防止 MySQL 的大查询把磁盘 IO 打满,导致 Redis 响应变慢:
# 创建 cgroup 层级
mkdir /sys/fs/cgroup/io/mysql_high_priority
echo "1000" > /sys/fs/cgroup/io/mysql_high_priority/io.weight
echo "100" > /sys/fs/cgroup/io/redis_low_priority/io.weight
注:具体命令因发行版而异,建议使用 cgcreate 工具或 systemd 自动处理。
三、 各类型数据库的特殊注意事项
1. MySQL/MariaDB
- 多实例方案:推荐使用 MySQL Enterprise Monitor 的多实例模板,或者使用 mysqld_multi 工具。
- 配置要点:每个实例必须有独立的
my.cnf,重点调整innodb_buffer_pool_size。假设总内存 16G,两个 MySQL 实例各分 6G,留 4G 给 OS 和其他数据库。 - 权限隔离:不同实例之间没有直接关系,但建议为每个实例创建独立的管理员账号,避免密码泄露导致横向渗透。
2. PostgreSQL
- 优势:PostgreSQL 天生擅长多实例共存,因为它的每个实例是一个独立的 Postmaster 进程。
- 操作:只需初始化不同的
PGDATA目录,并在postgresql.conf中修改port、unix_socket_directories和shared_buffers。 - 提示:
shared_buffers不要设得太大,否则多个 PG 实例加起来会撑爆内存。
3. Redis
- 风险最高:Redis 是单线程模型,且全内存操作。一个实例的
KEYS *或大 Key 删除可能导致整个服务器卡顿。 - 隔离建议:
- 如果使用 Redis Sentinel 或 Cluster,尽量将不同角色的节点分散到不同 IP(即使在同一台机器,也要通过不同网卡接口或隧道隔离,如果做不到,至少用不同的 TCP 端口)。
- 强烈建议:在同一台服务器上运行多个 Redis 实例时,务必设置
maxmemory-policy为allkeys-lru或volatile-lru,并严格限制maxmemory。 - 考虑使用 Redis Modules 或 Namespace 逻辑隔离,但物理进程隔离更安全。
4. MongoDB
- 副本集 vs 分片:MongoDB 可以运行多个 mongod 实例组成副本集。它们共享文件系统锁吗?不,MongoDB 3.6+ 默认使用 WiredTiger 引擎,支持多文档事务,但对同一集合有锁竞争。
- 建议:如果是生产环境,尽量避免在同一台小规格机器上跑 MongoDB 副本集的所有成员。如果是开发环境,确保每个实例使用不同的
--dbpath和--port,并监控 WiredTiger cache 大小。
5. Elasticsearch / Kibana
- JVM 堆内存陷阱:ES 极度依赖 JVM 堆内存。默认堆内存是 1G,但在多实例环境下,你必须手动计算:
Heap Size = 总内存 * 50% / 实例数。 - 警告:如果堆内存设置过大,会导致频繁的 Full GC,进而引发集群脑裂或写入超时。同时,ES 需要大量文件句柄,记得调整
fs.file-max和vm.max_map_count。
四、 运维与监控:别等到崩了才知道
当多个数据库挤在一台服务器上,监控粒度必须细化。
-
Prometheus + Node Exporter + 各数据库 Exporter
- 安装
node_exporter监控整机 CPU、内存、IO。 - 安装
mysqld_exporter、postgres_exporter、redis_exporter等。 - 关键点:在 Prometheus 的 scrape 配置中,通过
job_name和instance标签区分不同实例。例如:- job_name: 'mysql' static_configs: - targets: ['localhost:9104'] # MySQL 3306 labels: { instance: 'mysql_primary' } - targets: ['localhost:9105'] # MySQL 3307 labels: { instance: 'mysql_secondary' }
- 安装
-
日志集中收集
- 不要 SSH 上去看日志。使用 Filebeat 或 Fluentd 将所有数据库日志发送到 Elasticsearch 或 Loki。
- 配置 Logrotate,防止日志文件撑爆磁盘。每个数据库实例单独配置 logrotate 策略。
-
备份策略
- 串行执行:备份任务绝对不能并行!两台数据库同时做全量备份,IO 会直接堵死。
- 错峰调度:
- MySQL 备份:凌晨 2:00
- PostgreSQL 备份:凌晨 3:00
- Redis RDB:每 6 小时一次,但设置
save间隔较长,或在低峰期触发。
- 工具选择:
- MySQL: Percona XtraBackup
- PostgreSQL: pgBackRest 或 WAL-G
- Redis: 官方
BGSAVE配合脚本自动化拷贝 RDB 文件。
五、 总结与避坑指南
- 不要迷信“够用”:评估业务峰值。如果 MySQL 峰值占 80% 内存,再放一个 Redis 进去,必挂。预留 30%-40% 的资源给 OS 和其他数据库。
- 网络隔离:如果可能,使用 Docker 或 LXC 容器来封装每个数据库实例。Docker 天然提供了 cgroups 和 namespace 隔离,比裸奔在宿主机上安全得多。
- 示例:
docker run -p 3306:3306 -m 2g --cpus 1.5 mysql:8
- 示例:
- 安全第一:
- 关闭不必要的远程访问(绑定
127.0.0.1除非必要)。 - 防火墙只开放业务所需端口。
- 定期更新补丁,特别是 OpenSSL 和数据库内核漏洞。
- 关闭不必要的远程访问(绑定
- 最终建议:
- 开发/测试环境:完全可以混合部署,节省成本。
- 生产环境:除非是极小规模(如日均 PV < 1万)或边缘节点,否则强烈建议按功能拆分服务器。数据库是 IO 密集型应用,混合部署带来的调试成本和故障排查难度,远高于几台云服务器的费用。
如果你正在搭建这套环境,先从 Docker Compose 或 systemd + cgroups 入手,做好资源限额,这是性价比最高的隔离方式。
云技术博客