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

345 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库使用指南
## 📋 概述
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()` 时会自动创建数据库文件和表结构。