第8章 数据库设计与操作
作者
谭策 — 独立开发者 | AIOps 领域探索者
- 🌐 项目官网:ITOpsAgentinfo
- 📝 博客:zjzwfw.cloud
- 📧 邮箱:huawei_network@foxmail.com
- 💬 微信公众号:IT Online

许可证
MPL-2.0 © 谭策
本章导读
在前面几章中,我们已经完成了前后端开发基础的学习。本章将深入数据库设计与操作,理解项目使用的 33 张数据表的结构、关系和设计思想。
本章定位:数据库是应用的心脏。理解数据库设计后,你不仅能更高效地编写查询语句,还能在需要扩展功能时正确地设计新表。
学习目标
阅读完本章后,你将能够:
- 理解项目 44 张数据表的完整结构和用途(40张初始创建 + 16次增量迁移)
- 画出核心数据表之间的关系图
- 掌握 better-sqlite3 的各种查询模式
- 理解项目的数据库初始化流程
- 设计新的数据表并编写迁移脚本
- 使用索引和事务优化查询性能
- 理解项目的数据备份和恢复机制
8.1 数据库概览
8.1.1 为什么选择 SQLite?
SQLite 是一个嵌入式的关系型数据库,它将整个数据库存储在一个单一文件中。
| 特性 | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| 部署方式 | 嵌入式(无需单独服务器) | 独立服务器 | 独立服务器 |
| 数据文件 | 单个文件 | 多文件 | 多文件 |
| 并发能力 | 适合中小型应用 | 高并发 | 高并发 |
| 运维成本 | 极低 | 中等 | 较高 |
| 适用场景 | 个人项目、中小应用 | 大型 Web 应用 | 大型 Web 应用 |
项目选择 SQLite 的原因:
- 零配置:不需要安装和配置数据库服务器
- 轻量级:数据库文件只有几 MB
- 便携性:备份数据库就是复制一个文件
- Docker 友好:容器化部署时不需要额外的数据库容器
8.1.2 数据库文件位置
data/
└── itops.db ← SQLite 数据库文件(单个文件存储所有数据)8.1.3 数据库配置
// backend/src/models/database.ts
import Database from 'better-sqlite3';
const db = new Database('./data/itops.db', {
// verbose: console.log // 取消注释可以打印所有 SQL 语句
});
// 启用 WAL 模式(Write-Ahead Logging)
db.pragma('journal_mode = WAL');
// 启用外键约束
db.pragma('foreign_keys = ON');WAL 模式的作用:
传统模式:
写入数据 → 直接修改数据库文件 → 如果中途断电,数据可能损坏
WAL 模式:
写入数据 → 先写入 WAL 日志文件 → 确认成功后再修改数据库文件
即使中途断电,也能从 WAL 日志恢复数据完整性能优化配置:
项目中的数据库初始化包含了 12 项性能优化配置,不仅仅是 WAL 和外键:
// 完整的数据库配置清单
db.pragma('journal_mode = WAL'); // 1. WAL 模式:写入不阻塞读取
db.pragma('foreign_keys = ON'); // 2. 外键约束:数据完整性
db.pragma('busy_timeout = 5000'); // 3. 忙等待超时:5秒(防止并发写入冲突)
db.pragma('synchronous = NORMAL'); // 4. 同步模式:NORMAL 平衡性能与安全
db.pragma('temp_store = MEMORY'); // 5. 临时表存储:使用内存而非磁盘
db.pragma('mmap_size = 1073741824'); // 6. 内存映射:1GB(加速只读查询)
db.pragma('cache_size = -16384'); // 7. 页面缓存:16MB(负号表示 KB)
db.pragma('wal_autocheckpoint = 1000'); // 8. 自动检查点:每 1000 页触发
db.pragma('cache_spill = OFF'); // 9. 缓存溢出:关闭(减少磁盘 I/O)
db.pragma('journal_size_limit = 67108864');// 10. 日志大小限制:64MB
db.pragma('locking_mode = NORMAL'); // 11. 锁定模式:NORMAL(允许并发读取)
db.pragma('automatic_index = ON'); // 12. 自动索引:开启(优化无索引查询)关键配置说明:
| 配置 | 作用 | 性能影响 |
|---|---|---|
mmap_size = 1GB | 将数据库文件映射到内存 | 读查询性能提升 10-100 倍 |
cache_size = 16MB | SQLite 页面缓存大小 | 减少磁盘 I/O,适合频繁读取 |
busy_timeout = 5000 | 写入冲突时等待时间 | 避免并发写入时的 "database is locked" 错误 |
wal_autocheckpoint = 1000 | 检查点频率 | 平衡 WAL 文件大小和恢复时间 |
每日定时维护:
项目还配置了每日凌晨 3 点的数据库定时维护任务:
// 每日凌晨 3 点执行数据库维护
schedule.scheduleJob('0 3 * * *', () => {
db.exec('PRAGMA optimize'); // 优化查询计划
db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); // 截断 WAL 日志
db.exec('VACUUM'); // 清理碎片,压缩数据库
});8.2 数据表分类
项目的 44 张表可以分为以下几大类(40张初始创建 + 16次增量迁移):
ITOps Agent Platform 数据库
├── 核心业务表(5张)
│ ├── users - 用户管理
│ ├── servers - 服务器管理
│ ├── agents - Agent 管理
│ ├── workflows - 工作流管理
│ └── tasks - 任务管理
│
├── 告警相关表(7张)
│ ├── alerts - 告警记录
│ ├── alert_configs - 告警配置
│ ├── alert_notifications - 告警通知
│ ├── alert_workflow_mappings - 告警-工作流映射
│ ├── alert_webhook_logs - Webhook 日志
│ ├── alert_noise_reduction - 告警降噪
│ └── root_cause_analyses - 根因分析
│
├── 运维相关表(7张)
│ ├── server_groups - 服务器分组
│ ├── server_group_mapping - 服务器-分组关联
│ ├── server_command_history - 命令历史
│ ├── server_metrics - 服务器指标
│ ├── compliance_checks - 合规检查
│ ├── scripts - 脚本管理
│ └── scheduled_tasks - 定时任务
│
├── 修复相关表(4张)
│ ├── remediation_policies - 修复策略
│ ├── remediation_executions - 修复执行
│ ├── remediation_history - 修复历史
│ └── remediation_cooldowns - 修复冷却
│
├── 知识相关表(2张)
│ ├── knowledge_base - 知识库
│ └── copilot_conversations - Copilot 对话
│
├── 通知相关表(2张)
│ ├── notifications - 通知记录
│ └── notification_config - 通知配置
│
├── 报告相关表(2张)
│ ├── reports - 报告管理
│ └── report_schedules - 报告计划
│
├── 审计日志表(1张)
│ └── audit_logs - 审计日志
│
├── 系统配置表(2张)
│ ├── settings - 系统设置
│ └── encryption_keys - 加密密钥
│
├── Agent 相关表(1张)
│ └── agent_executions - Agent 执行记录
│
└── 安全相关表(1张)
└── token_blacklist - Token 黑名单8.3 核心数据表详解
8.3.1 用户表(users)
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT,
role TEXT NOT NULL,
enabled INTEGER DEFAULT 1,
password_must_change INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);| 字段 | 类型 | 说明 |
|---|---|---|
id | INTEGER | 自动递增的主键 |
username | TEXT | 用户名,唯一约束 |
password | TEXT | bcrypt 加密后的密码哈希 |
email | TEXT | 邮箱地址 |
role | TEXT | 角色:admin、operator、viewer |
enabled | INTEGER | 是否启用:1=启用,0=禁用 |
password_must_change | INTEGER | 首次登录是否强制改密 |
created_at | DATETIME | 创建时间 |
updated_at | DATETIME | 更新时间 |
注意事项:
- 密码字段存储的是 bcrypt 哈希值,永远不是明文
- 用户登录时使用
bcrypt.compare()验证密码 role字段决定用户的操作权限
8.3.2 服务器表(servers)
CREATE TABLE IF NOT EXISTS servers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
hostname TEXT NOT NULL,
port INTEGER DEFAULT 22,
username TEXT NOT NULL,
password TEXT,
private_key TEXT,
use_ssh_key INTEGER DEFAULT 0,
description TEXT,
tags TEXT,
enabled INTEGER DEFAULT 1,
last_connected DATETIME,
os TEXT,
cpu_cores INTEGER,
memory_gb REAL,
disk_gb REAL,
ip_address TEXT,
private_ip TEXT,
cloud_provider TEXT,
cloud_instance_id TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);关键字段说明:
| 字段 | 说明 |
|---|---|
id | UUID 格式的主键 |
hostname | 服务器地址(IP 或域名) |
use_ssh_key | 1=使用 SSH 密钥登录,0=使用密码登录 |
private_key | SSH 私钥内容(加密存储) |
tags | JSON 格式的标签数组 |
os | 操作系统信息(自动采集) |
cpu_cores | CPU 核心数(自动采集) |
cloud_provider | 云厂商(AWS、阿里云等) |
8.3.3 Agent 表(agents)
CREATE TABLE IF NOT EXISTS agents (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
avatar TEXT,
role TEXT,
system_prompt TEXT,
model TEXT DEFAULT 'doubao-4o',
temperature REAL DEFAULT 0.7,
enabled INTEGER DEFAULT 1,
is_preset INTEGER DEFAULT 0,
category TEXT,
tags TEXT,
description TEXT,
usage_count INTEGER DEFAULT 0,
last_used_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);核心概念:
system_prompt:Agent 的系统提示词,决定 Agent 的行为和能力model:使用的 AI 模型名称temperature:AI 生成的随机性(0=确定,1=随机)is_preset:是否为预设 Agent(项目自带的)
8.3.4 工作流表(workflows)
CREATE TABLE IF NOT EXISTS workflows (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
nodes TEXT,
edges TEXT,
agent_configs TEXT,
is_template INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);核心概念:
nodes:JSON 格式的节点数组(工作流的各个步骤)edges:JSON 格式的连线数组(节点之间的执行顺序)agent_configs:JSON 格式的 Agent 配置is_template:是否为预设工作流模板
8.3.5 告警表(alerts)
CREATE TABLE IF NOT EXISTS alerts (
id TEXT PRIMARY KEY,
source TEXT NOT NULL,
severity TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT,
metadata TEXT,
related_task_id TEXT,
status TEXT DEFAULT 'new',
alert_fingerprint TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);关键字段:
source:告警来源(Prometheus、Zabbix 等)severity:严重级别(critical、warning、info)status:告警状态(new、acknowledged、resolved)alert_fingerprint:告警指纹,用于去重和降噪
8.4 表关系图
8.4.1 核心关系
users (1) ──────→ (N) audit_logs
│
│
└─────────→ (N) copilot_conversations
servers (1) ────→ (N) server_command_history
│
├──→ (N) server_metrics
│
├──→ (N) compliance_checks
│
└──→ (N) ←── server_groups
│
└──→ (N) server_group_mapping
agents (1) ────→ (N) agent_executions
workflows (1) ──→ (N) tasks
│
├──→ (N) alert_workflow_mappings
│
├──→ (N) scheduled_tasks
│
└──→ (N) remediation_policies
alerts (1) ────→ (N) root_cause_analyses
│
├──→ (N) remediation_executions
│
├──→ (N) alert_noise_reduction
│
└──→ (1) alert_workflow_mappings8.4.2 一对多关系说明
| 父表 | 子表 | 关系说明 |
|---|---|---|
| users | audit_logs | 一个用户可以产生多条审计日志 |
| servers | server_command_history | 一台服务器有多条命令执行记录 |
| servers | server_metrics | 一台服务器有多条监控指标 |
| agents | agent_executions | 一个 Agent 有多条执行记录 |
| workflows | tasks | 一个工作流可以产生多个任务 |
| alerts | remediation_executions | 一个告警可以触发多次修复执行 |
| server_groups | server_group_mapping | 一个分组可以包含多台服务器 |
| remediation_policies | remediation_executions | 一个策略可以产生多次执行记录 |
8.4.3 多对多关系
servers ←── server_group_mapping ──→ server_groups
(N) (关联表) (N)
说明:一台服务器可以属于多个分组,一个分组可以包含多台服务器8.5 数据库操作实战
8.5.1 基础查询
// 查询所有用户
const users = db.prepare('SELECT id, username, email, role FROM users').all();
// 查询单个用户
const user = db.prepare(
'SELECT * FROM users WHERE id = ?'
).get(userId);
// 条件查询
const onlineServers = db.prepare(
'SELECT * FROM servers WHERE enabled = 1'
).all();
// 排序查询
const recentAlerts = db.prepare(
'SELECT * FROM alerts ORDER BY created_at DESC LIMIT 20'
).all();
// 聚合查询
const stats = db.prepare(`
SELECT
COUNT(*) as total_servers,
SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) as online_count
FROM servers
`).get();8.5.2 插入数据
// 插入单条数据
const result = db.prepare(`
INSERT INTO servers (id, name, hostname, port, username, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`).run(randomUUID(), 'prod-server', '192.168.1.1', 22, 'root');
console.log('插入的 ID:', result.lastInsertRowid);
console.log('影响行数:', result.changes);8.5.3 更新数据
// 更新单条数据
db.prepare(`
UPDATE servers
SET hostname = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`).run('192.168.1.100', serverId);
// 批量更新
db.prepare(`
UPDATE alerts
SET status = 'resolved', updated_at = CURRENT_TIMESTAMP
WHERE status = 'new' AND created_at < datetime('now', '-7 days')
`).run();8.5.4 删除数据
// 删除单条数据
db.prepare('DELETE FROM servers WHERE id = ?').run(serverId);
// 级联删除(需要外键约束)
db.prepare('DELETE FROM server_groups WHERE id = ?').run(groupId);
// 关联表中的记录会自动删除(ON DELETE CASCADE)8.5.5 事务操作
// 创建事务:添加服务器并同时记录审计日志
const addServer = db.transaction((data: {
id: string;
name: string;
hostname: string;
port: number;
username: string;
userId: string;
}) => {
// 1. 插入服务器
db.prepare(`
INSERT INTO servers (id, name, hostname, port, username, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`).run(data.id, data.name, data.hostname, data.port, data.username);
// 2. 记录审计日志
db.prepare(`
INSERT INTO audit_logs (id, user_id, action, resource_type, resource_id, details, ip_address, created_at)
VALUES (?, ?, 'create', 'server', ?, ?, ?, CURRENT_TIMESTAMP)
`).run(
randomUUID(),
data.userId,
data.id,
JSON.stringify({ name: data.name }),
'127.0.0.1'
);
// 3. 如果任何一步失败,整个事务自动回滚
return { success: true, serverId: data.id };
});
// 执行事务
try {
addServer.run({
id: randomUUID(),
name: 'test-server',
hostname: '192.168.1.1',
port: 22,
username: 'root',
userId: '1'
});
console.log('添加成功');
} catch (error) {
console.error('添加失败,事务已回滚:', error);
}8.6 数据库初始化
8.6.1 初始化流程
// backend/src/models/database.ts 简化版
import Database from 'better-sqlite3';
export const db = new Database('./data/itops.db');
export function initializeDatabase(): void {
// 1. 启用 WAL 模式
db.pragma('journal_mode = WAL');
// 2. 启用外键约束
db.pragma('foreign_keys = ON');
// 3. 创建所有表
createTables();
// 4. 执行数据库迁移
runMigrations();
// 5. 插入预设数据
seedPresetData();
console.log('数据库初始化完成');
}
function createTables(): void {
// 用户表
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
...
)
`);
// 服务器表
db.exec(`
CREATE TABLE IF NOT EXISTS servers (
id TEXT PRIMARY KEY,
...
)
`);
// ... 其他表的创建语句
}8.6.2 预设数据
项目启动时会自动插入预设数据:
预设数据清单:
├── 1 个默认管理员用户(admin)
├── 9 个预设 Agent
│ ├── 告警分析 Agent
│ ├── 故障诊断 Agent
│ ├── 系统巡检 Agent
│ ├── 安全审计 Agent
│ ├── 性能优化 Agent
│ ├── 配置管理 Agent
│ ├── 日志分析 Agent
│ ├── 容量规划 Agent
│ └── 合规检查 Agent
├── 6 个预设工作流
│ ├── 告警自动处理工作流
│ ├── 系统巡检工作流
│ ├── 故障诊断工作流
│ ├── 安全扫描工作流
│ ├── 性能分析工作流
│ └── 合规检查工作流
├── 知识库条目
├── 告警映射规则
└── 报告模板8.6.3 数据库迁移
当需要修改已有表结构时,使用迁移脚本:
// backend/src/models/migrations.ts 模式
export function runMigrations(): void {
// 检查是否已经执行过迁移
const migrationVersion = db.prepare(
"SELECT value FROM settings WHERE key = 'db_version'"
).get() as { value: string } | undefined;
const currentVersion = migrationVersion ? parseInt(migrationVersion.value) : 0;
// 按顺序执行未执行的迁移
if (currentVersion < 1) {
runMigration1();
db.prepare("UPDATE settings SET value = '1' WHERE key = 'db_version'").run();
}
if (currentVersion < 2) {
runMigration2();
db.prepare("UPDATE settings SET value = '2' WHERE key = 'db_version'").run();
}
// ... 更多迁移
}
function runMigration1(): void {
// 示例:添加新字段
db.exec(`
ALTER TABLE tasks ADD COLUMN report_id TEXT;
`);
}8.7 查询优化
8.7.1 索引
对于频繁查询的字段,可以创建索引来加速查询:
// 创建索引
db.exec(`
CREATE INDEX IF NOT EXISTS idx_alerts_status ON alerts(status);
CREATE INDEX IF NOT EXISTS idx_alerts_created_at ON alerts(created_at);
CREATE INDEX IF NOT EXISTS idx_tasks_workflow_id ON tasks(workflow_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at);
`);什么时候需要索引?
| 场景 | 是否需要索引 | 原因 |
|---|---|---|
| WHERE 条件字段 | 是 | 加速条件筛选 |
| ORDER BY 字段 | 是 | 加速排序 |
| JOIN 字段 | 是 | 加速表连接 |
| 小表全表扫描 | 否 | 数据量少,索引反而增加开销 |
| 频繁更新的字段 | 否 | 索引会降低写入性能 |
8.7.2 查询性能分析
使用 EXPLAIN QUERY PLAN 查看查询执行计划:
// 查看查询的执行计划
const plan = db.prepare(`
EXPLAIN QUERY PLAN
SELECT * FROM alerts WHERE status = 'new' ORDER BY created_at DESC
`).all();
console.log(plan);
// 如果输出包含 "SCAN TABLE",说明是全表扫描
// 如果输出包含 "SEARCH TABLE ... USING INDEX",说明使用了索引8.7.3 分页查询
// 分页查询告警
function getAlerts(page: number, pageSize: number) {
const offset = (page - 1) * pageSize;
const alerts = db.prepare(`
SELECT * FROM alerts
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`).all(pageSize, offset);
const total = db.prepare(
'SELECT COUNT(*) as count FROM alerts'
).get() as { count: number };
return {
data: alerts,
total: total.count,
page,
pageSize,
totalPages: Math.ceil(total.count / pageSize)
};
}8.8 数据备份与恢复
8.8.1 备份方式
由于 SQLite 是单文件数据库,备份非常简单:
# 方式 1:直接复制文件
cp data/itops.db data/itops-backup-$(date +%Y%m%d).db
# 方式 2:使用 SQLite 备份命令
sqlite3 data/itops.db ".backup 'data/itops-backup.db'"
# 方式 3:导出为 SQL
sqlite3 data/itops.db ".dump" > data/backup.sql8.8.2 恢复方式
# 方式 1:直接替换文件
cp data/itops-backup.db data/itops.db
# 方式 2:从 SQL 导入
sqlite3 data/itops.db < data/backup.sql8.8.3 项目中的备份服务
项目提供了自动备份服务:
// backend/src/services/backupService.ts 模式
export const backupService = {
init(): void {
// 启动定时自动备份
setInterval(() => {
this.createBackup();
}, 24 * 60 * 60 * 1000); // 每天备份一次
},
createBackup(): string {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupPath = `./data/backup-${timestamp}.db`;
// 复制数据库文件
const source = fs.readFileSync('./data/itops.db');
fs.writeFileSync(backupPath, source);
logger.info(`备份完成: ${backupPath}`);
return backupPath;
}
};8.9 数据库设计最佳实践
8.9.1 主键选择
| 类型 | 适用场景 | 示例 |
|---|---|---|
| INTEGER AUTOINCREMENT | 内部表,不暴露给外部 | users 表 |
| TEXT (UUID) | 需要暴露给前端的 ID | servers、agents、alerts 等 |
8.9.2 时间字段
-- 使用 CURRENT_TIMESTAMP 自动记录时间
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
-- 使用 SQLite 函数计算时间
-- 7 天前的时间
datetime('now', '-7 days')
-- 当前时间加 1 小时
datetime('now', '+1 hour')8.9.3 JSON 字段存储
SQLite 没有原生的 JSON 类型,但可以用 TEXT 存储 JSON 字符串:
// 写入
db.prepare('INSERT INTO workflows (nodes, edges) VALUES (?, ?)').run(
JSON.stringify([{ id: '1', type: 'agent' }]),
JSON.stringify([{ source: '1', target: '2' }])
);
// 读取
const workflow = db.prepare('SELECT * FROM workflows WHERE id = ?').get(id);
const nodes = JSON.parse(workflow.nodes);
const edges = JSON.parse(workflow.edges);8.9.4 布尔值存储
SQLite 没有 BOOLEAN 类型,使用 INTEGER 代替:
enabled INTEGER DEFAULT 1 -- 1 = true, 0 = false
is_preset INTEGER DEFAULT 0本章小结
本章深入学习了项目的数据库设计:
- 数据库概览:44 张表、SQLite 嵌入式数据库、WAL 模式
- 数据表分类:核心业务、告警、运维、修复、知识、通知、报告、审计、系统配置等 10 类
- 核心表详解:users、servers、agents、workflows、alerts 等 5 张核心表的结构
- 表关系图:一对多、多对多关系梳理
- 数据库操作:增删改查、事务、分页
- 数据库初始化:创建表、迁移、预设数据
- 查询优化:索引、执行计划、分页查询
- 数据备份:文件复制、SQL 导出、自动备份
- 设计最佳实践:主键选择、时间字段、JSON 存储、布尔值存储
核心思想:数据库设计是应用架构的核心,好的数据库设计让应用开发事半功倍。
本章练习
基础练习
数据表理解:不看代码,凭记忆回答以下问题:
- 项目中有多少张表?分为几大类?
servers表的主键类型是什么?users表呢?为什么不同?- 哪些表之间有外键关联?
SQL 练习:编写以下 SQL 语句:
- 查询所有启用的 Agent
- 统计每个 Agent 的执行次数
- 查询最近 7 天的告警数量
数据库操作:使用 Node.js REPL 连接到项目的数据库,执行几条查询语句
进阶练习
创建新表:设计一个
maintenance_windows(维护窗口)表,包含以下字段:- id(主键)
- server_id(外键,关联 servers 表)
- start_time(开始时间)
- end_time(结束时间)
- description(描述)
- enabled(是否启用)
- created_at、updated_at
编写迁移脚本:为
servers表添加location(机房位置)字段编写查询优化:为
audit_logs表的常用查询场景创建合适的索引
思考题
为什么项目的
users表使用INTEGER AUTOINCREMENT主键,而servers等表使用TEXT (UUID)主键?项目将所有数据存储在单个 SQLite 文件中,如果数据库文件损坏了怎么办?如何防范?
SQLite 的 WAL 模式有什么优势?如果你的项目日增数据量达到 10GB,还应该使用 SQLite 吗?为什么?
延伸阅读
本章回顾:你已经全面理解了项目的数据库设计!开发篇上(第 5-8 章)到此结束。在下一章中,我们将进入开发篇下,学习 API 开发实战。
