Files
sunhpc-go/docs/database_usage.md
2026-02-14 05:36:00 +08:00

7.6 KiB
Raw Blame History

数据库使用指南

📋 概述

SunHPC 使用 SQLite 数据库,支持自定义数据库路径和名称。数据库配置通过 sunhpc init database 命令初始化,之后所有命令都可以通过单例模式访问同一个数据库实例。

🚀 快速开始

1. 初始化数据库

# 使用默认路径 (/var/lib/sunhpc/sunhpc.db)
sunhpc init database

# 使用自定义配置文件
sunhpc init database --config /path/to/config.yaml

# 使用环境变量
DB_PATH=/opt/sunhpc/data DB_NAME=cluster.db sunhpc init database

# 强制重新初始化
sunhpc init database --force

2. 在其他命令中使用数据库

package cmd

import (
    "fmt"
    "sunhpc/internal/db"
    "sunhpc/internal/log"
    "github.com/spf13/cobra"
)

var myCmd = &cobra.Command{
    Use:   "mycommand",
    Short: "我的命令",
    RunE: func(cmd *cobra.Command, args []string) error {
        // 获取数据库实例(自动使用配置的路径)
        database, err := db.GetInstance()
        if err != nil {
            return fmt.Errorf("获取数据库连接失败: %v", err)
        }
        defer database.Close()

        // 执行查询
        _, err = database.Execute("SELECT * FROM nodes")
        if err != nil {
            return fmt.Errorf("查询失败: %v", err)
        }

        // 获取结果
        rows, err := database.FetchAll()
        if err != nil {
            return fmt.Errorf("获取结果失败: %v", err)
        }

        // 处理结果
        for _, row := range rows {
            log.Infof("节点: %v", row)
        }

        return nil
    },
}

🔧 配置说明

配置文件格式

创建 config.yaml 文件:

db:
  path: "/opt/sunhpc/data"    # 数据库目录路径
  name: "my_cluster.db"         # 数据库文件名

配置优先级

从高到低:

  1. 配置文件config.yaml 中的 db.pathdb.name
  2. 环境变量DB_PATHDB_NAME
  3. 默认值/var/lib/sunhpcsunhpc.db

环境变量

# 设置数据库路径
export DB_PATH=/tmp/sunhpc
export DB_NAME=test.db

# 使用环境变量
sunhpc init database

📊 数据库 API

获取数据库实例

// 方式1使用默认配置推荐
database, err := db.GetInstance()

// 方式2指定路径和名称仅在初始化时使用
database, err := db.GetInstanceWithConfig("/path/to/db", "mydb.db")

// 检查实例是否已配置
if db.IsInstanceConfigured() {
    dbPath, dbName := db.GetInstanceConfig()
    log.Infof("数据库: %s/%s", dbPath, dbName)
}

执行 SQL

// 执行查询
_, err := database.Execute("SELECT * FROM nodes WHERE name = ?", "node1")

// 执行插入
_, err := database.Execute(
    "INSERT INTO nodes (name, cpus, memory) VALUES (?, ?, ?)",
    "node2", 32, 128,
)

// 执行更新
_, err := database.Execute(
    "UPDATE nodes SET cpus = ? WHERE name = ?",
    64, "node1",
)

// 执行删除
_, err := database.Execute("DELETE FROM nodes WHERE name = ?", "node1")

获取查询结果

// 获取单行
row, err := database.FetchOne()
if err != nil {
    return err
}
if row != nil {
    log.Infof("节点名称: %s", row["name"])
    log.Infof("CPU数量: %v", row["cpus"])
}

// 获取所有行
rows, err := database.FetchAll()
if err != nil {
    return err
}
for _, row := range rows {
    log.Infof("节点: %v", row)
}

🎯 使用示例

示例1查询节点列表

func listNodes() error {
    database, err := db.GetInstance()
    if err != nil {
        return err
    }
    defer database.Close()

    _, err = database.Execute("SELECT id, name, cpus, memory FROM nodes ORDER BY name")
    if err != nil {
        return err
    }

    rows, err := database.FetchAll()
    if err != nil {
        return err
    }

    for _, row := range rows {
        fmt.Printf("%-5s %-20s %-8s %-10s\n",
            row["id"], row["name"], row["cpus"], row["memory"])
    }

    return nil
}

示例2添加节点

func addNode(name string, cpus, memory int) error {
    database, err := db.GetInstance()
    if err != nil {
        return err
    }
    defer database.Close()

    _, err = database.Execute(
        "INSERT INTO nodes (name, cpus, memory) VALUES (?, ?, ?)",
        name, cpus, memory,
    )
    if err != nil {
        return fmt.Errorf("添加节点失败: %v", err)
    }

    return nil
}

示例3更新节点

func updateNode(name string, cpus, memory int) error {
    database, err := db.GetInstance()
    if err != nil {
        return err
    }
    defer database.Close()

    _, err = database.Execute(
        "UPDATE nodes SET cpus = ?, memory = ? WHERE name = ?",
        cpus, memory, name,
    )
    if err != nil {
        return fmt.Errorf("更新节点失败: %v", err)
    }

    return nil
}

示例4删除节点

func deleteNode(name string) error {
    database, err := db.GetInstance()
    if err != nil {
        return err
    }
    defer database.Close()

    _, err = database.Execute("DELETE FROM nodes WHERE name = ?", name)
    if err != nil {
        return fmt.Errorf("删除节点失败: %v", err)
    }

    return nil
}

🔍 数据库表结构

nodes 表

CREATE TABLE nodes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL UNIQUE,
    rack INTEGER DEFAULT 0,
    rank INTEGER DEFAULT 0,
    membership_id INTEGER,
    cpus INTEGER DEFAULT 0,
    memory INTEGER DEFAULT 0,
    disk INTEGER DEFAULT 0,
    os TEXT,
    kernel TEXT,
    last_state_change DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

networks 表

CREATE TABLE networks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    node_id INTEGER NOT NULL,
    name TEXT,
    ip TEXT UNIQUE,
    mac TEXT UNIQUE,
    subnet_id INTEGER,
    interface TEXT DEFAULT 'eth0',
    FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE,
    FOREIGN KEY (subnet_id) REFERENCES subnets(id) ON DELETE SET NULL
);

software_installs 表

CREATE TABLE software_installs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    version TEXT,
    install_type TEXT,
    node_id INTEGER,
    status TEXT,
    installed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    installed_by TEXT,
    FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE SET NULL
);

⚠️ 注意事项

  1. 单例模式GetInstance() 使用单例模式,整个程序只创建一个数据库实例
  2. 路径一致性:所有命令都使用同一个数据库路径,确保数据一致性
  3. 关闭连接:使用完毕后调用 database.Close() 释放资源
  4. 错误处理:始终检查错误返回值
  5. SQL注入防护:使用参数化查询(? 占位符)

📝 最佳实践

  1. 初始化优先:在程序启动时先执行 sunhpc init database
  2. 配置管理:使用配置文件统一管理数据库路径
  3. 事务处理:复杂操作使用事务确保数据一致性
  4. 日志记录:记录所有数据库操作,便于调试
  5. 资源释放:使用 defer database.Close() 确保连接关闭

🆘 常见问题

Q: 如何切换数据库路径?

A: 重新运行 sunhpc init database 命令,指定新的配置文件或环境变量。

Q: 多个命令会创建多个数据库实例吗?

A: 不会。GetInstance() 使用单例模式,整个程序只创建一个实例。

Q: 如何查看当前使用的数据库路径?

A: 使用 db.GetInstanceConfig() 获取配置信息。

Q: 数据库文件不存在会怎样?

A: 首次调用 GetInstance() 时会自动创建数据库文件和表结构。