非常教程

Sqlite参考手册

其他 | Miscellaneous

SQLite Unlock-Notify API

/* This example uses the pthreads API */
#include <pthread.h>

/*
** A pointer to an instance of this structure is passed as the user-context
** pointer when registering for an unlock-notify callback.
*/
typedef struct UnlockNotification UnlockNotification;
struct UnlockNotification {
  int fired;                         /* True after unlock event has occurred */
  pthread_cond_t cond;               /* Condition variable to wait on */
  pthread_mutex_t mutex;             /* Mutex to protect structure */
};

/*
** This function is an unlock-notify callback registered with SQLite.
*/
static void unlock_notify_cb(void **apArg, int nArg){
  int i;
  for(i=0; i<nArg; i++){
    UnlockNotification *p = (UnlockNotification *)apArg[i];
    pthread_mutex_lock(&p->mutex);
    p->fired = 1;
    pthread_cond_signal(&p->cond);
    pthread_mutex_unlock(&p->mutex);
  }
}

/*
** This function assumes that an SQLite API call (either sqlite3_prepare_v2() 
** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
** associated database connection.
**
** This function calls sqlite3_unlock_notify() to register for an 
** unlock-notify callback, then blocks until that callback is delivered 
** and returns SQLITE_OK. The caller should then retry the failed operation.
**
** Or, if sqlite3_unlock_notify() indicates that to block would deadlock 
** the system, then this function returns SQLITE_LOCKED immediately. In 
** this case the caller should not retry the operation and should roll 
** back the current transaction (if any).
*/
static int wait_for_unlock_notify(sqlite3 *db){
  int rc;
  UnlockNotification un;

  /* Initialize the UnlockNotification structure. */
  un.fired = 0;
  pthread_mutex_init(&un.mutex, 0);
  pthread_cond_init(&un.cond, 0);

  /* Register for an unlock-notify callback. */
  rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
  assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );

  /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED 
  ** or SQLITE_OK. 
  **
  ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
  ** case this function needs to return SQLITE_LOCKED to the caller so 
  ** that the current transaction can be rolled back. Otherwise, block
  ** until the unlock-notify callback is invoked, then return SQLITE_OK.
  */
  if( rc==SQLITE_OK ){
    pthread_mutex_lock(&un.mutex);
    if( !un.fired ){
      pthread_cond_wait(&un.cond, &un.mutex);
    }
    pthread_mutex_unlock(&un.mutex);
  }

  /* Destroy the mutex and condition variables. */
  pthread_cond_destroy(&un.cond);
  pthread_mutex_destroy(&un.mutex);

  return rc;
}

/*
** This function is a wrapper around the SQLite function sqlite3_step().
** It functions in the same way as step(), except that if a required
** shared-cache lock cannot be obtained, this function may block waiting for
** the lock to become available. In this scenario the normal API step()
** function always returns SQLITE_LOCKED.
**
** If this function returns SQLITE_LOCKED, the caller should rollback
** the current transaction (if any) and try again later. Otherwise, the
** system may become deadlocked.
*/
int sqlite3_blocking_step(sqlite3_stmt *pStmt){
  int rc;
  while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
    rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
    if( rc!=SQLITE_OK ) break;
    sqlite3_reset(pStmt);
  }
  return rc;
}

/*
** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
** It functions in the same way as prepare_v2(), except that if a required
** shared-cache lock cannot be obtained, this function may block waiting for
** the lock to become available. In this scenario the normal API prepare_v2()
** function always returns SQLITE_LOCKED.
**
** If this function returns SQLITE_LOCKED, the caller should rollback
** the current transaction (if any) and try again later. Otherwise, the
** system may become deadlocked.
*/
int sqlite3_blocking_prepare_v2(
  sqlite3 *db,              /* Database handle. */
  const char *zSql,         /* UTF-8 encoded SQL statement. */
  int nSql,                 /* Length of zSql in bytes. */
  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
  const char **pz           /* OUT: End of parsed string */
){
  int rc;
  while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
    rc = wait_for_unlock_notify(db);
    if( rc!=SQLITE_OK ) break;
  }
  return rc;
}

当两个或更多连接以共享高速缓存模式访问同一数据库时,将使用对各个表的读和写(共享和排它)锁来确保并发执行的事务处于隔离状态。在写入表之前,必须在该表上获得写(独占)锁。在阅读之前,必须获得读取(共享)锁定。连接在结束其事务时释放所有持有的表锁。如果连接无法获得所需的锁定,则对sqlite3_step()的调用将返回SQLITE_LOCKED。

虽然它不太常见,但如果sqlite3_prepare()或sqlite3_prepare_v2()的调用无法在每个附加数据库的sqlite_master表上获得读锁,则也可能会返回SQLITE_LOCKED。这些API需要读取包含在sqlite_master表中的模式数据,以便将SQL语句编译到sqlite3_stmt *对象。

本文介绍了一种使用SQLite sqlite3_unlock_notify()接口的技术,以便调用sqlite3_step()和sqlite3_prepare_v2(),直到所需的锁可用而不是立即返回SQLITE_LOCKED为止。如果sqlite3_blocking_step()或sqlite3_blocking_prepare_v2()函数向左返回SQLITE_LOCKED,则表明阻塞会导致系统死锁。

sqlite3_unlock_notify()API仅在库的预定义SQLITE_ENABLE_UNLOCK_NOTIFY定义的情况下编译时才可用。本文不能替代阅读完整的API文档!

sqlite3_unlock_notify()接口设计用于具有分配给每个数据库连接的单独线程的系统。实现中没有任何内容阻止单个线程运行多个数据库连接。但是,sqlite3_unlock_notify()接口一次只能在单个连接上工作,因此此处介绍的锁解析逻辑仅适用于每个线程的单个数据库连接。

The sqlite3_unlock_notify() API

在调用sqlite3_step()或sqlite3_prepare_v2()返回SQLITE_LOCKED之后,可调用sqlite3_unlock_notify()API来注册解锁通知回调。在数据库连接持有表锁并阻止从成功调用sqlite3_step()或sqlite3_prepare_v2()完成其事务并释放所有锁之后,SQLite将调用unlock-notify回调。例如,如果对sqlite3_step()的调用是尝试从表X中读取数据,并且某个其他连接Y在表X上持有写锁定,则sqlite3_step()将返回SQLITE_LOCKED。如果然后调用sqlite3_unlock_notify(),则在连接Y的事务结束后,将调用unlock-notify回调。解锁通知回调正在等待的连接(在本例中为连接Y)被称为“

如果试图写入数据库表的sqlite3_step()调用返回SQLITE_LOCKED,那么多个其他连接可能正在对正在讨论的数据库表进行读锁。在这种情况下,SQLite只是任意选择其中一个连接,并在连接的事务完成时发出unlock-notify回调。无论对sqlite3_step()的调用是否被一个或多个连接阻止,当发出相应的unlock-notify回调时,都不能保证所需的锁可用,而只有它可能使用。

当发出unlock-notify回调时,它会在与阻塞连接关联的sqlite3_step()(或sqlite3_close())的调用中发出。从unlock-notify回调中调用任何sqlite3_XXX()API函数是非法的。预期的用途是解锁通知回调将发出一些其他等待线程的信号或安排稍后发生的某些操作。

sqlite3_blocking_step()函数使用的算法如下所示:

  • 在提供的语句句柄上调用sqlite3_step()。如果调用返回SQLITE_LOCKED以外的任何内容,则将该值返回给调用者。否则,继续。
  • 调用与提供的语句句柄关联的数据库连接句柄上的sqlite3_unlock_notify()以注册解锁通知回调。如果对unlock_notify()的调用返回SQLITE_LOCKED,则将此值返回给调用者。
  • 阻塞直到unlock-notify回调被另一个线程调用。
  • 在语句句柄上调用sqlite3_reset()。由于SQLITE_LOCKED错误可能只发生在第一次调用sqlite3_step()时(一次调用sqlite3_step()不可能返回SQLITE_ROW,然后是下一次SQLITE_LOCKED),语句句柄可能会在此时被重置,而不会影响结果从调用者的角度来看的查询。如果此时未调用sqlite3_reset(),则下一次调用sqlite3_step()将返回SQLITE_MISUSE。
  • 返回到第1步。

sqlite3_blocking_prepare_v2()函数使用的算法类似,但省略了步骤4(重置语句句柄)。

Writer Starvation

多个连接可能同时持有读锁。如果许多线程正在获取重叠的读锁,则可能是至少有一个线程始终持有读锁。然后等待写锁的表将永远等待。这种情况被称为“作家饥饿”。

SQLite帮助应用程序避免编写器匮乏。任何试图在表上获取写入锁的尝试都失败(因为一个或多个其他连接持有读取锁),则在共享缓存中打开新事务的所有尝试都会失败,直到满足以下条件之一为止:

  • 现任作家结束交易,或者其他方法
  • 共享缓存中的开放读取事务数量降为零。

无法尝试打开新的读事务将SQLITE_LOCKED返回给调用者。如果调用者然后调用sqlite3_unlock_notify()注册一个unlock-notify回调,则阻塞连接是当前在共享缓存上具有开放写入事务的连接。这可以防止编写器匮乏,因为如果没有新的读取事务可以打开并且假设所有现有的读取事务最终结束,那么作者最终将有机会获得所需的写入锁定。

The pthreads API

在wait_for_unlock_notify()调用sqlite3_unlock_notify()时,阻止sqlite3_step()或sqlite3_prepare_v2()调用成功的阻塞连接可能已完成其事务。在这种情况下,在sqlite3_unlock_notify()返回之前立即调用unlock-notify回调。或者,在调用sqlite3_unlock_notify()之后但在线程开始等待异步发送信号之前,可能会由第二个线程调用unlock-notify回调。

具体如何处理这种潜在的竞争条件取决于应用程序使用的线程和同步原语接口。这个例子使用pthreads,这是由类似UNIX的系统(包括Linux)提供的接口。

pthreads接口提供了pthread_cond_wait()函数。这个函数允许调用者同时释放一个互斥并开始等待一个异步信号。使用这个函数,一个“被触发的”标记和一个互斥体,上面描述的竞态条件可以被消除如下:

当调用unlock-notify回调时,可能会在调用sqlite3_unlock_notify()的线程开始等待异步信号之前执行以下操作:

  • 获得互斥体。
  • 将“已触发”标志设置为true。
  • 尝试发信号通知等待的线程。
  • 释放互斥量。

当wait_for_unlock_notify线程准备好开始等待unlock-notify回调到达时,它会:

  • 获得互斥体。
  • 检查是否已设置“已触发”标志。如果是这样,则解锁通知回调已被调用。释放互斥并继续。
  • 从原理上释放互斥并开始等待异步信号。信号到达时,继续。

这样,当wait_for_unlock_notify()线程开始阻塞时,unlock-notify回调是否已经被调用或正在被调用并不重要。

Possible Enhancements

本文中的代码至少可以通过以下两种方式进行改进:

  • 它可以管理线程优先级。
  • 它可以处理删除表或索引时可能发生的SQLITE_LOCKED特殊情况。

尽管sqlite3_unlock_notify()函数只允许调用者指定单个用户上下文指针,但是一个unlock-notify回调函数会传递一个这样的上下文指针数组。这是因为如果阻塞连接结束其事务时,如果有多个unlock-notify被注册以调用相同的C函数,则将上下文指针编组为一个数组并发出一个回调函数。如果每个线程都被分配了一个优先级,那么不是像这个实现那样只是以任意顺序发信号通知线程,而是可以在较低优先级的线程之前发送更高优先级的线程。

如果执行“DROP TABLE”或“DROP INDEX”SQL命令,并且同一个数据库连接当前有一个或多个主动执行的SELECT语句,则返回SQLITE_LOCKED。如果在这种情况下调用sqlite3_unlock_notify(),则将立即调用指定的回调。重新尝试“DROP TABLE”或“DROP INDEX”语句将返回另一个SQLITE_LOCKED错误。在左侧显示的sqlite3_blocking_step()的实现中,这可能会导致无限循环。

调用者可以通过使用扩展错误代码来区分这种特殊的“DROP TABLE | INDEX”情况和其他情况。如果适合调用sqlite3_unlock_notify(),则扩展错误代码为SQLITE_LOCKED_SHAREDCACHE。否则,在“DROP TABLE | INDEX”的情况下,它只是简单的SQLITE_LOCKED。另一种解决方案可能是限制任何单个查询可能被重试的次数(比如说100)。虽然这可能不如人们想象的那么有效,但这种情况不太可能经常发生。

 SQLite在公共领域。

其他 | Miscellaneous相关

1.35% Faster Than The Filesystem
2.8+3 Filenames
3.An Asynchronous I/O Module For SQLite
4.Appropriate Uses For SQLite
5.Architecture of SQLite
6.Atomic Commit In SQLite
7.Automatic Undo/Redo With SQLite
8.Benefits of SQLite As A File Format
9.Change in Default Page Size in SQLite Version 3.12.0
10.Clustered Indexes and the WITHOUT ROWID Optimization
11.Compile-time Options
12.Constraint Conflict Resolution in SQLite
13.Custom Builds Of SQLite
14.Deterministic SQL Functions
15.Distinctive Features Of SQLite
16.EXPLAIN QUERY PLAN
17.Features Of SQLite
18.File Format Changes in SQLite
19.Full-Featured SQL
20.High Reliability
21.Hints for Debugging SQLite
22.How SQLite Is Tested
23.How To Compile SQLite
24.How To Download Canonical SQLite Source Code
25.Imposter Tables
26.In-Memory Databases
27.Indexes On Expressions
28.Internal Versus External BLOBs
29.Isolation In SQLite
30.Long Term Support
31.Maintaining Private Branches Of SQLite
32.Many Small Queries Are Efficient In SQLite
33.Measuring and Reducing CPU Usage in SQLite
34.Memory-Mapped I/O
35.NULL Handling in SQLite
36.Partial Indexes
37.Pointer Passing Interfaces
38.Powersafe Overwrite
39.Release History Of SQLite
40.Result and Error Codes
41.Row Values
42.Rowid Tables
43.Run-Time Loadable Extensions
44.SQL Features That SQLite Does Not Implement
45.sqldiff.exe: Database Difference Utility
46.SQLite As An Application File Format
47.SQLite Autoincrement
48.SQLite Backup API
49.SQLite Changes From Version 3.4.2 To 3.5.0
50.SQLite Changes From Version 3.5.9 To 3.6.0
51.SQLite Database Speed Comparison
52.SQLite File IO Specification
53.SQLite Frequently Asked Questions
54.SQLite In 5 Minutes Or Less
55.SQLite is a Self Contained System
56.SQLite Is Serverless
57.SQLite Is Transactional
58.SQLite Library Footprint
59.SQLite Shared-Cache Mode
60.SQLite Version 3 Overview
61.SQLite: Single File Database
62.Temporary Files Used By SQLite
63.TH3
64.The COMPLETION() Table-Valued Function
65.The CSV Virtual Table
66.The dbhash.exe Utility Program
67.The DBSTAT Virtual Table
68.The Error And Warning Log
69.The generate_series Table-Valued Function
70.The OS Backend (VFS) To SQLite
71.The Spellfix1 Virtual Table
72.The SQLite Amalgamation
73.The SQLite Bytecode Engine
74.The sqlite3_analyzer.exe Utility Program
75.The SQLITE_STMT Virtual Table
76.The UNION Virtual Table
77.The Virtual Database Engine of SQLite
78.Uniform Resource Identifiers
79.Using SQLite In Multi-Threaded Applications
80.Version Numbers in SQLite
81.What If OpenDocument Used SQLite?
82.Why Is SQLite Coded In C
83.Zero-Configuration
Sqlite

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来

主页 https://sqlite.org/
源码 https://www.sqlite.org/src/
发布版本 3.21.0

Sqlite目录

1.C界面 | C Interface
2.C Interface: Session Module
3.CLI
4.数据库文件表 | Database File Format
5.数据类 | Datatypes
6.动态内存分配 | Dynamic Memory Allocation
7.外键约束 | Foreign Key Constraints
8.全文索引 | Full-Text Search
9.损坏方式 | How To Corrupt
10.JSON
11.语言 | Language
12.局限性 | Limits
13.锁定和并发 | Locking and Concurrency
14.其他 | Miscellaneous
15.PRAGMA Statements
16.查询计划程序 | Query Planner
17.R*Tree Module
18.RBU Extension
19.语法图 | Syntax Diagrams
20.Tcl Interface
21.虚拟表机制 | Virtual Table Mechanism
22.预写日志 | Write-Ahead Logging
23.SQL 教程
24.SQL 简介
25.SQL 语法
26.SQL DELETE 语句
27.SQL UPDATE 语句
28.SQL NOT NULL 约束
29.SQL 约束
30.SQL CREATE TABLE 语句
31.SQL CREATE DATABASE 语句
32.SQL INSERT INTO SELECT 语句
33.SQL SELECT INTO 语句
34.SQL CREATE VIEW、REPLACE VIEW、 DROP VIEW 语句
35.SQL AUTO INCREMENT 字段
36.SQL ALTER TABLE 语句
37.SQL 撤销索引、表以及数据库
38.SQL CREATE INDEX 语句
39.SQL DEFAULT 约束
40.SQL CHECK 约束
41.SQL FOREIGN KEY 约束
42.SQL PRIMARY KEY 约束
43.SQL UNIQUE 约束
44.SQL 通用数据类型
45.SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
46.SQL NULL 值 – IS NULL 和 IS NOT NULL
47.SQL Server 和 MySQL 中的 Date 函数
48.SQL MS Access、MySQL 和 SQL Server 数据类型
49.SQL 函数
50.SQL 总结
51.SQL 主机
52.SQL 快速参考
53.SQL ROUND() 函数
54.SQL Server GETDATE() 函数
55.MySQL DATE_FORMAT() 函数
56.MySQL DATEDIFF() 函数
57.MySQL DATE_SUB() 函数
58.MySQL DATE_ADD() 函数
59.MySQL EXTRACT() 函数
60.MySQL DATE() 函数
61.MySQL CURTIME() 函数
62.MySQL CURDATE() 函数
63.MySQL NOW() 函数
64.SQL Server CONVERT() 函数
65.SQL Server DATEDIFF() 函数
66.SQL Server DATEADD() 函数
67.SQL Server DATEPART() 函数
68.SQLite 命令
69.SQLite 安装
70.SQLite 简介
71.SQLite 运算符
72.SQLite Select 语句
73.SQLite 删除表
74.SQLite 创建表
75.SQLite Insert 语句
76.SQLite 分离数据库
77.SQLite 附加数据库
78.SQLite 创建数据库
79.SQLite 数据类型
80.SQLite 语法
81.SQLite Order By
82.SQLite Limit 子句
83.SQLite Glob 子句
84.SQLite Like 子句
85.SQLite Delete 语句
86.SQLite Update 语句
87.SQLite AND/OR 运算符
88.SQLite Where 子句
89.SQLite 表达式
90.SQLite Distinct 关键字
91.SQLite Having 子句
92.SQLite Group By
93.SQLite Join
94.SQLite 约束
95.SQLite PRAGMA
96.SQLite 事务
97.SQLite 视图
98.SQLite Truncate Table
99.SQLite Alter 命令
100.SQLite Indexed By