SQLite开发文档:数据类型、文件锁状态、多线程

本文是easeapi.com整理的关于SQLite的第二篇文章,主要介绍SQLite的数据类型、重要的数据结构、文件锁状态、线程模式相关的内容。

SQLite的数据类型

SQLite支持的数据类型有:

  • INTEGER:有符号整形

存储时将根据值大小占用1,2,3,4,6,8字节空间,读取时统一转换成8字节有符号整型。

  • REAL:浮点型,占用8字节;
  • TEXT:字符串;
  • BLOB:二进制数据;
  • NULL:空值。

与大多数其他数据库系统不一样,SQLite不强制约束数据类型(除了INTEGER PRIMARY KEY column)。即使声明为INTEGER类型的字段,也可以插入TEXT、BLOB等类型数据,数据库引擎会尝试自动将值转换为适当的数据类型并存储。参考:Datatypes In SQLite Version 3

SQLite重要的数据结构

Connection

Connection代表在一个事务环境下的一个单独的数据库连接。每一个Connection用sqlite3结构表示,通过sqlite3_open系列接口获取。

Statement

Statement表示一个编译过的SQL语句,包含了执行一个SQL命令所需要一切资源,包括指向磁盘记录的B-tree游标,以及参数等。只要SQL语句不变,对应的Statement可以重复使用,此时可以将Statement缓存以提高效率。Statement在代码中对应sqlite3_stmt结构,通过sqlite3_prepare_v2系列接口获取。

B-tree和Pager

每一个Connection可以包含多个数据库对象,每一个数据库对象都有一个B-tree对象,一个B-tree对应一个pager对象。pager负责读写数据库,管理内存缓存和页,以及管理事务、锁和回滚恢复等。

SQLite内置系统表SQLITE_MASTER,包含数据库中所有表信息。

SQLite的文件锁状态

#define NO_LOCK         0
#define SHARED_LOCK     1
#define RESERVED_LOCK   2
#define PENDING_LOCK    3
#define EXCLUSIVE_LOCK  4

如上,SQLite数据库文件有5种锁状态,实现了线程读写互斥。

  • NO_LOCK

数据库此时没有读写操作。

  • SHARED_LOCK

数据库可以多线程读取但不能写入。

  • RESERVED_LOCK

预留锁,表示准备向数据库写入数据。当一个连接进入RESERVED状态时,pager就开始初始化回卷日志(rollback journal)文件。

  • PENDING_LOCK

表示即将提交写事务,将等待其他读线程释放SHARED锁。一个线程持有PENDING锁后,其他线程就不能获取SHARED锁。只要等所有读线程释放SHARED锁后,才能进入EXCLUSIVE状态。

  • EXCLUSIVE_LOCK

写入数据库。进入这个状态后,其他任何线程都不能访问数据库文件。为保证效率,EXCLUSIVE_LOCK状态的时间越短越好。

SQLite的线程模式

SQLite内部有两个锁开关:bCoreMutex和bFullMutex。他们的作用分别是:

  • bCoreMutex=1:保证不同Connection不会对数据库进行修改;
  • bFullMutex=1:保证了一个Connection不会同时对数据库修改;

由bCoreMutex和bFullMutex控制了SQLite的三种线程模式:

#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0  /* IMP: R-54466-46756 */
    case SQLITE_CONFIG_SINGLETHREAD: {
      /* EVIDENCE-OF: R-02748-19096 This option sets the threading mode to
      ** Single-thread. */
      sqlite3GlobalConfig.bCoreMutex = 0;  /* Disable mutex on core */
      sqlite3GlobalConfig.bFullMutex = 0;  /* Disable mutex on connections */
      break;
    }
#endif
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-20520-54086 */
    case SQLITE_CONFIG_MULTITHREAD: {
      /* EVIDENCE-OF: R-14374-42468 This option sets the threading mode to
      ** Multi-thread. */
      sqlite3GlobalConfig.bCoreMutex = 1;  /* Enable mutex on core */
      sqlite3GlobalConfig.bFullMutex = 0;  /* Disable mutex on connections */
      break;
    }
#endif
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-59593-21810 */
    case SQLITE_CONFIG_SERIALIZED: {
      /* EVIDENCE-OF: R-41220-51800 This option sets the threading mode to
      ** Serialized. */
      sqlite3GlobalConfig.bCoreMutex = 1;  /* Enable mutex on core */
      sqlite3GlobalConfig.bFullMutex = 1;  /* Enable mutex on connections */
      break;
    }
#endif
  • Single-thread

在Single-thread模式下,SQLite会关闭bCoreMutex和bFullMutex开关,在多线程操作下不安全。如果调用方可以保证所有操作均是在单线程环境中进行,则可以选择Single-thread模式,可以减少线程锁的开销。

  • Multi-thread

在Multi-thread模式下,bCoreMutex打开,bFullMutex关闭。此时,只要保证单个Connection不会被多个线程同时使用就是安全的。

  • Serialized

在Serialized模式下,bCoreMutex和bFullMutex都是打开的,SQLite可以不受限制地被多个线程安全使用(支持使用多个Connection,每个Connection都可以多线程读写),但性能也会受到影响。


设定SQLite的线程模式

三个地方的设定都会影响到SQLite的实际运行线程模式:

  • SQLITE_THREADSAFE编译宏

SQLITE_THREADSAFE=0:默认启用Single-thread;
SQLITE_THREADSAFE=2:默认启用Multi-thread;
SQLITE_THREADSAFE=1:默认启用Serialized。

  • sqlite3_open_v2设定

sqlite3_open_v2接口的flags参数可以指定线程模式:

SQLITE_OPEN_NOMUTEX:没有指定单线程模式的情况下,对应Multi-thread模式。
SQLITE_OPEN_FULLMUTEX:没有指定单线程模式的情况下,对应Serialized模式。

  • sqlite3_config设定

SQLITE_CONFIG_SINGLETHREAD
SQLITE_CONFIG_MULTITHREAD
SQLITE_CONFIG_SERIALIZED

如果在编译时不是设定SQLITE_THREADSAFE=0,可以在初始化前调用sqlite3_config或在sqlite3_open时修改线程模式。sqlite3_config的设定,仅在初始化之前(sqlite3_open或sqlite3_initialize执行前)有效。

如果编译时设定SQLITE_THREADSAFE=0,则只能使用单线程模式。

SQL语法的支持

SQLite默认支持常用的SQL语句,目前已知的case有:

  • 支持json扩展

需开启SQLITE_ENABLE_JSON1编译选项。可以存储JSON字符串并查询JSON内容:

SELECT timestamp, json_extract(column, \"$.key\") FROM event;
  • DELETE等操作不支持LIMIT TOP子句

开启SQLITE_ENABLE_UPDATE_DELETE_LIMIT编译选项未能解决问题,可以采取其它语句变相解决。

其他文章

SQLite开发文档:PRAGMA配置、性能优化
SQLite开发文档:SQLCipher加密
iOS NSURLProtocol详解及使用陷阱
iOS Asset Catalog and Bundle