1. 场景

这两天一直被这个sqlit3困扰,起因是项目中需要有这样一个中间,中间件承担着API角色和流量转发的角色,需要接收来自至少300个agent的请求数据,和健康检测的请求。 所以当即想到用go来实现,因为数据教训,不考虑使用pg大型数据库,所以就选择了轻量化的sqlite数据库。程序很快就开发完了。上线,运行几个节点,数据读写都未发生异常,但是当测试数据到达一定量级后,会出现database is locked错误。 查了些资料,大意是sqlite并发读支持不错,但是并发写就不太友好,所以有了此次的实践。

ps: 部分代码来自于chatGPT,不得不说chatGPT太香了。

在 Gorm 中操作 SQLite3数据库时,由于 SQLite3 的写锁机制是针对整个数据库而不是单个表或行,因此高并发读写可能会导致锁库的情况。

2. 如何避免

为了避免锁库问题,可以采用以下几种方法:

  1. 使用 WAL 模式

    使用 SQLite3 的 WAL(Write-Ahead Logging)模式可以显著降低锁库的概率。在 WAL 模式下,读操作不会阻塞写操作,写操作也不会阻塞读操作,因此可以实现高并发的读写操作。

    可以在 Gorm 中使用以下代码开启 WAL 模式:

    import "gorm.io/driver/sqlite"
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
    DSN: "mode=wal",
    }) // 上面这种参数设置方式已经不适用新的设置方法如下
    if Inst, err = gorm.Open(sqlite.Open(dsn), gormConfig); err == nil {
    // 启用 WAL 模式
    _ = Inst.Exec("PRAGMA journal_mode=WAL;")
    //_ = Inst.Exec("PRAGMA journal_size_limit=104857600;")
    //_ = Inst.Exec("PRAGMA busy_timeout=999999;")
    }
  2. 合理控制事务范围

    在进行高并发读写操作时,需要注意事务范围的控制,尽可能缩小事务的范围,减少写锁的占用时间。例如,在进行批量写入操作时,可以将每次写入拆分为多个事务,以减少写锁的占用时间。

  3. 使用缓存

    使用缓存可以减少对数据库的读操作,从而减少锁库的概率。可以使用第三方缓存库(如 Redis)来实现缓存功能。

  4. 增加数据库连接数

    增加数据库连接数可以提高数据库的并发处理能力,减少锁库的概率。可以在 Gorm 中使用以下代码来增加数据库连接数:

    import "gorm.io/driver/sqlite"
    
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    
    sqlDB, dbError := db.DB()
    if dbError != nil {
    return nil, fmt.Errorf("failed to create sqlDB")
    } // SetMaxIdleConns 设置空闲连接池中连接的最大数量
    sqlDB.SetMaxIdleConns(10) // SetMaxOpenConns 设置打开数据库连接的最大数量。
    sqlDB.SetMaxOpenConns(100)'

    需要注意的是,增加连接数也会增加服务器的负载,因此需要根据实际情况进行调整。

    综上所述,通过采用合适的锁机制、事务控制、缓存和连接数设置等措施,可以有效避免 SQLite3 数据库的锁库问题。

3. 完整的代码示例

  1. 示例1:

    下面是一个完整的 Gorm 操作 SQLite3 数据库的代码示例,其中包括开启 WAL 模式、控制事务范围、使用缓存和增加数据库连接数等措施,以避免锁库问题。

    import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "time"
    ) // 定义模型结构体
    type User struct {
    ID uint
    Name string
    Age uint8
    CreatedAt time.Time
    UpdatedAt time.Time
    } // 初始化数据库连接
    func InitDB() (*gorm.DB, error) {
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
    // 开启 WAL 模式
    DSN: "mode=wal",
    // 增加最大连接数为 100
    MaxOpenConns: 100,
    })
    if err != nil {
    return nil, err
    }
    // 设置数据库连接池参数
    sqlDB, err := db.DB()
    if err != nil {
    return nil, err
    }
    sqlDB.SetMaxIdleConns(10)
    sqlDB.SetMaxOpenConns(100)
    sqlDB.SetConnMaxLifetime(time.Hour) return db, nil
    } // 定义批量写入函数
    func BatchInsertUsers(db *gorm.DB, users []User) error {
    // 每次写入 1000 条数据
    batchSize := 1000
    batchCount := (len(users) + batchSize - 1) / batchSize
    for i := 0; i < batchCount; i++ {
    start := i * batchSize
    end := (i + 1) * batchSize
    if end > len(users) {
    end = len(users)
    }
    batch := users[start:end]
    // 启用事务
    tx := db.Begin()
    if err := tx.Error; err != nil {
    return err
    }
    if err := tx.Create(&batch).Error; err != nil {
    tx.Rollback()
    return err
    }
    // 提交事务
    if err := tx.Commit().Error; err != nil {
    return err
    }
    }
    return nil
    } // 查询用户信息
    func GetUsers(db *gorm.DB) ([]User, error) {
    var users []User
    // 使用缓存,减少对数据库的读操作
    err := db.Cache(&users).Find(&users).Error
    if err != nil {
    return nil, err
    }
    return users, nil
    } // 示例代码
    func main() {
    // 初始化数据库连接
    db, err := InitDB()
    if err != nil {
    panic(err)
    }
    defer db.Close() // 批量插入数据
    users := []User{}
    for i := 0; i < 100000; i++ {
    user := User{
    Name: "user_" + string(i),
    Age: uint8(i % 100),
    CreatedAt: time.Now(),
    UpdatedAt: time.Now(),
    }
    users = append(users, user)
    }
    err = BatchInsertUsers(db, users)
    if err != nil {
    panic(err)
    } // 查询数据
    users, err = GetUsers(db)
    if err != nil {
    panic(err)
    }
    for _, user := range users {
    fmt.Println(user)
    }
    }
  2. 示例2:使用 WAL 模式和事务控制来避免锁库问题:

    package main
    
    import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    ) type User struct {
    ID uint
    Name string
    } func main() {
    // 创建 SQLite3 数据库连接
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
    // 开启 WAL 模式
    DSN: "mode=wal",
    })
    if err != nil {
    panic("failed to connect database")
    }
    // 设置连接池大小
    sqlDB, err := db.DB()
    if err != nil {
    panic("failed to set database pool size")
    }
    sqlDB.SetMaxIdleConns(10)
    sqlDB.SetMaxOpenConns(100) // 自动迁移 User 模型对应的表
    err = db.AutoMigrate(&User{})
    if err != nil {
    panic("failed to migrate table")
    } // 并发写入 1000 条数据
    for i := 0; i < 1000; i++ {
    go func(i int) {
    err := db.Transaction(func(tx *gorm.DB) error {
    user := User{Name: fmt.Sprintf("user_%d", i)}
    result := tx.Create(&user)
    return result.Error
    })
    if err != nil {
    fmt.Printf("failed to write data: %v\n", err)
    }
    }(i)
    } // 并发读取数据
    for i := 0; i < 1000; i++ {
    go func() {
    var users []User
    err := db.Transaction(func(tx *gorm.DB) error {
    result := tx.Find(&users)
    return result.Error
    })
    if err != nil {
    fmt.Printf("failed to read data: %v\n", err)
    } else {
    fmt.Printf("read %d records\n", len(users))
    }
    }()
    } // 等待 10 秒钟,以便所有的写入和读取操作都完成
    time.Sleep(10 * time.Second)
    }

    在这个代码示例中,我们首先使用 gorm.Open 函数创建了一个 SQLite3 数据库连接,并设置了连接池大小和 WAL 模式。然后,我们使用 d b.AutoMigrate 函数自动迁移 User 模型对应的表。

    接着,我们在循环中并发地写入 1000 条数据,并使用事务控制来控制事务的范围。每个写入操作都会创建一个 User 对象,并使用 tx.Create 函数将其写入数据库。

    然后,我们在另一个循环中并发地读取数据,并使用事务控制来控制事务的范围。每个读取操作都会使用 tx.Find 函数从数据库中读取所有的 User 记录,并打印出读取的记录数。

    最后,我们等待 10 秒钟,以便所有的写入和读取操作都完成。在这个示例中,我们使用了并发的写入和读取

gorm操作sqlite3,高并发读写如何避免锁库?的更多相关文章

  1. (转载)java高并发:CAS无锁原理及广泛应用

    java高并发:CAS无锁原理及广泛应用   版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...

  2. sqlite3 解决并发读写冲突的问题

    #include "stdafx.h" #include "sqlite3.h" #include <iostream> #include < ...

  3. Java高并发情况下的锁机制优化

    本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 1 public synchronized void syncMethod(){ 2 othercode ...

  4. MongoDB大数据高并发读写性能测试报告

    服务器大小: 单节点部署,磁盘1T,内存128G 并发导入规模: 1,多线程并发导入csv文件 2,csv文件分1万.10万.100万.200万行记录4种大小 3,每个csv对应一个collectio ...

  5. PHP 解决对文件操作的高并发问题

    解决方案:     对文件进行加锁时,设置一个超时时间.超时设置为1ms,如果这段时间内没有获得锁,就反复获得,直到获得对文件的操作权为止.如果超市限制已到,就必须马上退出,让出锁让其他进程进行操作. ...

  6. 如何在高并发环境下设计出无锁的数据库操作(Java版本)

    一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...

  7. php中并发读写文件冲突的解决方案

    在这里提供4种高并发读写文件的方案,各有优点,可以根据自己的情况解决php并发读写文件冲突的问题. 对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题.但如果 ...

  8. php中并发读写文件冲突的解决方案(文件锁应用示例)

    PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适 ...

  9. 朱晔的互联网架构实践心得S2E6:浅谈高并发架构设计的16招

    朱晔的互联网架构实践心得S2E6:浅谈高并发架构设计的16招 概览 标题中的高并发架构设计是指设计一套比较合适的架构来应对请求.并发量很大的系统,使系统的稳定性.响应时间符合预期并且能在极端的情况下自 ...

  10. java高并发系列 - 第22天:java中底层工具类Unsafe,高手必须要了解

    这是java高并发系列第22篇文章,文章基于jdk1.8环境. 本文主要内容 基本介绍. 通过反射获取Unsafe实例 Unsafe中的CAS操作 Unsafe中原子操作相关方法介绍 Unsafe中线 ...

随机推荐

  1. Java基础之基本运算符

    基本运算符 算数运算符:+.-.*./.%.++.-- 赋值运算符:= 关系运算符:>.<.>=.<=.==.!= instanceof 逻辑运算符:&&.|| ...

  2. 2022-04-19内部群每日三题-清辉PMP

    1.一个项目的完工预算(BAC)为100,000美元.目前的项目报告显示该项目符合进度和预算.一位团队成员对相关工作包进行自下而上估算,表明剩余工作将比原计划少用10,000美元,并且仍能按进度计划交 ...

  3. less的基本用法

    学习less详细文章链接 https://juejin.cn/post/6844903520441729037#heading-9

  4. (jmeter笔记)jmeter导出excel,中文显示乱码

    导出excel显示乱码 解决方法:http请求--Content encoding写utf-8 导出如下:

  5. 学习sql

    里面说了很多sql的应用,可以借鉴以下网址:https://www.w3school.com.cn/sql/index.asp

  6. noi 1.1 6 空格分隔输出

    描述 读入一个字符,一个整数,一个单精度浮点数,一个双精度浮点数,然后按顺序输出它们,并且要求在他们之间用一个空格分隔.输出浮点数时保留6位小数. 输入 共有四行:第一行是一个字符:第二行是一个整数: ...

  7. python + selenium 常用公共方法封装

    selenium 环境配置及浏览器驱动的安装:https://www.cnblogs.com/gancuimian/p/16435300.html uiautomator2 常用公共方法封装见之前的帖 ...

  8. 1903021126 申文骏 Java 第二周作业 代码编写及运行

    项目 内容 课程班级博客链接 19级信计班(本) 作业要求链接 Java第二周作业 博客名称 1903021126 申文骏 Java 第二周作业 代码编写及运行 要求 每道题要有题目,代码(使用插入代 ...

  9. Windows,easygui 安装

    在官网下载了easygui,但是根据网上的方法解压后将 easygui 文件夹(创建文件:easygui,只放easygui.py)放到Python36\Lib\site-packages下不行,有模 ...

  10. 20181224蒋嘉豪-exp5

    网络对抗技术Exp5 信息搜集与漏洞扫描20181224jjh 目录 网络对抗技术Exp5 信息搜集与漏洞扫描20181224jjh 信息搜集技术与隐私保护--知识点总结 间接收集 直接收集 社会工程 ...