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

引言

在软件开发中,数据库是管理和存储数据的关键组件。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 数据库文件。以下是一个简单的示例:

Go 复制代码
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 数据库。在实际开发中,根据具体需求选择合适的操作方法和优化策略,可以提高程序的性能和稳定性。

相关推荐
·云扬·8 分钟前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德11 分钟前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫37 分钟前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i1 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.1 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn1 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露1 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星1 小时前
sql语言之分组语句group by
java·数据库·sql
符哥20081 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql
C++ 老炮儿的技术栈2 小时前
Qt 编写 TcpClient 程序 详细步骤
c语言·开发语言·数据库·c++·qt·算法