# 数据库使用指南 ## 📋 概述 SunHPC 使用 SQLite 数据库,支持自定义数据库路径和名称。数据库配置通过 `sunhpc init database` 命令初始化,之后所有命令都可以通过单例模式访问同一个数据库实例。 ## 🚀 快速开始 ### 1. 初始化数据库 ```bash # 使用默认路径 (/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. 在其他命令中使用数据库 ```go 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` 文件: ```yaml db: path: "/opt/sunhpc/data" # 数据库目录路径 name: "my_cluster.db" # 数据库文件名 ``` ### 配置优先级 从高到低: 1. **配置文件**:`config.yaml` 中的 `db.path` 和 `db.name` 2. **环境变量**:`DB_PATH` 和 `DB_NAME` 3. **默认值**:`/var/lib/sunhpc` 和 `sunhpc.db` ### 环境变量 ```bash # 设置数据库路径 export DB_PATH=/tmp/sunhpc export DB_NAME=test.db # 使用环境变量 sunhpc init database ``` ## 📊 数据库 API ### 获取数据库实例 ```go // 方式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 ```go // 执行查询 _, 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") ``` ### 获取查询结果 ```go // 获取单行 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:查询节点列表 ```go 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:添加节点 ```go 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:更新节点 ```go 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:删除节点 ```go 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 表 ```sql 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 表 ```sql 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 表 ```sql 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()` 时会自动创建数据库文件和表结构。