在互联网大厂,服务器监控是基础设施的命脉。一旦核心数据库或网关宕机,每分钟的损失可能高达数百万。
传统的监控方案在面对海量指标时各有痛点:Zabbix 擅长告警但历史数据存储能力弱;Prometheus 查询语言学习曲线陡峭且不易与业务数据进行关联分析。运维人员真正需要的是既能像 Prometheus 一样吞吐海量时序数据,又能像 MySQL 一样用标准 SQL 进行复杂关联查询。
本文将带你体验如何用 KWDB 3.1.0 搭建一个轻量级但高性能的服务器监控系统,用一个数据库搞定指标存储与资产管理。
场景设定
监控 500 台服务器的 CPU、内存、磁盘 IO 和网络流量。
核心挑战
- 高并发写入:每台服务器每 5 秒上报一次,每秒 100+ 次写入,且数据量随业务扩张线性增长。
- 复杂聚合:需要快速计算每台机器的 P95 CPU 使用率,甚至跨机架、跨业务线进行聚合分析。
- 长周期存储:需要保留 1 年的历史数据用于趋势分析和容量规划,这对存储成本提出了挑战。
架构设计
监控链路通常包含 Agent 采集、数据接收服务、数据库集群、可视化面板及报警中心。通过 HTTP 批量插入和 SQL 查询实现闭环。
建模实战:指标体系
一个优秀的监控系统,必须能打通固定资产与动态指标。
初始化环境
sudo /usr/local/kaiwudb/bin/kwbase sql \
--certs-dir=/etc/kaiwudb/certs \
--host=127.0.0.1:26257
CREATE DATABASE IF NOT EXISTS smart_ops;
USE smart_ops;
服务器资产表 (CMDB)
这是典型的关系型数据,存储服务器的静态属性。为什么不把这些信息直接写在时序表里?因为 rack_id(机架)、os_version(操作系统)等信息是所有指标共享的维度。将它们独立存储,既能节省存储空间(无需在每条指标数据中重复记录),又能支持灵活的维度变更(例如服务器迁移机架,只需改一张表)。
CREATE TABLE servers (
hostname VARCHAR(50) PRIMARY KEY, -- 主机名 (唯一)
ip_address VARCHAR(20), -- IP 地址
rack_id VARCHAR(20), -- 机架编号
os_version VARCHAR(50), -- 操作系统
cpu_cores INT, -- CPU 核数
mem_gb INT -- 内存大小
);
INSERT INTO servers VALUES
('web-01','192.168.1.101','Rack-A01','Ubuntu 22.04',16,32),
('web-02','192.168.1.102','Rack-A01','Ubuntu 22.04',16,32),
('db-01','192.168.1.201','Rack-B02','CentOS 7.9',32,128),
('db-02','192.168.1.202','Rack-B02','CentOS 7.9',32,128);
性能指标表 (Metrics)
Tag 设计是关键,hostname 是核心维度,它是连接 CMDB 表和 Metrics 表的纽带。
CREATE TABLE server_metrics (
ts TIMESTAMP NOT NULL, -- 时间戳
hostname VARCHAR(50) NOT NULL, -- 主机名 (Tag)
cpu_usage DOUBLE, -- CPU 使用率 (%)
mem_usage DOUBLE, -- 内存使用率 (%)
disk_io_read DOUBLE, -- 磁盘读 (MB/s)
disk_io_write DOUBLE, -- 磁盘写 (MB/s)
net_in DOUBLE, -- 网络入 (Mbps)
net_out DOUBLE, -- 网络出 (Mbps)
PRIMARY KEY(ts, hostname)
);
数据模拟:压测级脚本
我们编写了一个 Python 脚本 gen_ops_data.py 来模拟 4 台服务器过去 6 小时的数据,以便验证写入性能和查询逻辑。
import random
from datetime import datetime, timedelta
FILENAME = "ops_data.sql"
HOSTS = ['web-01','web-02','db-01','db-02']
START_TIME = datetime.now() - timedelta(hours=6)
INTERVAL_SECONDS = 5
TOTAL_POINTS = int(6*3600/ INTERVAL_SECONDS)
print(f"正在生成 {len(HOSTS)} 台服务器,过去 6 小时的监控数据...")
with open(FILENAME,"w") as f:
f.write("USE smart_ops;\n")
f.write("INSERT INTO server_metrics (ts, hostname, cpu_usage, mem_usage, disk_io_read, disk_io_write, net_in, net_out) VALUES\n")
records = []
for host in HOSTS:
base_cpu = 20 if 'web' in host else 40
base_mem = 40 if 'web' in host else 70
for i in range(TOTAL_POINTS):
ts = (START_TIME + timedelta(seconds=i*INTERVAL_SECONDS)).strftime('%Y-%m-%d %H:%M:%S')
cpu = base_cpu + random.uniform(-10,30)
if cpu > 100: cpu = 100
mem = base_mem + random.uniform(-5,10)
if mem > 100: mem = 100
disk_r = random.uniform(0,100)
disk_w = random.uniform(0,50)
if 'db' in host:
disk_r *= 2
disk_w *= 3
net_in = random.uniform(10,1000)
net_out = random.uniform(10,1000)
records.append(f"('{ts}', '{host}', {round(cpu,1)}, {round(mem,1)}, {round(disk_r,1)}, {round(disk_w,1)}, {round(net_in,1)}, {round(net_out,1)})")
batch_size = 1000
total = len(records)
for i, record in enumerate(records):
if (i + 1) % batch_size == 0 or i == total - 1:
f.write(f"{record};\n")
if i < total - 1:
f.write("INSERT INTO server_metrics (ts, hostname, cpu_usage, mem_usage, disk_io_read, disk_io_write, net_in, net_out) VALUES\n")
else:
f.write(f"{record},\n")
print(f"生成完毕!总记录数:{total}")
print(f"请运行:time sudo /usr/local/kaiwudb/bin/kwbase sql --certs-dir=/etc/kaiwudb/certs --host=127.0.0.1:26257 < {FILENAME}")
执行导入命令后,可以看到每一批 1000 条数据的写入时间稳定在 30ms 左右。这意味着在单线程情况下,KWDB 的写入吞吐量轻松达到 3.3 万 TPS。对于 500 台服务器每 5 秒上报一次(即 100 TPS)的场景,KWDB 仅用极小的系统资源就能轻松扛住。整个导入过程耗时约 1 分 13 秒,写入了 17280 条记录,全程无报错,验证了 Batch Insert 方案的健壮性。
业务场景实战
注意:执行前请确保
USE smart_ops;。
场景一:P95 性能分析
需求是计算每台服务器在过去 1 小时内的 P95 CPU 使用率,这是容量规划的重要依据。简单的平均值可能掩盖毛刺,P95 更能反映真实压力。
USE smart_ops;
SELECT hostname,
avg(cpu_usage) as avg_cpu,
max(cpu_usage) as max_cpu
FROM server_metrics
WHERE ts > now() - interval '1 hour'
GROUP BY hostname;
查询耗时仅 9.68ms。结果清晰展示了不同角色的服务器负载特征。db-01 和 db-02 的 CPU 使用率都在 50% 左右,符合数据库服务器的高负载特征;而 web-01 和 web-02 则在 30% 左右。这帮助我们快速识别出了系统的潜在瓶颈在 DB 层。
场景二:机架级负载均衡
数据中心的交换机带宽是有限的。如果某个机架上的服务器流量总和过大,会打爆接入交换机,导致整个机架网络瘫痪。这需要我们将时序数据与 CMDB 资产数据关联分析。
USE smart_ops;
SELECT s.rack_id,
sum(m.net_in) as total_net_in,
sum(m.net_out) as total_net_out
FROM server_metrics m
JOIN servers s ON m.hostname = s.hostname
WHERE m.ts > now() - interval '5 minute'
GROUP BY s.rack_id;
这是一个典型的跨表聚合查询。KWDB 成功将 Metrics 表中的流量数据与 CMDB 表中的机架信息进行了关联。结果显示两个机架的流量负载非常均衡,说明当前的负载均衡策略是有效的。如果某个机架流量突增,这个查询能立竿见影地发现问题。
场景三:僵死服务器检测
找出最近 5 分钟没有上报数据的服务器,可能是宕机了。
USE smart_ops;
SELECT s.hostname, s.ip_address, max(m.ts) as last_heartbeat
FROM servers s
LEFT JOIN server_metrics m ON s.hostname = m.hostname
GROUP BY s.hostname, s.ip_address
HAVING max(m.ts) < now() - interval '5 minute' OR max(m.ts) IS NULL;
这种反向查询(找缺失的数据)是时序数据库中较难处理的场景,而 KWDB 凭借标准的 SQL 能力(LEFT JOIN + HAVING)轻松搞定。返回 0 rows 说明当前所有在册的服务器在过去 5 分钟内都有正常的心跳上报,系统处于极其健康的状态。
避坑指南
- 数据降采样:对于 1 年前的历史数据,不需要保留 5 秒级的精度。建议使用 KWDB 的 Downsampling 功能或者定期跑批任务,将数据聚合为'1 小时 1 点'存入历史表。
- 索引优化:如果你经常按 rack_id 查询,建议在 servers 表的 rack_id 字段建立索引。
总结
KWDB 在运维监控场景下表现出色,单机即可支撑数千台服务器的指标写入。相比 Prometheus,它最大的优势在于支持标准 SQL,这让运维人员可以非常灵活地进行多维关联分析。
核心价值回顾:
- 降低门槛:只要会写 SQL 就能做监控分析,无需学习 PromQL 等专用语言。
- 打破孤岛:在一个数据库内实现了 Metrics 与 CMDB 的融合,让监控数据有了业务含义。
- 高压缩率:实测显示,KWDB 对同构的时序数据有极高的压缩比,大幅降低了长周期存储的硬件成本。
随着 AIOps 的发展,基于 KWDB 我们还能做更多:利用分析函数计算 CPU 使用率的同比环比变化自动发现异常突增;当 Web 服务器响应变慢时,通过 SQL 关联查询同一时刻数据库服务器的负载,快速定位根因;甚至存储结构化的日志数据,实现指标加日志的统一检索。
通过构建统一的运维数据底座,我们不再是救火队员,而是系统的健康管理师。

