跳转到内容
产品文档已发布

RAILWISE-OS 性能优化指南

RAILWISE-OS 系统性能优化策略,包含数据库优化、缓存策略、前端优化与监控告警

复核 2026-07-09入门公开可引用RailWise 技术团队
产品文档

AI语义标签: #性能优化 #数据库调优 #缓存策略 #监控告警 #PostgreSQL #Redis #前端优化

RAILWISE-OS 面向工程监测业务,需要处理大量时序监测数据、高频写入与复杂查询。本指南提供系统性能优化的最佳实践,帮助运维团队与开发者提升系统响应速度与吞吐量。

指标 目标值 说明
API 响应时间 (P95) < 200ms 常规查询
数据写入吞吐量 > 1000 条/秒 批量写入
报告生成时间 < 30 秒 100页以内报告
页面首屏加载 < 2 秒 Web 管理端
并发用户数 > 500 单实例
数据库查询 (P95) < 100ms 索引查询

根据服务器内存调整 PostgreSQL 参数:

# postgresql.conf - 32GB 内存服务器示例
# 共享缓冲区(建议 25% 内存)
shared_buffers = 8GB
# 有效缓存大小(建议 50-75% 内存)
effective_cache_size = 24GB
# 工作内存(复杂查询)
work_mem = 64MB
# 维护工作内存(VACUUM, CREATE INDEX)
maintenance_work_mem = 2GB
# 自动 Vacuum 工作内存
autovacuum_work_mem = 512MB
# 最大连接数
max_connections = 200
# 并发维护操作
max_parallel_maintenance_workers = 4
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
# WAL 配置
wal_buffers = 64MB
max_wal_size = 8GB
min_wal_size = 2GB
wal_compression = on
# 随机页成本(SSD 建议 1.1)
random_page_cost = 1.1
# 有效 IO 并发数(SSD 建议 200)
effective_io_concurrency = 200
# 启用 JIT 编译(PostgreSQL 11+)
jit = on
# 查询计划缓存
plan_cache_mode = auto
-- 监测数据表(假设已存在)
-- CREATE TABLE monitoring_data (...);
-- 1. 复合索引:项目 + 监测点 + 时间(最常用查询)
CREATE INDEX CONCURRENTLY idx_monitoring_project_point_time
ON monitoring_data (project_id, point_id, measured_at DESC);
-- 2. 时间范围查询索引
CREATE INDEX CONCURRENTLY idx_monitoring_time
ON monitoring_data (measured_at DESC)
WHERE measured_at > '2025-01-01';
-- 3. 状态查询索引(部分索引)
CREATE INDEX CONCURRENTLY idx_monitoring_abnormal
ON monitoring_data (project_id, point_id, measured_at)
WHERE status IN ('warning', 'alarm');
-- 4. BRIN 索引(大块数据,时间序列)
CREATE INDEX CONCURRENTLY idx_monitoring_time_brin
ON monitoring_data USING BRIN (measured_at)
WITH (pages_per_range = 128);
-- 5. GIN 索引(JSONB 字段查询)
CREATE INDEX CONCURRENTLY idx_monitoring_values_gin
ON monitoring_data USING GIN (values);
-- 查看索引使用情况
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan DESC;
-- 重建低效索引
REINDEX INDEX CONCURRENTLY idx_monitoring_project_point_time;
-- 分析表(更新统计信息)
ANALYZE monitoring_data;

对于大规模监测数据,建议使用表分区:

-- 按时间范围分区(每月一个分区)
CREATE TABLE monitoring_data (
id UUID DEFAULT gen_random_uuid(),
project_id VARCHAR(64) NOT NULL,
point_id VARCHAR(64) NOT NULL,
sensor_type VARCHAR(32) NOT NULL,
values JSONB NOT NULL,
measured_at TIMESTAMPTZ NOT NULL,
status VARCHAR(16) DEFAULT 'normal',
created_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (id, measured_at)
) PARTITION BY RANGE (measured_at);
-- 创建分区
CREATE TABLE monitoring_data_2026_01
PARTITION OF monitoring_data
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE monitoring_data_2026_02
PARTITION OF monitoring_data
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
-- 自动创建未来分区(使用 pg_partman 扩展)
SELECT partman.create_parent(
'public.monitoring_data',
'measured_at',
'native',
'monthly'
);
-- 启用慢查询日志
log_min_duration_statement = 1000; -- 记录超过 1 秒的查询
-- 查看当前慢查询
SELECT
query,
calls,
total_exec_time,
mean_exec_time,
rows,
shared_blks_hit,
shared_blks_read
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;
-- 优化前:全表扫描
SELECT * FROM monitoring_data
WHERE project_id = 'proj_abc123'
AND measured_at BETWEEN '2026-07-01' AND '2026-07-08';
-- 优化后:使用索引 + 限制返回字段
SELECT
point_id,
values->>'dx' as dx,
values->>'dy' as dy,
values->>'dz' as dz,
measured_at
FROM monitoring_data
WHERE project_id = 'proj_abc123'
AND measured_at BETWEEN '2026-07-01' AND '2026-07-08'
ORDER BY measured_at DESC
LIMIT 1000;
┌─────────────────────────────────────────────────────────────┐
│ 缓存分层架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ L1: 本地缓存 (Caffeine) │
│ ├── 项目基础信息(TTL: 5分钟) │
│ ├── 用户权限(TTL: 10分钟) │
│ └── 字典数据(TTL: 30分钟) │
│ │
│ L2: 分布式缓存 (Redis) │
│ ├── 监测数据热点(TTL: 1小时) │
│ ├── 报告生成结果(TTL: 24小时) │
│ └── 会话状态(TTL: 2小时) │
│ │
│ L3: 数据库查询缓存 │
│ └── PostgreSQL 共享缓冲区 │
│ │
└─────────────────────────────────────────────────────────────┘
# redis.conf 优化配置
# 内存配置
maxmemory 4gb
maxmemory-policy allkeys-lru
# 持久化配置
save 900 1
save 300 10
save 60 10000
# AOF 配置
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 连接配置
tcp-keepalive 300
timeout 300
maxclients 10000
# 慢查询日志
slowlog-log-slower-than 10000
slowlog-max-len 128
// 监测数据查询缓存示例
class MonitoringDataService {
private localCache = new Map<string, any>();
private readonly LOCAL_TTL = 5 * 60 * 1000; // 5分钟
async getMonitoringData(
projectId: string,
pointId: string,
startTime: string,
endTime: string
) {
const cacheKey = `monitoring:${projectId}:${pointId}:${startTime}:${endTime}`;
// 1. 检查本地缓存
const localData = this.localCache.get(cacheKey);
if (localData && Date.now() - localData.timestamp < this.LOCAL_TTL) {
return localData.data;
}
// 2. 检查 Redis 缓存
const redisData = await redis.get(cacheKey);
if (redisData) {
const parsed = JSON.parse(redisData);
this.localCache.set(cacheKey, { data: parsed, timestamp: Date.now() });
return parsed;
}
// 3. 查询数据库
const data = await db.query(/* ... */);
// 4. 写入缓存
await redis.setex(cacheKey, 3600, JSON.stringify(data)); // 1小时TTL
this.localCache.set(cacheKey, { data, timestamp: Date.now() });
return data;
}
// 数据写入后清除缓存
async invalidateCache(projectId: string, pointId: string) {
const pattern = `monitoring:${projectId}:${pointId}:*`;
const keys = await redis.keys(pattern);
if (keys.length > 0) {
await redis.del(...keys);
}
// 清除本地缓存
for (const key of this.localCache.keys()) {
if (key.startsWith(`monitoring:${projectId}:${pointId}`)) {
this.localCache.delete(key);
}
}
}
}
// 系统启动时预热热点数据
async function warmupCache() {
// 1. 预热活跃项目列表
const activeProjects = await db.query(`
SELECT id FROM projects
WHERE status = 'active'
ORDER BY updated_at DESC
LIMIT 100
`);
for (const project of activeProjects) {
// 缓存项目基本信息
const projectInfo = await projectService.getById(project.id);
await redis.setex(
`project:info:${project.id}`,
300,
JSON.stringify(projectInfo)
);
// 缓存最近7天监测数据摘要
const summary = await monitoringService.getRecentSummary(project.id, 7);
await redis.setex(
`monitoring:summary:${project.id}:7d`,
600,
JSON.stringify(summary)
);
}
console.log(`缓存预热完成: ${activeProjects.length} 个项目`);
}
// 路由懒加载
const routes = [
{
path: '/projects',
component: () => import('./views/ProjectList.vue'),
},
{
path: '/projects/:id',
component: () => import('./views/ProjectDetail.vue'),
},
{
path: '/data/monitoring',
component: () => import('./views/MonitoringData.vue'),
},
];
// 组件异步加载
const ChartComponent = defineAsyncComponent(() =>
import('./components/Chart.vue')
);
// 虚拟列表(大数据量表格)
import { VirtualList } from 'vue-virtual-scroller';
<template>
<VirtualList
:items="monitoringData"
:item-height="48"
:buffer="200"
>
<template #default="{ item }">
<DataRow :data="item" />
</template>
</VirtualList>
</template>
// ECharts 大数据量优化
const chartOption = {
// 使用 dataZoom 实现数据区域缩放
dataZoom: [
{ type: 'inside', start: 0, end: 10 },
{ type: 'slider', start: 0, end: 10 },
],
// 大数据量使用 sampling
series: [{
type: 'line',
data: largeDataSet,
sampling: 'lttb', // Largest Triangle Three Buckets 算法
showSymbol: false, // 不显示数据点标记
lineStyle: { width: 1 },
}],
// 启用 progressive 渲染
progressive: 1000,
progressiveThreshold: 5000,
};
// 数据降采样(服务端或客户端)
function lttbDownsample(data: DataPoint[], threshold: number): DataPoint[] {
const sampled = [];
sampled.push(data[0]);
const a = (data.length - 2) / (threshold - 2);
let maxAreaPoint = null;
let maxArea = -1;
for (let i = 0; i < threshold - 2; i++) {
const avgRangeStart = Math.floor((i + 1) * a) + 1;
const avgRangeEnd = Math.floor((i + 2) * a) + 1;
const avgRange = data.slice(avgRangeStart, avgRangeEnd);
const avgPoint = avgRange.reduce((sum, p) => sum + p.value, 0) / avgRange.length;
const rangeOffs = Math.floor(i * a) + 1;
const rangeTo = Math.floor((i + 1) * a) + 1;
for (let j = rangeOffs; j < rangeTo; j++) {
const area = Math.abs(
(data[0].value - avgPoint) * (data[j].time - data[avgRangeStart].time) -
(data[0].time - data[avgRangeStart].time) * (data[j].value - avgPoint)
);
if (area > maxArea) {
maxArea = area;
maxAreaPoint = data[j];
}
}
sampled.push(maxAreaPoint);
}
sampled.push(data[data.length - 1]);
return sampled;
}
// 请求合并(防抖)
class RequestBatcher {
private pendingRequests = new Map<string, Promise<any>>();
async batchRequest<T>(key: string, fn: () => Promise<T>): Promise<T> {
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key)!;
}
const promise = fn().finally(() => {
this.pendingRequests.delete(key);
});
this.pendingRequests.set(key, promise);
return promise;
}
}
// 使用示例
const batcher = new RequestBatcher();
// 多个组件同时请求同一数据,只发送一次请求
const data = await batcher.batchRequest('project:123', () =>
api.getProject('123')
);
// 报告异步生成 + 轮询
class ReportGenerator {
async generateReport(config: ReportConfig): Promise<string> {
// 1. 提交生成任务
const job = await api.reports.submit(config);
// 2. 轮询状态
return this.pollJobStatus(job.id, {
maxAttempts: 60,
interval: 5000, // 5秒
});
}
private async pollJobStatus(
jobId: string,
options: { maxAttempts: number; interval: number }
): Promise<string> {
for (let i = 0; i < options.maxAttempts; i++) {
const status = await api.reports.getStatus(jobId);
if (status.status === 'completed') {
return status.downloadUrl;
}
if (status.status === 'failed') {
throw new Error(`报告生成失败: ${status.error}`);
}
await new Promise(r => setTimeout(r, options.interval));
}
throw new Error('报告生成超时');
}
}
# Python 模板编译缓存
from jinja2 import Environment, FileSystemLoader
import hashlib
class CachedTemplateEngine:
def __init__(self, template_dir: str):
self.env = Environment(
loader=FileSystemLoader(template_dir),
cache_size=100, # 缓存100个编译后的模板
auto_reload=False, # 生产环境关闭自动重载
)
self._compiled_cache = {}
def get_template(self, template_name: str):
# 使用模板内容哈希作为缓存键
template_source = self.env.loader.get_source(self.env, template_name)
cache_key = hashlib.md5(template_source[0].encode()).hexdigest()
if cache_key not in self._compiled_cache:
template = self.env.get_template(template_name)
self._compiled_cache[cache_key] = template
return self._compiled_cache[cache_key]
# 监控指标配置
metrics:
- name: api_response_time
type: histogram
labels: [endpoint, method, status]
buckets: [0.01, 0.05, 0.1, 0.2, 0.5, 1, 2, 5]
- name: db_query_time
type: histogram
labels: [query_type, table]
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1]
- name: cache_hit_ratio
type: gauge
labels: [cache_layer]
- name: active_connections
type: gauge
labels: [service]
- name: memory_usage
type: gauge
labels: [service, type]
# 告警规则配置
alerts:
- name: api_latency_high
condition: api_response_time_p95 > 500ms
duration: 5m
severity: warning
labels:
team: backend
annotations:
summary: "API 响应延迟过高"
description: "{{ $labels.endpoint }} P95 延迟超过 500ms"
- name: db_slow_queries
condition: rate(db_slow_query_count[5m]) > 10
duration: 5m
severity: warning
labels:
team: dba
annotations:
summary: "慢查询数量增加"
- name: cache_hit_ratio_low
condition: cache_hit_ratio < 0.8
duration: 10m
severity: warning
labels:
team: backend
annotations:
summary: "缓存命中率过低"
- name: memory_usage_high
condition: memory_usage > 0.85
duration: 5m
severity: critical
labels:
team: ops
annotations:
summary: "内存使用率过高"
Terminal window
# PostgreSQL 性能诊断
# 1. 查看当前连接
SELECT * FROM pg_stat_activity WHERE state = 'active';
# 2. 查看锁等待
SELECT * FROM pg_locks WHERE NOT granted;
# 3. 查看表膨胀
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric/nullif(n_live_tup,0)*100, 2) as dead_pct
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
# 4. 查看缓存命中率
SELECT
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) * 100 as cache_hit_ratio
FROM pg_statio_user_tables;
# Redis 性能诊断
# 1. 查看内存使用
redis-cli INFO memory
# 2. 查看慢查询
redis-cli SLOWLOG GET 10
# 3. 查看大 Key
redis-cli --bigkeys
# 4. 查看连接数
redis-cli INFO clients

文档版本: v3.2.0 | 最后更新: 2026-07-08

引用与复核把知识带回真实工程判断

引用时保留页面与来源线索;涉及标准条文、阈值、频率和项目结论,请回到现行依据与责任人复核。

查看 Agent 使用规则