深入探究 Go 语言中使用 SQLite 数据库

发布于:2025-06-25 ⋅ 阅读:(21) ⋅ 点赞:(0)

引言

在软件开发中,数据库是管理和存储数据的关键组件。SQLite 作为一款轻量级的嵌入式数据库,因其零配置、高性能和易于集成等特性,成为众多小型项目和嵌入式系统的理想选择。而 Go 语言以其高效、简洁的特点,为操作 SQLite 数据库提供了强大的支持。本文将结合实际代码,深入介绍如何在 Go 语言中使用 SQLite 数据库,包括数据库和表的创建、增删改查操作以及操作的优化。

环境准备

在开始使用 Go 语言操作 SQLite 数据库之前,我们需要安装必要的库。可以使用以下命令安装github.com/mattn/go-sqlite3库:

go get github.com/mattn/go-sqlite3

创建数据库和表

创建数据库

在 Go 语言中,使用database/sql包结合github.com/mattn/go-sqlite3驱动来操作 SQLite 数据库。通过sql.Open函数可以打开或创建一个 SQLite 数据库文件。以下是一个简单的示例:

package main

import (
    "database/sql"
    "fmt"
    "path/filepath"

    _ "github.com/mattn/go-sqlite3"
)

const defDBPath = "./data"

func initContDB() error {
    dbFile := filepath.Join(defDBPath, "test.db")
    var err error
    gDB, err := sql.Open("sqlite3", dbFile)
    if err != nil {
        fmt.Println("open test db ", err)
        return err
    }
    fmt.Println("open test db ok")
    _, err = gDB.Query("SELECT COUNT(*) FROM sqlite_master")
    if err != nil {
        fmt.Println("test db query ", err)
        return err
    }
    return nil
}

在上述代码中,sql.Open函数接受两个参数:驱动名(这里是sqlite3)和数据库文件的路径。如果文件不存在,SQLite 会自动创建该文件。

创建表

在创建数据库之后,我们需要创建表来存储数据。可以使用Exec方法执行 SQL 语句来创建表。为了避免重复创建表,我们可以先检查表是否已经存在。以下是创建表的示例代码:

func existTable(db *sql.DB, tbl string) bool {
    stmt, err := db.Prepare("SELECT COUNT(*) FROM sqlite_master where tbl_name=?")
    if err != nil {
        fmt.Println(err)
        return false
    }
    defer stmt.Close()
    row := stmt.QueryRow(tbl)
    if row == nil {
        return false
    }
    var val int
    row.Scan(&val)
    if val == 0 {
        return false
    }
    return true
}

func createTable() {
    if existTable(gDB, "t_cont_info") {
        return
    }
    tbl := `CREATE TABLE [t_cont_info](
              [name] VARCHAR PRIMARY KEY NOT NULL UNIQUE, 
              [ver] VARCHAR, 
              [image] VARCHAR NOT NULL, 
              [cmd] INT(2) NOT NULL, 
              [opt_cmd] INT(2) NOT NULL, 
              [cpus] INT(2) NOT NULL, 
              [cpu_threshold] INT(4) NOT NULL, 
              [memory] INT(4) NOT NULL, 
              [mem_threshold] INT(4) NOT NULL, 
              [store_cap] INT(4) NOT NULL, 
              [store_threshold] INT(4) NOT NULL, 
              [ip] VARCHAR, 
              [create_time] VARCHAR, 
              [start_time] VARCHAR, 
              [runtimes] BIGINT, 
              [backup_path] VARCHAR,
              [opt_time] VARCHAR);
            `
    _, err := gDB.Exec(tbl)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("creatTable t_cont_info")
}

在上述代码中,existTable函数用于检查指定的表是否存在。createTable函数根据检查结果决定是否创建t_cont_info表。

增删改查操作

插入数据

插入数据可以使用PrepareExec方法。以下是插入的示例代码:

func insertWarnMsg(name, msg string) error {
    stmt, err := gWarnDB.Prepare(`INSERT INTO t_cont_info
                ([name], [msg], [opt_time]) VALUES (?,?,?)`)
    if err != nil {
        fmt.Println(err)
        return err
    }
    _, err = stmt.Exec(name, msg, getLocalTime())
    if err != nil {
        stmt.Close()
        fmt.Println(err)
        return err
    }
    stmt.Close()

    checkWarnLimit(name)
    return nil
}

在上述代码中,Prepare方法用于准备 SQL 语句,Exec方法用于执行插入操作。

更新数据

更新数据的操作与插入数据类似,同样使用PrepareExec方法。以下是更新的示例代码:

func updContInfo(ct ContItem) {
    smt, err := gDB.Prepare("SELECT [name] FROM t_cont_info WHERE name=?")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer smt.Close()
    upd := false
    row := smt.QueryRow(ct.Name)
    if row != nil {
        var name string
        err = row.Scan(&name)
        if err != nil {

        } else if name == ct.Name {
            upd = true
        }
    }
    if upd {
        sqlCmd := `UPDATE t_cont_info SET [ver]=?, [image]=?, [cmd]=?, [opt_cmd]=?, [cpus]=?, [cpu_threshold]=?, 
            [memory]=?, [mem_threshold]=?, [store_cap]=?, [store_threshold]=?, [ip]=?, [create_time]=?, 
            [start_time]=?, [runtimes]=?, [backup_path]=?, [opt_time]=? WHERE [name]=?`
        stmt, err := gDB.Prepare(sqlCmd)
        if err != nil {
            fmt.Println(err, ", sql: ", sqlCmd)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(ct.Ver, ct.Img, ct.Cmd, ct.OptCmd, ct.CPUs, ct.CPUThreshold,
            ct.Memory, ct.MemThreshold, ct.StoreCap, ct.StoreThreshold, ct.IP, ct.CreateTime,
            ct.StartTime, ct.RunTimes, ct.BackupPath, getLocalTime(), ct.Name)
        if err != nil {
            fmt.Println(err)
            return
        }
    } else {
        stmt, err := gDB.Prepare(`INSERT INTO t_cont_info
                ([name] , [ver], [image], [cmd], [opt_cmd], [cpus], [cpu_threshold], 
                [memory], [mem_threshold], [store_cap], [store_threshold], [ip], [create_time], 
                [start_time], [runtimes], [backup_path], [opt_time]
                ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
        if err != nil {
            fmt.Println(err)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(ct.Name, ct.Ver, ct.Img, ct.Cmd, ct.OptCmd, ct.CPUs, ct.CPUThreshold,
            ct.Memory, ct.MemThreshold, ct.StoreCap, ct.StoreThreshold, ct.IP, ct.CreateTime,
            ct.StartTime, ct.RunTimes, ct.BackupPath, getLocalTime())
        if err != nil {
            fmt.Println(err)
            return
        }
    }
}

在上述代码中,首先检查数据是否存在,如果存在则更新数据,否则插入新数据。

删除数据

删除数据可以使用PrepareExec方法执行DELETE语句。以下是删除的示例代码:

func deleteContItem(name string) {
    del := func(tbl, name string) {
        stmt, err := gDB.Prepare(fmt.Sprintf("DELETE FROM %s WHERE [name]=?", tbl))
        if err != nil {
            fmt.Println(err)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(name)
        if err != nil {
            fmt.Println(err)
            return
        }
    }

    del("t_cont_info", name)
    del("t_cont_res_map", name)
    fmt.Println("deleteContItem: ", name)
}

在上述代码中,定义了一个匿名函数del来执行删除操作,分别删除t_cont_infot_cont_res_map表中指定名称的数据。

查询数据

查询数据可以使用Query方法执行SELECT语句,并使用Scan方法将结果扫描到变量中。以下是查询所有数据的示例代码:

func loadFromDB() (lst []ContItem, err error) {
    rows, err := gDB.Query(`SELECT [name] , [ver], [image], [cmd], [opt_cmd], [cpus], 
                    [cpu_threshold], [memory], [mem_threshold], [store_cap], [store_threshold], 
                    [ip], [create_time], [start_time], [runtimes], [backup_path] FROM t_cont_info`)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer rows.Close()
    for rows.Next() {
        var ct ContItem
        rows.Scan(&ct.Name, &ct.Ver, &ct.Img, &ct.Cmd, &ct.OptCmd, &ct.CPUs, &ct.CPUThreshold,
            &ct.Memory, &ct.MemThreshold, &ct.StoreCap, &ct.StoreThreshold, &ct.IP,
            &ct.CreateTime, &ct.StartTime, &ct.RunTimes, &ct.BackupPath)
        ct.DevMapList, _ = loadContResMap(ct.Name, CONT_RES_DEV)
        ct.VolMapList, _ = loadContResMap(ct.Name, CONT_RES_VOL)
        ct.PortMapList, _ = loadContResMap(ct.Name, CONT_RES_PORT)
        lst = append(lst, ct)
        fmt.Println("loadFromDB: ", ct)
    }
    return
}

在上述代码中,Query方法返回一个Rows对象,通过Next方法遍历结果集,并使用Scan方法将数据存储到ContItem结构体中。

操作优化

批量操作

在进行大量数据的插入或更新操作时,可以使用事务来提高性能。事务可以将多个操作合并为一个原子操作,减少数据库的开销。以下是一个使用事务进行批量插入的示例代码:

func batchInsert(data []interface{}) error {
    tx, err := gDB.Begin()
    if err != nil {
        return err
    }
    stmt, err := tx.Prepare("INSERT INTO table_name (column1, column2) VALUES (?,?)")
    if err != nil {
        tx.Rollback()
        return err
    }
    defer stmt.Close()
    for _, item := range data {
        _, err = stmt.Exec(item.Value1, item.Value2)
        if err != nil {
            tx.Rollback()
            return err
        }
    }
    return tx.Commit()
}

在上述代码中,Begin方法开始一个事务,Prepare方法准备 SQL 语句,Exec方法执行插入操作,最后使用Commit方法提交事务。如果发生错误,使用Rollback方法回滚事务。

索引优化

为经常用于查询条件的列创建索引可以提高查询性能。例如,在t_cont_info表中,如果经常根据name列进行查询,可以为name列创建索引:

func createIndex() {
    _, err := gDB.Exec("CREATE INDEX idx_name ON t_cont_info (name)")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("create index idx_name")
}

在上述代码中,使用CREATE INDEX语句为name列创建索引。

总结

本文深入介绍了如何在 Go 语言中使用 SQLite 数据库,包括数据库和表的创建、增删改查操作以及操作的优化。通过使用database/sql包和github.com/mattn/go-sqlite3驱动,我们可以方便地操作 SQLite 数据库。在实际开发中,根据具体需求选择合适的操作方法和优化策略,可以提高程序的性能和稳定性。


网站公告

今日签到

点亮在社区的每一天
去签到