Sqlite学习笔记(五)SQLite封锁机制

概述

     SQLite虽然是一个轻量的嵌入式数据库,但这并不影响它支持事务。所谓支持事务,即需要在并发环境下,保持事务的ACID特性。事务的原子性,隔离性都需要通过并发控制来保证。那么Sqlite的并发控制是怎样的,如何实现,在这里跟大家分享下我的理解。

     SQLite是一个文件数据库,所有的数据都在一个db文件中,对于wal模式,还包含wal索引文件和wal日志文件。SQlite支持库级并发,即允许多个读事务同时运行,同一时刻最多只有一个写事务,读写冲突,相对于传统的DBMS支持表级,行级甚至MVCC,SQLite的库级并发确实显得比较寒碜。但是锁粒度越细,意味着维护锁的成本越高,系统也会越复杂,因此SQLite的封锁机制要简单很多,对资源的消耗也非常少。SQLite 3.7版本后,对并发控制做了优化,推出了WAL日志模式,可以实现读写并发,但同一个时刻仍然只能有一个写事务。由于SQLite的实现方式,SQLite只支持两种隔离级别,串行化和读未提交。读未提交,就是读全程不上锁;串行化在事务开启时上读锁,上锁和释放锁同样遵守两阶段锁协议,在事务提交或回滚时才释放锁。

文件锁

     要说清楚SQLite锁实现机制,首先要了解文件锁,因为SQLite所有锁实现都是基于文件锁。对于Linux系统,文件锁主要包含两类,协同锁和强制锁,协同锁类似于互斥量,需要参与者都遵守游戏规则,在操作文件前,都先上锁,而强制锁由OS内核强制实行。协同锁根据锁粒度分为文件级别和范围级别。锁文件是最简单的对文件加锁的方法,每个需要加锁的数据文件都有一个锁文件(lock file)。当锁文件存在时,就认为该数据文件已经被加锁,别的进程不应该访问。当锁不存在,进程就可以创建一个锁文件,然后访问相应的数据文件。只要创建锁的过程是原子的,就能保证某一时刻只有一个进程拥有该锁,这种方法保证某一时刻只有一个进程访问文件。文件锁的弊端显而易见,并发粒度太低。范围锁相对于文件锁,可以锁文件的一部分内容,并且有读锁和写锁。对于同一部分内容,读锁可以共存,读锁和写锁互斥。POSIX标准提供接口fcntl()来实现。

锁类型   

       SQLite中的锁正是利用了范围锁来实现并发控制的目的。SQLite中主要包含了4种锁:共享锁(SHARED_LOCK)、保留锁(RESERVED_LOCK)、未决锁(PENDING_LOCK)和排它锁(EXCLUSIVE_LOCK),这4种锁定义了3个区域,其中共享锁和排它锁占用文件相同的区域。具体而言,SQLite定义了文件的以下区域为锁文件区域,由于fcntl可以对不存在的文件区域加锁,因此 PENDING_BYTE定位在区域1G的地方,即使DB文件没这么大也不影响。三种类型的锁,分别在1G,1G+1,1G+2的偏移处,之所以SHARED_SIZE长度是510,原因在于windows环境下,LockFile()加锁区域不能重叠(Linux没有这种问题),对于同一个字节上锁会影响并发,因此设置了一个范围,对SHARED_FIRST—SHARED_FIRST+ SHARED_SIZE范围内的随机数进行加锁,这样可以减少冲突,保证高效的读取文件。具体锁类别和说明参见表1 

锁类别

字节范围

说明

PENDING_BYTE

0x40000000

一种过渡锁,读事务获取读锁,写事务获取写锁前,都需要获取该锁。

RESERVED_BYTE

0x40000001

表示线程要开始写操作,某一时刻只能有一个RESERVED Lock,但是RESERVED锁和SHARED锁可以共存,而且可以对数据库加新的SHARED锁。

SHARED_LOCK

0x40000002-0x40000200

共享锁,开启事务时,都需要获取该锁

EXCLUSIVE_LOCK

0x40000002-0x40000200

排它锁

                                                       表1

      从各个锁的作用来看,不免会疑问,为啥要加上RESERVED_LOCK和PENDING_LOCK两种类型,直接通过共享锁和排它锁不就可以达到读读共享,读写互斥的目的了吗。这里引入这Reserved锁的目的是为了提高并发。由于SQLite只有库级排斥锁(EXCLUSIVE LOCK),如果写事务一开始就上EXCLUSIVE锁,然后再进行实际的数据更新,写磁盘操作,这会使得并发性大大降低。而SQLite一旦得到数据库的RESERVED锁,就可以对缓存中的数据进行修改,而与此同时,其它进程可以继续进行读操作。直到真正需要写磁盘时才对数据库加EXCLUSIVE锁。Pending锁的作用主要是为了防止写饿死的情况,写事务获取Pending锁后,新的读事务无法再进来,然后再加EXCLUSIVE锁,这样写事务获取锁的几率大大提高,读写事务的流程如下表2,状态变迁图如图1。

类型

操作

锁信息

说明

读事务

begin

 

不持有锁

select c1 from user where id=1

Lock: Pending(Read)

Lock:Shared(Read)

Unlock:Pending

获取Shared读锁前,需要先获取Pending共享锁,

通过这种方式与写事务互斥。

commit

UnLock:Shared

 

写事务

begin

 

 

Update c1=c1+1 where id=1

Lock: Pending(Read)

Lock:Shared

Unlock:Pending

Lock:Reserved(Write)

先获取Shared读锁,然后获取Reserved的排它锁,阻止其它写事务

commit

Lock:Pending(Write)

Lock:Exclusive(Write)

Unlock: Pending

Unlock: Exclusive(Write)

获取Pending的排它锁,阻止新的读事务,最后上排它锁,阻止所有读事务,读写不能并发

Pending锁方式好处是,减少写饿死的几率。

                                                       表2

 

                                         图1

Wal锁类型

      引入WAL机制后,SQLite开始支持读写并发,并且引入了WAL日志文件锁。WAL日志锁实质是锁wal-index文件的区域,根据不同的锁类型,将wal-index文件的不同区域划定义成不同的锁,主要有读锁,写锁,检查点锁,具体如表3,4。WAL模式下,最新的数据位于日志文件中,无论是读事务还是写事务都需要持有WAL_READ_LOCK的读锁,因为它们都需要获取最新的事务点。因此,做检查点时,可以通过对WAL_READ_LOCK位置(124-127)上锁,来确定检查点需要等待还是停止推进。同时我们也可以看到,对于DB文件,读写事务都只需要对DB文件上读锁,对于WAL日志文件,WAL_READ_LOCK和WAL_WRITE_LOCK位于不同的位置,读写相互不影响,所以读写不互斥。 

锁类别

字节范围

说明

读事务(WAL)

begin

 

 

select c1 from user where id=1

DB文件:

Lock: Pending(Read)

Lock:Shared

Unlock:Pending

WAL文件:

Lock:WAL_READ_LOCK(Read)

除了获取DB文件锁,还需要获取WAL锁,得到最新提交事务的位点。

若有事务再作检查点,需要重试多次。

commit

Unlock:WAL_READ_LOCK

Unlock:Shared

 

写事务(WAL)

begin

 

 

Update c1=c1+1 where id=1

DB文件:

Lock: Pending-Read

Lock:Shared(Read)

Unlock:Pending

WAL文件:

Lock:WAL_READ_LOCK(Read)

Lock:WAL_WRITE_LOCK(Write)

通过

EXCLUSIVE-WRITE-LOCK控制写写并发

由于不操作DB文件,因此不存在读写冲突,读写可以并发。

commit

WAL文件:

Lock:SHARED-READ-LOCK

Unlock:WAL_READ_LOCK(Read)

Unlock: WAL_WRITE_LOCK(Write)

 

DB文件:

Unlock:Shared

获取SHARED-READ-LOCK目的是为了获取最新提交日志的位点

检查点

操作

(WAL)

 

 

WAL文件:

Lock:WAL_CKPT_LOCK(write)

Lock:WAL_READ_LOCK(write)

UnLock:WAL_READ_LOCK

UnLock:WAL_CKPT_LOCK

EXCLUSIVE-CKPT-LOCK
保证只有一个写事务做检查点;

WAL_READ_LOCK阻止读写事务。

                                                  表3 

锁类别

字节范围

说明

WAL_WRITE_LOCK

120

写锁位置

WAL_CKPT_LOCK

121

检查点锁位置

WAL_RECOVER_LOCK

122

故障恢复锁位置

WAL_READ_LOCK

123

读锁(表示不需要wal文件)

 

124-127

读锁(每个位置,对应一个锁)

做检查点时,逐一对每个位置上写锁,若上锁失败表示对应位置上的读事务没有结束,根据检查点策略确定是等待(FULL),还是停止推进(PASSIVE)。

                                                 表4   

调试

     SQLite通过几个宏定义可以打印语句执行的锁信息,方便大家了解语句执行中加了哪些锁,什么时候加的,什么时候释放的,以及如何处理锁冲突。具体的宏包括SQLITE_LOCK_TRACE,SQLITE_FORCE_OS_TRACE,和SQLITE_DEBUG,具体可以在代码中查看宏定义的注释。

gcc sqlite3.c -g -lpthread -ldl -fPIC -shared -DSQLITE_TEST -DSQLITE_DEBUG -DSQLITE_LOCK_TRACE -DSQLITE_FORCE_OS_TRACE -o libsqlite3.so

参考文档

http://my.oschina.net/u/587236/blog/129022

http://www.cnblogs.com/hustcat/archive/2009/03/01/1400757.html

 

 

转载于:https://www.cnblogs.com/cchust/p/4761814.html

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

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

相关文章

idea中git如何切换到master_IDEA中Git的使用

原文转载于:https://www.cnblogs.com/javabg/p/8567790.html工作中多人使用版本控制软件协作开发,常见的应用场景归纳如下:假设小组中有两个人,组长小张,组员小袁场景一:小张创建项目并提交到远程Git仓库场…

LeetCode 2032. 至少在两个数组中出现的值(哈希/位运算)

文章目录1. 题目2. 解题2.1 哈希查找2.2 位运算1. 题目 给你三个整数数组 nums1、nums2 和 nums3 ,请你构造并返回一个 不同 数组,且由 至少 在 两个 数组中出现的所有值组成。 数组中的元素可以按 任意 顺序排列。 示例 1: 输入&#xff1…

LeetCode 2033. 获取单值网格的最小操作数(贪心)

文章目录1. 题目2. 解题1. 题目 给你一个大小为 m x n 的二维整数网格 grid 和一个整数 x 。 每一次操作,你可以对 grid 中的任一元素 加 x 或 减 x 。 单值网格 是全部元素都相等的网格。 返回使网格化为单值网格所需的 最小 操作数。如果不能,返回 …

mysql最左_Mysql最左原则

1. 前言偶然看到一个技术群&#xff0c;对一道关于联合索引的讨论。面试题如下&#xff1a;a_b_c_index 三列复合索引 a 1 and b<100 and c5 这个查询 会用到索引的那几部分&#xff1f;复制代码先说下个人经过本人查询多方资料得到的结论&#xff0c; 只会用到 a 和 b部分(…

LeetCode 2034. 股票价格波动(set + map)

文章目录1. 题目2. 解题1. 题目 给你一支股票价格的数据流。数据流中每一条记录包含一个 时间戳 和该时间点股票对应的 价格 。 不巧的是&#xff0c;由于股票市场内在的波动性&#xff0c;股票价格记录可能不是按时间顺序到来的。 某些情况下&#xff0c;有的记录可能是错的…

什么是python函数_什么是python函数

python函数是指组织好的、可重复使用的、用来实现单一或相关联功能的代码段。python函数包含系统中自带的一些函数、第三方函数、以及用户自定义的函数。函数是可以实现一些特定功能的小方法或是小程序。在Python中有很多内建函数&#xff0c;当然随着学习的深入&#xff0c;我…

LeetCode 2035. 将数组分成两个数组并最小化数组和的差(状态压缩DP)

文章目录1. 题目2. 解题1. 题目 给你一个长度为 2 * n 的整数数组。 你需要将 nums 分成 两个 长度为 n 的数组&#xff0c;分别求出两个数组的和&#xff0c;并 最小化 两个数组和之 差的绝对值 。 nums 中每个元素都需要放入两个数组之一。 请你返回 最小 的 数组和之差。 …

android--仿网易新闻主界面

主要是学习ActionBarDrawerLayoutActionBarDrawerToggle,很不错的教程,下面一步一步带你实现这个过程,有不足之处欢迎留言交流.下面先来一张效果图 根据图片分析,要实现的有侧边栏DrawerLayout,ActionBar的颜色和菜单以及ActionBarDrawerToggle的动画效果. 在这之前,Theme要改…

fastapi 查询参数和字符串校验 / 路径参数和数值校验

文章目录1. 约束限制2. 必须参数3. 查询参数列表 / 多个值4. 声明更多元数据5. 别名参数6. 弃用参数7. Path 路径参数8. 按需对参数排序learn from https://fastapi.tiangolo.com/zh/tutorial/query-params-str-validations/ 1. 约束限制 from typing import Optional from f…

数据库中包含开始时间、结束时间,并且查询条件也有开始时间、结束时间的查询方法...

分类&#xff1a; oracle学习数据库例&#xff1a;考试表中有两个字段&#xff1a;startDate、endDate&#xff0c;分别代表考试开始时间、结束时间。现在需要查询某一时间段内正在进行的考试&#xff0c;实际只要满足考试的时间段和查询条件的时间段有交集即可&#xff0c;包含…

LeetCode 2038. 如果相邻两个颜色均相同则删除当前颜色

文章目录1. 题目2. 解题1. 题目 总共有 n 个颜色片段排成一列&#xff0c;每个颜色片段要么是 ‘A’ 要么是 ‘B’ 。 给你一个长度为 n 的字符串 colors &#xff0c;其中 colors[i] 表示第 i 个颜色片段的颜色。 Alice 和 Bob 在玩一个游戏&#xff0c;他们 轮流 从这个字符…

LeetCode 2039. 网络空闲的时刻(BFS)

文章目录1. 题目2. 解题1. 题目 给你一个有 n 个服务器的计算机网络&#xff0c;服务器编号为 0 到 n - 1 。 同时给你一个二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi] 表示服务器 ui 和 vi 之间有一条信息线路&#xff0c;在 一秒 内它们之间可以传输 任意 数目…

LeetCode 2040. 两个有序数组的第 K 小乘积(嵌套二分查找)

文章目录1. 题目2. 解题1. 题目 给你两个 从小到大排好序 且下标从 0 开始的整数数组 nums1 和 nums2 以及一个整数 k &#xff0c;请你返回第 k &#xff08;从 1 开始编号&#xff09;小的 nums1[i] * nums2[j] 的乘积&#xff0c;其中 0 < i < nums1.length 且 0 <…

数据库SQL优化大总结之 百万级数据库优化方案(转载)

原文地址&#xff1a;http://www.cnblogs.com/yunfeifei/p/3850440.html 网上关于SQL优化的教程很多&#xff0c;但是比较杂乱。近日有空整理了一下&#xff0c;写出来跟大家分享一下&#xff0c;其中有错误和不足的地方&#xff0c;还请大家纠正补充。 这篇文章我花费了大量的…

LeetCode 2042. 检查句子中的数字是否递增

文章目录1. 题目2. 解题1. 题目 句子是由若干 token 组成的一个列表&#xff0c;token 间用 单个 空格分隔&#xff0c;句子没有前导或尾随空格。 每个 token 要么是一个由数字 0-9 组成的不含前导零的 正整数 &#xff0c;要么是一个由小写英文字母组成的 单词 。 示例&…

java shiro security_安全框架Shiro和SpringSecurity的比较

两个基本的概念安全实体&#xff1a;系统需要保护的具体对象数据权限&#xff1a;系统相关的功能操作&#xff0c;例如基本的CRUDShiro首先Shiro较之 Spring Security&#xff0c;Shiro在保持强大功能的同时&#xff0c;还在简单性和灵活性方面拥有巨大优势。Shiro是一个强大而…

LeetCode 2043. 简易银行系统

文章目录1. 题目2. 解题1. 题目 你的任务是为一个很受欢迎的银行设计一款程序&#xff0c;以自动化执行所有传入的交易&#xff08;转账&#xff0c;存款和取款&#xff09;。 银行共有 n 个账户&#xff0c;编号从 1 到 n 。每个账号的初始余额存储在一个下标从 0 开始的整数…

LeetCode 2044. 统计按位或能得到最大值的子集数目(状态压缩DP)

文章目录1. 题目2. 解题1. 题目 给你一个整数数组 nums &#xff0c;请你找出 nums 子集 按位或 可能得到的 最大值 &#xff0c;并返回按位或能得到最大值的 不同非空子集的数目 。 如果数组 a 可以由数组 b 删除一些元素&#xff08;或不删除&#xff09;得到&#xff0c;则…

fastapi 请求体 - 多个参数 / 字段Field / 嵌套模型

文章目录1. 混合使用 Path、Query 和请求体参数2. 多个请求体参数3. 请求体中的单一值4. 多个请求体参数和查询参数5. 嵌入单个请求体参数6. 字段7. 嵌套模型7.1 List 字段7.2 子模型作为类型8. 特殊类型校验9. 带有一组子模型的属性10. 任意 dict 构成的请求体learn from http…

java ltpa_LTPA Cookie原理

1. 什么是LTPA?Lightweight Third-Party Authentication (LTPA)是IBM Websphere和Domino产品中使用单点登录技术。当服务器配置好LTPA认证方式&#xff0c;用户通过浏览器成功登录后&#xff0c;服务器会自动发送一个session cookie给浏览器&#xff1b;此cookie中包含一个LTP…