Golang中从数据库获取数据的最佳方法是什么

Golang中从数据库获取数据的最佳方法是什么 你好,我们正在用Go语言开发一个基于Linux服务器的软件,并且我们正在为该软件添加许可证功能。我们想请教一下,从数据库获取数据的最佳方法是什么,以及我们应该使用哪种SQL数据库。

正如我们所说,我们想要添加许可证验证,并希望验证服务器IP是否在我们的数据库中获得了许可,同时还需要检查诸如许可证状态、许可证过期日期等数据。那么,最佳方法是什么?我们应该使用MySQL还是MongoDB?

2 回复

monsterlegend99:

那么,哪种方法最好,我们应该使用哪种 SQL,是 MySQL 还是 MongoDB?

MongoDB 不是 SQL。它是 NoSQL。你应该选择哪一个取决于你的数据。我更喜欢 PostgreSQL,因为我觉得它比 MySQL 更安全。

Logi Analytics

Logi Analytics

构建闪电般快速的嵌入式分析体验,同时加速实现价值——无需额外的工程资源。

更多关于Golang中从数据库获取数据的最佳方法是什么的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


对于从数据库获取数据的最佳方法,我建议使用标准库database/sql配合预处理语句和连接池。以下是具体实现方案:

数据库选择建议

使用MySQL而不是MongoDB,原因如下:

  • 许可证数据是结构化关系型数据
  • 需要事务支持确保数据一致性
  • SQL查询更适合复杂的条件检查

最佳实践代码示例

package main

import (
    "database/sql"
    "fmt"
    "log"
    "time"
    
    _ "github.com/go-sql-driver/mysql"
)

type License struct {
    ID           int
    ServerIP     string
    Status       string
    ExpiryDate   time.Time
    MaxUsers     int
    LicenseKey   string
}

type LicenseManager struct {
    db *sql.DB
}

// 初始化数据库连接
func NewLicenseManager(dsn string) (*LicenseManager, error) {
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        return nil, err
    }
    
    // 配置连接池
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(10)
    db.SetConnMaxLifetime(5 * time.Minute)
    
    // 验证连接
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    return &LicenseManager{db: db}, nil
}

// 使用预处理语句获取许可证数据
func (lm *LicenseManager) ValidateLicense(serverIP, licenseKey string) (*License, error) {
    query := `
        SELECT id, server_ip, status, expiry_date, max_users, license_key 
        FROM licenses 
        WHERE server_ip = ? AND license_key = ? AND status = 'active'
    `
    
    var license License
    row := lm.db.QueryRow(query, serverIP, licenseKey)
    
    err := row.Scan(
        &license.ID,
        &license.ServerIP,
        &license.Status,
        &license.ExpiryDate,
        &license.MaxUsers,
        &license.LicenseKey,
    )
    
    if err != nil {
        if err == sql.ErrNoRows {
            return nil, fmt.Errorf("license not found or inactive")
        }
        return nil, err
    }
    
    // 检查过期日期
    if license.ExpiryDate.Before(time.Now()) {
        return nil, fmt.Errorf("license expired")
    }
    
    return &license, nil
}

// 批量查询多个许可证状态
func (lm *LicenseManager) CheckMultipleLicenses(serverIPs []string) (map[string]bool, error) {
    query := `
        SELECT server_ip 
        FROM licenses 
        WHERE server_ip IN (`
    
    // 构建IN查询参数
    args := make([]interface{}, len(serverIPs))
    for i, ip := range serverIPs {
        if i > 0 {
            query += ","
        }
        query += "?"
        args[i] = ip
    }
    query += ") AND status = 'active' AND expiry_date > NOW()"
    
    rows, err := lm.db.Query(query, args...)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    validLicenses := make(map[string]bool)
    for rows.Next() {
        var ip string
        if err := rows.Scan(&ip); err != nil {
            return nil, err
        }
        validLicenses[ip] = true
    }
    
    return validLicenses, nil
}

// 使用事务更新许可证状态
func (lm *LicenseManager) UpdateLicenseStatus(licenseID int, newStatus string) error {
    tx, err := lm.db.Begin()
    if err != nil {
        return err
    }
    defer func() {
        if err != nil {
            tx.Rollback()
        }
    }()
    
    // 更新许可证状态
    _, err = tx.Exec(
        "UPDATE licenses SET status = ?, updated_at = NOW() WHERE id = ?",
        newStatus, licenseID,
    )
    if err != nil {
        return err
    }
    
    // 记录状态变更日志
    _, err = tx.Exec(
        "INSERT INTO license_logs (license_id, old_status, new_status) VALUES (?, ?, ?)",
        licenseID, "active", newStatus,
    )
    if err != nil {
        return err
    }
    
    return tx.Commit()
}

func main() {
    // 数据库连接DSN
    dsn := "user:password@tcp(localhost:3306)/license_db?parseTime=true"
    
    lm, err := NewLicenseManager(dsn)
    if err != nil {
        log.Fatal(err)
    }
    defer lm.db.Close()
    
    // 验证许可证示例
    license, err := lm.ValidateLicense("192.168.1.100", "ABC123-XYZ456")
    if err != nil {
        log.Printf("License validation failed: %v", err)
    } else {
        log.Printf("Valid license: %+v", license)
    }
}

数据库表结构示例

CREATE TABLE licenses (
    id INT PRIMARY KEY AUTO_INCREMENT,
    server_ip VARCHAR(45) NOT NULL,
    license_key VARCHAR(100) NOT NULL UNIQUE,
    status ENUM('active', 'suspended', 'expired') DEFAULT 'active',
    expiry_date DATETIME NOT NULL,
    max_users INT DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_server_ip (server_ip),
    INDEX idx_expiry_status (expiry_date, status)
);

CREATE TABLE license_logs (
    id INT PRIMARY KEY AUTO_INCREMENT,
    license_id INT NOT NULL,
    old_status VARCHAR(50),
    new_status VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (license_id) REFERENCES licenses(id)
);

关键要点

  1. 使用连接池:合理配置SetMaxOpenConnsSetMaxIdleConns
  2. 预处理语句:防止SQL注入,提高性能
  3. 错误处理:正确处理sql.ErrNoRows
  4. 事务支持:确保数据一致性
  5. 索引优化:在server_ipexpiry_datestatus字段上创建索引

这种方法结合了性能、安全性和可维护性,特别适合许可证验证这类需要高可靠性的场景。

回到顶部