SQLite 锁机制与事务简介

闪存
  • 对象序列化系列

Android 对象序列化之你不知道的 Serializable
Android 对象序列化之 Parcelable 深入分析
Android 对象序列化之追求完美的 Serial

  • 数据序列化系列(待更)

《Android 数据序列化之 JSON》
《Android 数据序列化之 Protocol Buffer 使用》
《Android 数据序列化之 Protocol Buffer 源码分析》

  • SQLite 存储系列

Android 存储选项之 SQLiteDatabase 创建过程源码分析
Android 存储选项之 SQLiteDatabase 源码分析
数据库连接池 SQLiteConnectionPool 源码分析
SQLiteDatabase 启用事务源码分析
SQLite 数据库 WAL 模式工作原理简介
SQLite 数据库锁机制与事务简介
SQLite 数据库优化那些事儿


SQLite 锁机制简介

SQLite 采用粗放型的锁。当一个连接要写数据库,所有其他的连接被锁住,直到写连接结束了它的事务。SQLite 有一个加锁表,来帮助不同的写数据库都能够在最后一刻再加锁,以保证最大的并发性。

SQLite 使用锁逐步上升机制,为了写数据库,连接需要逐级地获得排它锁。SQLite 有 5 个不同的锁状态:未加锁(UNLOCKED)、共享锁(SHARED)、保留(RESERVED)、未决(PENDING)和排它(EXCLUSIVE)。每个数据库在同一时刻只能处于其中一个状态。每种状态(未加锁状态除外)都有一种锁与之对应。锁的状态以及状态的转换如下图所示:

最初的状态是未加锁状态,在此状态下,连接还没有存取数据库。当连接到了一个数据库,甚至已经用 BEGIN 开始了一个事务,连接都还处于未加锁状态。

未加锁状态的下一个状态时共享状态。为了能够从数据库中读(不写)数据,连接必须首先进入共享状态,也就是说首先要获得一个共享锁。多个连接可以同时获得并保持共享锁,也就是说多个连接可以同时从一个数据库中读数据。但哪怕是只有一个共享锁还没有释放,也不允许任何连接写数据库。

如果一个连接想要写数据库,它必须首先获得一个保留锁。一个数据库上同时只能有一个保留锁。保留锁可以与共享锁共存,保留锁是写数据库的第一阶段。保留锁既不阻止其它拥有共享锁的连接继续读数据库,也不阻止其它连接获得新的共享锁。一旦一个连接获得了保留锁,它就可以开始处理数据库修改操作了,尽管这些修改只能在缓冲区中进行,而不是实际地写到磁盘。对读出内容所做的修改保存在内存缓冲区中。当连接想要提交修改(或事物)时,需要将保留锁提升为排它锁。为了得到排它锁,还必须首先将保留锁提升为未决锁。获得未决锁之后,其它连接就不能再获得新的共享锁了,但已经拥有共享锁的连接仍然可以继续正常读数据库。此时,拥有未决锁的连接等待其它拥有共享锁的连接完成工作并释放其共享锁。一旦所有其它共享锁都被释放,拥有未决锁的连接就可以将其锁提升至排它锁,此时就可以自由地对数据库进行修改了。所有以前对缓冲区所做的修改都会被写到数据库文件。

sqlite 锁机制
死锁

虽然您可能觉得前面关于锁的讨论很有趣,但是您可能也在考虑为什么锁如此重要?为什么需要了解锁的机制?如果不了解您正在做什么,您可能会陷入死锁。

举例下表中,两个连接 A 和 B 完全不知道对方在同一时刻对数据库进行操作。连接 A 启动第一个命令,B 启动第二、第三个命令,A 启动第四个,如此等等。

执行顺序 连接 A 连接 B
1 sqlite> begin;
2 sqlite> begin
3 sqlite> insert into foo values('x')
4 sqlite> select * from foo;
5 sqlite> commit
6 SQL error: database is locked
7 sqlite> insert into foo values('x')
8 SQL error: database is locked

两个连接都在死锁中结束。B 连接首先尝试写数据库,也就拥有了一个未决锁。A 连接再试图写,但当其 insert 语句试图将共享锁提升为预留锁时失败。

为了方便讨论,在此假设连接 A 和 B 都一直在等待数据库可写。那么此时,其它的连接都被锁在外面。如果试图打开第三个连接,它甚至都不能读数据库。因为 B 连接拥有未决锁(它能阻止其它连接获得共享锁)。那么此时,不仅 A 和 B 死锁了,而且它们也将其他人锁在数据库外。基本上,有共享锁和未决锁的那些连接如果偶不想放弃控制,其它所有的进程都不能再操作此数据库了。

如何避免死锁?当然不能让 A 和 B 坐在会议室通过彼此的律师谈判解决,因为它们甚至不知道彼此的存在。答案是采用正确的事务类型来完成工作。

SQLite 事务简介

SQLite 有三种不同的事务,使用不同的锁状态。事务可以开始于:DEFERRED、IMMEDIATE 或 EXCLUSIVE。事务类型在 BEGIN 命令中指定:BEGIN [DEFERRED | IMMEDIATE | EXCLUSIVE] TRANSACTION;

一个 DEFERRED 事务不获取任何锁(直到它需要锁的时候),BEGIN 语句本身也不会做什么事情——它开始于 UNLOCK 状态。默认情况下就是这样的,如果仅仅用 BEGIN 开始一个事务,那么事务就是 DEFERRED 的,同时它不会获取任何锁;当对数据库进行第一次读操作时,它会获取 SHARED 锁;同样,当进行第一次写操作时,它会获取 RESERVED 锁。由 BEGIN 开始的 IMMEDIATE 事务会尝试获取 RESERVED 锁。如果成功,BEGIN IMMEDIATE 保证没有别的连接可以写数据库。但是,别的连接可以对数据库进行读操作;但是,RESERVED 锁会阻止其它连接的 BEGIN IMMEDIATE 或者 BEGIN EXCLUSIVE 命令,当其它连接执行上述命令时,会返回 SQLITE_BUSY 错误。这时候你就可以对数据库进行修改操作了,但是你还不能提交,当你 COMMIT 时,会返回 SQLITE_BUSY 错误,这意味着还有其它的读事务没有完成,得等它们执行完后才能提交事务。

EXCLUSIVE 事务会尝试着获取对数据库的 EXCLUSIVE 锁。这与 IMMEDIATE 类似,但是一旦成功,EXCLUSIVE 事务保证没有其它的连接,所以就可能对数据库进行读写操作了。上节那个例子的问题在于两个连接最终都想写数据库,但是它们都没有放弃各自原来的锁,最终,SHARED 锁导致了问题的出现。如果两个都已 BEGIN IMMEDIATE 开始事务,那么死锁就不会发生。在这种情况下,在同一时刻只能有一个连接进入 BEGIN IMMEDIATE 开始事务,那么死锁就不会发生。在这种情况下,在同一时刻只能有一个连接进入 BEGIN IMMEDIATE,其它的连接就得等待。BEGIN IMMEDIATE 和 BEGIN EXCLUSIVE 通常被写事务使用。就像同步机制一样,它防止了死锁的产生。

基本准则是:如果你正在使用的数据库没有其它的连接,用 BEGIN 就足够了。但是,如果你使用的数据库有其它的连接也会对数据库进行写操作,就得使用 BEGIN IMMEDIATE 或 BEGIN EXCLUSIVE 开始你的事务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • SQLite锁机制 SQLite有一个加锁表,用来帮助不同的写数据库都能够在最后一刻加锁,保证最大的并发性。 SQ...
    carver阅读 2,273评论 0 4
  • 一、数据库锁 SQLite 采用粗放型的锁。当一个连接要写数据库,所有其它的连接被锁住,直到写连接结束了它的事务。...
    须左能乎阅读 695评论 0 49
  • 事务定义了一组SQL命令的边界,这组命令或者作为一个整体被全部执行,或者都不执行,这称为数据库完整性的原子性原...
    我系哆啦阅读 1,085评论 0 9
  • [TOC] SQL语句 表相关语句 创建表 temp, temporary:表示创建的表是临时表,只在当前会话有效...
    老干妈拌饭阅读 738评论 0 0
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 4,865评论 0 9