Skip to content

第8章 数据库设计与操作

作者

谭策 — 独立开发者 | AIOps 领域探索者

IT Online 微信公众号

许可证

MPL-2.0 © 谭策

本章导读

在前面几章中,我们已经完成了前后端开发基础的学习。本章将深入数据库设计与操作,理解项目使用的 33 张数据表的结构、关系和设计思想。

本章定位:数据库是应用的心脏。理解数据库设计后,你不仅能更高效地编写查询语句,还能在需要扩展功能时正确地设计新表。

学习目标

阅读完本章后,你将能够:

  • 理解项目 44 张数据表的完整结构和用途(40张初始创建 + 16次增量迁移)
  • 画出核心数据表之间的关系图
  • 掌握 better-sqlite3 的各种查询模式
  • 理解项目的数据库初始化流程
  • 设计新的数据表并编写迁移脚本
  • 使用索引和事务优化查询性能
  • 理解项目的数据备份和恢复机制

8.1 数据库概览

8.1.1 为什么选择 SQLite?

SQLite 是一个嵌入式的关系型数据库,它将整个数据库存储在一个单一文件中。

特性SQLiteMySQLPostgreSQL
部署方式嵌入式(无需单独服务器)独立服务器独立服务器
数据文件单个文件多文件多文件
并发能力适合中小型应用高并发高并发
运维成本极低中等较高
适用场景个人项目、中小应用大型 Web 应用大型 Web 应用

项目选择 SQLite 的原因:

  • 零配置:不需要安装和配置数据库服务器
  • 轻量级:数据库文件只有几 MB
  • 便携性:备份数据库就是复制一个文件
  • Docker 友好:容器化部署时不需要额外的数据库容器

8.1.2 数据库文件位置

data/
└── itops.db    ← SQLite 数据库文件(单个文件存储所有数据)

8.1.3 数据库配置

typescript
// 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 和外键:

typescript
// 完整的数据库配置清单
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 = 16MBSQLite 页面缓存大小减少磁盘 I/O,适合频繁读取
busy_timeout = 5000写入冲突时等待时间避免并发写入时的 "database is locked" 错误
wal_autocheckpoint = 1000检查点频率平衡 WAL 文件大小和恢复时间

每日定时维护

项目还配置了每日凌晨 3 点的数据库定时维护任务:

typescript
// 每日凌晨 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)

sql
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
);
字段类型说明
idINTEGER自动递增的主键
usernameTEXT用户名,唯一约束
passwordTEXTbcrypt 加密后的密码哈希
emailTEXT邮箱地址
roleTEXT角色:admin、operator、viewer
enabledINTEGER是否启用:1=启用,0=禁用
password_must_changeINTEGER首次登录是否强制改密
created_atDATETIME创建时间
updated_atDATETIME更新时间

注意事项

  • 密码字段存储的是 bcrypt 哈希值,永远不是明文
  • 用户登录时使用 bcrypt.compare() 验证密码
  • role 字段决定用户的操作权限

8.3.2 服务器表(servers)

sql
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
);

关键字段说明

字段说明
idUUID 格式的主键
hostname服务器地址(IP 或域名)
use_ssh_key1=使用 SSH 密钥登录,0=使用密码登录
private_keySSH 私钥内容(加密存储)
tagsJSON 格式的标签数组
os操作系统信息(自动采集)
cpu_coresCPU 核心数(自动采集)
cloud_provider云厂商(AWS、阿里云等)

8.3.3 Agent 表(agents)

sql
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)

sql
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)

sql
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_mappings

8.4.2 一对多关系说明

父表子表关系说明
usersaudit_logs一个用户可以产生多条审计日志
serversserver_command_history一台服务器有多条命令执行记录
serversserver_metrics一台服务器有多条监控指标
agentsagent_executions一个 Agent 有多条执行记录
workflowstasks一个工作流可以产生多个任务
alertsremediation_executions一个告警可以触发多次修复执行
server_groupsserver_group_mapping一个分组可以包含多台服务器
remediation_policiesremediation_executions一个策略可以产生多次执行记录

8.4.3 多对多关系

servers ←── server_group_mapping ──→ server_groups
  (N)          (关联表)                (N)

说明:一台服务器可以属于多个分组,一个分组可以包含多台服务器

8.5 数据库操作实战

8.5.1 基础查询

typescript
// 查询所有用户
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 插入数据

typescript
// 插入单条数据
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 更新数据

typescript
// 更新单条数据
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 删除数据

typescript
// 删除单条数据
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 事务操作

typescript
// 创建事务:添加服务器并同时记录审计日志
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 初始化流程

typescript
// 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 数据库迁移

当需要修改已有表结构时,使用迁移脚本:

typescript
// 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 索引

对于频繁查询的字段,可以创建索引来加速查询:

typescript
// 创建索引
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 查看查询执行计划:

typescript
// 查看查询的执行计划
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 分页查询

typescript
// 分页查询告警
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 是单文件数据库,备份非常简单:

bash
# 方式 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.sql

8.8.2 恢复方式

bash
# 方式 1:直接替换文件
cp data/itops-backup.db data/itops.db

# 方式 2:从 SQL 导入
sqlite3 data/itops.db < data/backup.sql

8.8.3 项目中的备份服务

项目提供了自动备份服务:

typescript
// 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)需要暴露给前端的 IDservers、agents、alerts 等

8.9.2 时间字段

sql
-- 使用 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 字符串:

typescript
// 写入
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 代替:

sql
enabled INTEGER DEFAULT 1      -- 1 = true, 0 = false
is_preset INTEGER DEFAULT 0

本章小结

本章深入学习了项目的数据库设计:

  1. 数据库概览:44 张表、SQLite 嵌入式数据库、WAL 模式
  2. 数据表分类:核心业务、告警、运维、修复、知识、通知、报告、审计、系统配置等 10 类
  3. 核心表详解:users、servers、agents、workflows、alerts 等 5 张核心表的结构
  4. 表关系图:一对多、多对多关系梳理
  5. 数据库操作:增删改查、事务、分页
  6. 数据库初始化:创建表、迁移、预设数据
  7. 查询优化:索引、执行计划、分页查询
  8. 数据备份:文件复制、SQL 导出、自动备份
  9. 设计最佳实践:主键选择、时间字段、JSON 存储、布尔值存储

核心思想:数据库设计是应用架构的核心,好的数据库设计让应用开发事半功倍。


本章练习

基础练习

  1. 数据表理解:不看代码,凭记忆回答以下问题:

    • 项目中有多少张表?分为几大类?
    • servers 表的主键类型是什么?users 表呢?为什么不同?
    • 哪些表之间有外键关联?
  2. SQL 练习:编写以下 SQL 语句:

    • 查询所有启用的 Agent
    • 统计每个 Agent 的执行次数
    • 查询最近 7 天的告警数量
  3. 数据库操作:使用 Node.js REPL 连接到项目的数据库,执行几条查询语句

进阶练习

  1. 创建新表:设计一个 maintenance_windows(维护窗口)表,包含以下字段:

    • id(主键)
    • server_id(外键,关联 servers 表)
    • start_time(开始时间)
    • end_time(结束时间)
    • description(描述)
    • enabled(是否启用)
    • created_at、updated_at
  2. 编写迁移脚本:为 servers 表添加 location(机房位置)字段

  3. 编写查询优化:为 audit_logs 表的常用查询场景创建合适的索引

思考题

  1. 为什么项目的 users 表使用 INTEGER AUTOINCREMENT 主键,而 servers 等表使用 TEXT (UUID) 主键?

  2. 项目将所有数据存储在单个 SQLite 文件中,如果数据库文件损坏了怎么办?如何防范?

  3. SQLite 的 WAL 模式有什么优势?如果你的项目日增数据量达到 10GB,还应该使用 SQLite 吗?为什么?


延伸阅读


本章回顾:你已经全面理解了项目的数据库设计!开发篇上(第 5-8 章)到此结束。在下一章中,我们将进入开发篇下,学习 API 开发实战。

基于 MPL-2.0 许可证发布