SQLite的并发问题

news/2025/9/24 12:09:27/文章来源:https://www.cnblogs.com/Caisenberg/p/19108799

   转载自:C# 下 SQLite 并发操作与锁库问题的 5 种解决方案_51CTO博客_sqlcipher c#

SQLite是轻量级的数据库,可用于嵌入式设备,仅需几百KB的内存即可工作,整个数据库存储在单一文件中,便于管理,迁移,备份。无需繁琐配置。

轻量高性能必然带来一定的局限,这次遇到的就是SQLite数据库的并发操作问题

关键点:在多线程或多进程并发访问的场景下,同一时刻仅允许单个线程进行写入操作。

现象:database is locked 错误

问题描述:在一个线程正在执行写入操作的过程中,另一个线程的写入请求只能被迫等待,等待时间过长超过系统预设的超时时间(通常5s可更改)就会触发此错误。

解决方案

1. 读写锁

2. 事务

3. WAL模式

4. 连接池

5. 多线程模式

详细说明

1. 读写锁

确保资源不被争抢导致崩溃

读写锁
using System.Threading;public class SqliteDataManager
{//ReaderWriterLockSlim类提供读写锁private static ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim;private static string _connString = "Data Source=mydb.db;Version=3;";// 读操作(允许多线程并发)public void ReadData{_rwLock.EnterReadLock;  // 获取读锁try{using (var conn = new SQLiteConnection(_connString)){conn.Open;var cmd = conn.CreateCommand;cmd.CommandText = "SELECT * FROM MyTable";using (var reader = cmd.ExecuteReader){while (reader.Read) { /* 读取数据 */ }}}}finally{_rwLock.ExitReadLock;  // 必须在finally中释放}}// 写操作(独占锁)public void WriteData(string value){_rwLock.EnterWriteLock;  // 获取写锁try{using (var conn = new SQLiteConnection(_connString)){conn.Open;var cmd = conn.CreateCommand;cmd.CommandText = "INSERT INTO MyTable VALUES (@Value)";cmd.Parameters.AddWithValue("@Value", value);cmd.ExecuteNonQuery;}}finally{_rwLock.ExitWriteLock;  // 必须释放}}
}

可升级为写锁的读锁,可减少锁竞争,避免重复加锁

可升级为写锁的读锁
public sealed class SqliteConnectionManager
{private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim;private static SQLiteConnection _conn;public static SQLiteConnection GetConnection{_lock.EnterUpgradeableReadLock;  // 可升级为写锁的读锁try{if (_conn == null){_lock.EnterWriteLock;  // 升级为写锁try{if (_conn == null){_conn = new SQLiteConnection("Data Source=mydb.db");_conn.Open;}}finally { _lock.ExitWriteLock; }}return _conn;}finally { _lock.ExitUpgradeableReadLock; }}
}

具体使用

查看代码
using System;
using System.Data.SQLite;
using System.Threading;class DatabaseManager
{private static SQLiteConnection _connection;private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();public static SQLiteConnection GetConnection(){if(_connection==null){_lock.EnterWriteLock;try{if(_connection == null){_connection=new SQLiteConnection("Data Source=database.db");}}finally{_lock.ExitWriteLock();}return _connection;}public static void InsertUser(string name) { var connection = GetConnection();_lock.EnterWriteLock(); try { //写锁中使用了事务using (var transaction = connection.BeginTransaction()) { try { using (var command = new SQLiteCommand(connection)) { command.CommandText = "INSERT INTO Users (Name) VALUES (@name)";                        command.Parameters.AddWithValue("@name", name); command.ExecuteNonQuery(); } transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); } } } finally { _lock.ExitWriteLock(); }}public static void SelectUsers() { var connection = GetConnection(); _lock.EnterReadLock(); try { using (var command = new SQLiteCommand("SELECT * FROM Users", connection)) { using (var reader = command.ExecuteReader()) { while (reader.Read()) { Console.WriteLine(reader["Name"]); } } } } finally { _lock.ExitReadLock(); }}}}
}

2.事务

保障数据完整性,事务具有原子性,内部的所有操作要么成功要么一起失败并回滚到插入之前的状态。一般只在读的时候使用事务。

在写入时使用事务
public static void InsertUser(string name) 
{ var connection = GetConnection(); using (var transaction = connection.BeginTransaction()) { try { using (var command = new SQLiteCommand(connection)) { command.CommandText = "INSERT INTO Users (Name) VALUES (@name)"; command.Parameters.AddWithValue("@name", name); command.ExecuteNonQuery(); } transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); }} 
}

3. WAL模式

Write-Ahead Logging

_connection = new SQLiteConnection("Data Source=database.db;Journal Mode=WAL");

在连接字符串中,加入Journal Mode =WAL即可开启WAL模式,此模式可读写并行

原理:将写入的数据暂存到WAL文件中,在这个过程中主数据库文件依然可以对外提供读服务,当文件满足一定条件(如WAL文件大小达到阈值、事务提交等)时,数据库引擎在后台将WAL文件中的数据合并到主数据库文件中。

4. 连接池

_connection = new SQLiteConnection("Data Source=database.db;Max Pool Size=100;Pooling=True");

预先创建一定数量的数据库连接,减少连接创建与销毁开销,但需要合理配置连接池参数,否则可能出现连接泄露或资源浪费

5 多线程模式(即综合使用)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915685.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

域渗透靶场-vulntarget-a综合靶场

nmap发现开放80端口浏览器访问,是通达oa可以搜索poc或者使用oa利用工具拿到shell 通过账号admin 密码滞空,进入后台我们这里使用通达OA 11.2 后台getshell 点击系统管理 -> 系统参数设置 -> OA服务设置 找到We…

在线设计logo的网站小程序开发和app开发差别

1、读取哈使用方法不同SAP内存使用SET/GET parameters方法;ABAP内存使用 EXPORT 和 IMPORT 方法;2、共享范围不同SAP内存可以被所有的主session访问,内存数据可以同一个session中不同程序之间,或者不同session之间;AB…

现在的网站开发用什么技术南宁百度seo

简介: MaxCompute 是面向分析的企业级 SaaS 模式云数据仓库,以 Serverless 架构提供快速、全托管的在线数据仓库服务,消除了传统数据平台在资源扩展性和弹性方面的限制,最小化用户运维投入,使您可以经济并高效的分析处…

温州网站 公司做网站招标

一、源码特点 idea 开发 SSM 学员信息管理系统是一套完善的信息管理系统,结合SSM框架和bootstrap完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据库&#xff…

网站建设主要学什么软件济宁哪里有网站建设

五层: 应用层:应用层是最高层,负责为用户提供网络服务和应用程序。在应用层,用户应用程序与网络进行交互,发送和接收数据。典型的应用层协议包括HTTP(用于网页浏览)、SMTP(用于电子邮…

数组和链表读取、插入、删除以及查找的区别

数组和链表是两种常见的数据结构,它们在读取、插入、删除操作上有显著差异,下面详细说明: 1. 读取(访问)操作数组: 数组是连续的内存空间,元素按索引(下标)存储,因此可以通过索引直接访问任意位置的元素,时…

day 09 课程

day 09 课程课程:https://www.bilibili.com/video/BV1o4411M71o/?spm_id_from=333.788.videopod.episodes&p=168 9.1 了解函数———————————————————————————————————————…

在K8S中,日志分析工具有哪些可以与K8S集群通讯?

在Kubernetes中,日志分析工具与集群的通信方式多样,可以根据部署位置和采集方式分为几大类。以下是主流工具的详细分类和介绍: 📊 日志分析工具分类概览 graph TDA[K8S日志分析工具] --> B1[集群内部部署]A --…

在K8S中,网络通信模式有哪些?

好的,这是一个非常核心的问题。Kubernetes 的网络模型是一个复杂的体系,但我们可以将其分解为几个清晰的层次来理解。 Kubernetes 网络通信模式可以分为四大类,下图清晰地展示了这四类通信的全景:A[Kubernetes 网络…

中山网站代运营python nginx做网站

Creator 版本: 3.8.2 目标平台:小游戏开发 压缩后 我不知道别人压缩几百kb是怎么做到的。不过哪个要钱。 我这个技巧不用花钱。 论坛有教程但是没有教详细怎么做。 开整! 做一个空白的场景。然后写一个load脚本。load主场景。 从代码可…

厦门网站建设a建筑设计工资一般多少

TCP客户端数据通信 文章目录 TCP客户端数据通信1、软件准备2、硬件准备3、仿真电路原理图4、仿真代码实现5、仿真结果本文将介绍Arduino在Protues仿真环境中作为TCP客户端,如何与TCP服务器进行数据通信。 1、软件准备 1)Arduino IDE或 VSCode + PlatformIO 2)Proteus电路仿…

一文教你搞定PASS 2025:样本量计算神器安装到使用全流程

软件介绍 PASS 2025 是用于效能分析和样本量估计的统计软件包,它在前版本基础上新增了37项样本量计算程序,涵盖医学研究、社会科学调查、工程实验等多种复杂统计场景。新增的Logrank类检验样本量计算程序,能精准计算…

React 18.2中采用React Router 6.4

React 18.2中采用React Router 6.4pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&…

题解:AT_abc257_h [ABC257Ex] Dice Sum 2

柿子还是得写在草稿纸上手推。 题意:很简单了,不再赘述。 做法: 首先这个权值有点抽象,我们写出来稍微化简一下。 \[\frac{1}{6^n}\sum_{x_1=1}^6\sum_{x_2=1}^6\cdots\sum_{x_n=1}^6(\sum_{i=1}^na_{i,x_i})^2 - …

ClickHouse UPDATE 机制详解 - 若

ClickHouse UPDATE 机制详解 问题现象 在使用ClickHouse进行UPDATE操作时,经常会遇到这样的现象: UPDATE ethereum.block_tasks SETstatus = pending, owner = consumer-1_1758676754070328000, assigned_at = 2025-…

Jetpack Room 从入门到精通 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

网站建设unohacha免费北京网站建设

导言 在 Rust 中,互斥器(Mutex)是一种用于在多个线程之间共享数据的并发原语。互斥器提供了一种安全的方式,允许多个线程访问共享数据,但每次只允许一个线程进行写操作。本篇博客将详细介绍 Rust 中互斥器的使用方法&…

不干胶网站做最好的wordpress关闭

应用场景:一个游戏可能会衍生出其他APP或小程序之类的软件,例如王者营地是王者荣耀的官方APP,王者营地提供资讯、赛事、社区、战绩等功能。所以游戏端会和衍生出来的软件端做一些数据互通。这里把软件端称为中台系统。 Get请求和Post请求的区…

自建个网站怎么做网站建设与管理是干什么的

Windows 下本地 Docker RAGFlow 部署指南 环境要求部署步骤1. 克隆代码仓库2. 配置 Docker 镜像加速(可选)3. 修改端口配置(可选)4. 启动服务5. 验证服务状态6. 访问服务7. 登录系统8. 配置模型8.1 使用 Ollama 本地模型8.2 使用在线 API 服务9. 开始使用10. 常见问题处理端…

ClickHouse index_granularity 详解 - 若

ClickHouse index_granularity 详解 什么是 index_granularity index_granularity 是ClickHouse中一个重要的性能配置参数,它定义了索引的粒度(granularity),即每多少个数据行会创建一个索引标记(index mark)。 …