03 - SQLite 技术全景:嵌入式关系数据库引擎完整解析

03 - SQLite 技术全景:嵌入式关系数据库引擎完整解析

全球部署量最大的数据库引擎 | Android 数据存储基石 | 24万行单文件架构

目录

  1. 概述
  2. 架构设计
  3. 核心组件
  4. SQL执行流程
  5. 构建系统
  6. Android集成
  7. 性能优化
  8. 高级特性
  9. 测试基础设施
  10. 最佳实践

概述

什么是 SQLite?

SQLite 是一个零配置、无服务器、自包含的 SQL 数据库引擎,是世界上使用最广泛的数据库软件。每个 Android 设备都包含多个 SQLite 数据库实例。

位置 : external/sqlite/
语言 : C (单文件架构)
代码行数 : ~241,651 行(sqlite3.c 单文件)
许可证 : Public Domain(公有领域)
版本: 基于 SQLite 3.x 系列

核心特性

特性 说明
零配置 无需安装、配置或管理
自包含 单文件数据库,易于备份和传输
无服务器 直接读写磁盘文件
跨平台 支持 Android、iOS、Linux、Windows、macOS
ACID事务 完整的原子性、一致性、隔离性、持久性
完整SQL 支持 SQL-92 标准及扩展
小巧高效 编译后仅 ~2-3 MB

使用统计

  • 全球部署: 超过 1 万亿个活跃数据库实例
  • Android 集成: 每个应用可创建多个数据库
  • 系统使用: 联系人、通话记录、浏览器历史、设置等
  • 应用使用: 微信、WhatsApp、Facebook 等主流应用

架构哲学

复制代码
"SQLite is not designed to compete with Oracle.
 It's designed to compete with fopen()."

 ------ SQLite 设计目标:取代文件 I/O

核心理念:

  • 简单性: 单一库文件,零依赖
  • 可靠性: 经过数十亿次部署验证
  • 效率: 针对嵌入式环境优化
  • 稳定性: API 向后兼容 20+ 年

架构设计

整体架构层次

复制代码
┌─────────────────────────────────────────┐
│         应用层 (Application)             │
│   android.database.sqlite.SQLiteDatabase │
└─────────────────────────────────────────┘
                    ↓ JNI
┌─────────────────────────────────────────┐
│         接口层 (Interface)               │
│   sqlite3_open, sqlite3_prepare, etc.   │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│      SQL编译器 (SQL Compiler)            │
│  Tokenizer → Parser → Code Generator    │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│      虚拟机 (Virtual Machine - VDBE)     │
│      150+ Opcodes, Memory Cells         │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│        B-树引擎 (B-tree Engine)          │
│     Table B-tree, Index B-tree          │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│        分页器 (Pager & Cache)            │
│   Page Cache, Journal, WAL, Locking     │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│       操作系统接口 (OS Interface - VFS)   │
│      File I/O, Locking, Memory          │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│           磁盘文件 (Disk File)           │
│         .db, .db-journal, .db-wal       │
└─────────────────────────────────────────┘

模块职责

模块 文件 职责
Tokenizer tokenize.c 词法分析,将SQL文本转换为Token流
Parser parse.c 语法分析,构建抽象语法树(AST)
Code Generator select.c, insert.c, update.c, delete.c 将AST转换为VDBE字节码
VDBE vdbe.c, vdbeaux.c, vdbeapi.c 执行字节码指令
B-tree btree.c 管理表和索引的B+树存储
Pager pager.c 页缓存、事务、崩溃恢复
OS Interface os_unix.c, os_win.c 平台抽象层

数据流向

复制代码
SQL字符串输入
    ↓
词法分析 (Tokenizer)
    ↓
语法分析 (Parser) → AST
    ↓
查询优化 (Query Planner)
    ↓
代码生成 → VDBE字节码
    ↓
VDBE执行 → 操作B-tree游标
    ↓
B-tree → 读写页面
    ↓
Pager → 缓存管理
    ↓
VFS → 文件I/O
    ↓
磁盘存储

核心组件

1. SQL 编译器

1.1 Tokenizer(词法分析器)

位置 : sqlite3.c 内嵌(tokenize.c 逻辑)
行数: ~3,000 行

功能:

  • 将 SQL 文本分解为 Token
  • 识别关键字、标识符、字面量、操作符
  • 处理注释和空白字符
  • 支持 UTF-8 和 UTF-16 编码

Token 类型:

c 复制代码
#define TK_SEMI       1    // ;
#define TK_EXPLAIN    2    // EXPLAIN
#define TK_QUERY      3    // QUERY
#define TK_PLAN       4    // PLAN
#define TK_BEGIN      5    // BEGIN
#define TK_TRANSACTION 6   // TRANSACTION
#define TK_DEFERRED   7    // DEFERRED
#define TK_IMMEDIATE  8    // IMMEDIATE
#define TK_EXCLUSIVE  9    // EXCLUSIVE
#define TK_COMMIT    10    // COMMIT
#define TK_END       11    // END
#define TK_ROLLBACK  12    // ROLLBACK
#define TK_SAVEPOINT 13    // SAVEPOINT
#define TK_RELEASE   14    // RELEASE
#define TK_TO        15    // TO
#define TK_TABLE     16    // TABLE
#define TK_CREATE    17    // CREATE
#define TK_IF        18    // IF
// ... 150+ token types

关键字哈希表:

  • 使用完美哈希函数
  • O(1) 关键字识别
  • 147 个 SQL 关键字

示例:

sql 复制代码
SELECT name FROM users WHERE age > 18;

Token 流:

复制代码
TK_SELECT → TK_ID(name) → TK_FROM → TK_ID(users) →
TK_WHERE → TK_ID(age) → TK_GT → TK_INTEGER(18) → TK_SEMI
1.2 Parser(语法分析器)

位置 : sqlite3.c 内嵌(parse.c)
行数 : ~14,000 行
工具: LEMON Parser Generator

核心数据结构:

c 复制代码
typedef struct Parse Parse;
struct Parse {
  sqlite3 *db;              // 数据库连接
  char *zErrMsg;            // 错误消息
  Vdbe *pVdbe;              // 正在构建的虚拟机
  int rc;                   // 返回码
  u8 colNamesSet;           // 列名是否已设置
  u8 checkSchema;           // 是否需要检查Schema
  u8 nested;                // 嵌套级别
  u8 nTempReg;              // 临时寄存器数量
  u8 isMultiWrite;          // 是否多写操作
  u8 mayAbort;              // 是否可能中止
  int nRangeReg;            // 范围寄存器数量
  int iRangeReg;            // 范围寄存器起始
  int nErr;                 // 错误计数
  int nTab;                 // 表数量
  int nMem;                 // 内存单元数量
  int nSet;                 // Set指令数量
  int nOnce;                // Once指令数量
  int nOpAlloc;             // 已分配的操作码数量
  int iSelfTab;             // Self-tab for UPDATE/DELETE
  AggInfo *pAggInfo;        // 聚合函数信息
  Trigger *pTriggerPrg;     // 触发器程序
  Table *pTriggerTab;       // 触发器表
  Parse *pToplevel;         // 顶层解析器
  Table *pTriggerTab;       // 触发器表
};

AST 节点类型:

c 复制代码
typedef struct Expr Expr;        // 表达式节点
typedef struct ExprList ExprList; // 表达式列表
typedef struct Select Select;     // SELECT语句
typedef struct SrcList SrcList;   // FROM子句表列表
typedef struct IdList IdList;     // 标识符列表
typedef struct Where WhereInfo;   // WHERE子句信息

语法规则示例:

c 复制代码
// LEMON语法定义
cmd ::= SELECT selcollist(X) from(F) where_opt(W) groupby_opt(G)
        having_opt(H) orderby_opt(O) limit_opt(L). {
  Select *pSelect = sqlite3SelectNew(pParse, X, F, W, G, H, O, L);
  sqlite3Select(pParse, pSelect, &dest);
}

expr(A) ::= expr(X) AND expr(Y). {
  A.pExpr = sqlite3ExprAnd(pParse->db, X.pExpr, Y.pExpr);
}

expr(A) ::= expr(X) OR expr(Y). {
  A.pExpr = sqlite3ExprOr(pParse->db, X.pExpr, Y.pExpr);
}
1.3 Code Generator(代码生成器)

位置 : select.c, insert.c, update.c, delete.c, expr.c
行数: ~90,000 行(合计)

功能:

  • 将 AST 转换为 VDBE 字节码
  • 生成查询执行计划
  • 寄存器分配
  • 游标管理

SELECT 语句代码生成流程:

c 复制代码
// select.c 核心函数
int sqlite3Select(
  Parse *pParse,         // 解析器上下文
  Select *p,             // SELECT语句AST
  SelectDest *pDest      // 结果输出目标
) {
  // 1. 解析FROM子句,打开表和索引
  // 2. 生成WHERE子句过滤代码
  // 3. 生成GROUP BY聚合代码
  // 4. 生成HAVING过滤代码
  // 5. 生成ORDER BY排序代码
  // 6. 生成LIMIT限制代码
  // 7. 生成结果输出代码
}

示例SQL及生成的VDBE代码:

sql 复制代码
SELECT name FROM users WHERE age > 18;

生成的字节码(简化版):

复制代码
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------
0     Init           0     15    0                    0   Start at 15
1     OpenRead       0     2     0     3              0   root=2 iDb=0; users
2     Rewind         0     13    0                    0
3       Column       0     2     1                    0   r[1]=users.age
4       Ge             18    12    1     (BINARY)      81  if r[1]>=18 goto 12
5       Column       0     1     2                    0   r[2]=users.name
6       ResultRow    2     1     0                    0   output=r[2]
12    Next           0     3     0                    1
13    Close          0     0     0                    0
14    Halt           0     0     0                    0
15    Transaction    0     0     1     0              1   usesStmtJournal=0
16    Goto           0     1     0                    0

指令说明:

  • OpenRead: 打开表游标
  • Rewind: 定位到第一行
  • Column: 读取列值
  • Ge: 大于等于比较
  • ResultRow: 输出结果行
  • Next: 移动到下一行

2. 虚拟数据库引擎 (VDBE)

2.1 VDBE 架构

位置 : vdbe.c, vdbeaux.c, vdbeapi.c, vdbemem.c
行数: ~40,000 行

核心概念:

  • SQLite 的"心脏" ------ 执行所有数据库操作
  • 基于寄存器的虚拟机(不是栈式)
  • 类似于 JVM 或 .NET CLR,但专为数据库设计

VDBE 结构:

c 复制代码
typedef struct Vdbe Vdbe;
struct Vdbe {
  sqlite3 *db;              // 数据库连接
  Vdbe *pPrev, *pNext;      // 链表
  Parse *pParse;            // 解析器上下文
  int nMem;                 // 内存单元数量
  int nCursor;              // 游标数量
  u32 cacheCtr;             // 缓存计数器
  int pc;                   // 程序计数器
  int rc;                   // 返回码
  u16 nResColumn;           // 结果列数量
  u16 nOp;                  // 指令数量
  int nOpAlloc;             // 已分配指令数量
  VdbeOp *aOp;              // 指令数组
  Mem *aMem;                // 内存单元数组
  Mem **apArg;              // 参数数组
  Mem *pResultSet;          // 结果集
  char *zErrMsg;            // 错误消息
  VdbeCursor **apCsr;       // 游标数组
  // ... 更多字段
};
2.2 操作码系统

操作码数量 : 150+ opcodes
位置: opcodes.h(自动生成)

主要类别:

数据操作:

c 复制代码
#define OP_Init            1   // 初始化
#define OP_Goto            2   // 无条件跳转
#define OP_Gosub           3   // 子程序调用
#define OP_Return          4   // 子程序返回
#define OP_Yield           5   // 协程让出
#define OP_HaltIfNull      6   // 空值则停止
#define OP_Halt            7   // 停止执行
#define OP_Integer         8   // 加载整数常量
#define OP_Int64           9   // 加载64位整数
#define OP_String          10  // 加载字符串
#define OP_Null            11  // 加载NULL
#define OP_Blob            12  // 加载BLOB

表/索引操作:

c 复制代码
#define OP_OpenRead        31  // 打开表/索引(只读)
#define OP_OpenWrite       32  // 打开表/索引(读写)
#define OP_OpenAutoindex   33  // 打开自动索引
#define OP_OpenEphemeral   34  // 打开临时表
#define OP_Close           35  // 关闭游标
#define OP_SeekGE          36  // 查找 >= 的记录
#define OP_SeekGT          37  // 查找 > 的记录
#define OP_SeekLE          38  // 查找 <= 的记录
#define OP_SeekLT          39  // 查找 < 的记录
#define OP_Seek            40  // 定位到指定位置
#define OP_NotFound        41  // 未找到则跳转
#define OP_Found           42  // 找到则跳转
#define OP_IsNull          43  // 空值检查
#define OP_NotNull         44  // 非空检查

数据读写:

c 复制代码
#define OP_Column          51  // 读取列值
#define OP_Affinity        52  // 应用类型亲和性
#define OP_MakeRecord      53  // 创建记录
#define OP_Count           54  // 计数
#define OP_Savepoint       55  // 保存点
#define OP_Insert          56  // 插入记录
#define OP_Delete          57  // 删除记录
#define OP_ResetCount      58  // 重置计数
#define OP_SorterCompare   59  // 排序比较
#define OP_SorterData      60  // 排序数据
#define OP_RowData         61  // 行数据
#define OP_Rowid           62  // 行ID
#define OP_NullRow         63  // 空行

聚合函数:

c 复制代码
#define OP_AggStep         71  // 聚合步骤
#define OP_AggFinal        72  // 聚合完成
#define OP_AggValue        73  // 聚合值

流程控制:

c 复制代码
#define OP_If              81  // 条件跳转
#define OP_IfNot           82  // 条件跳转(取反)
#define OP_IfNullRow       83  // 空行跳转
#define OP_Jump            84  // 多路跳转
#define OP_Once            85  // 仅执行一次

算术/逻辑:

c 复制代码
#define OP_Add             91  // 加法
#define OP_Subtract        92  // 减法
#define OP_Multiply        93  // 乘法
#define OP_Divide          94  // 除法
#define OP_Remainder       95  // 取余
#define OP_Concat          96  // 字符串连接
#define OP_BitAnd          97  // 位与
#define OP_BitOr           98  // 位或
#define OP_ShiftLeft       99  // 左移
#define OP_ShiftRight     100  // 右移
#define OP_AddImm         101  // 立即数加法
#define OP_MustBeInt      102  // 必须是整数
#define OP_RealAffinity   103  // 实数亲和性
2.3 内存单元系统

Mem 结构:

c 复制代码
typedef struct Mem Mem;
struct Mem {
  union MemValue {
    double r;              // 实数值
    i64 i;                 // 整数值
    int nZero;             // 零字节数量
    void *p;               // 指针值
    FuncDef *pDef;         // 函数定义
  } u;
  char *z;                 // 字符串/BLOB数据
  int n;                   // 字符串/BLOB长度
  u16 flags;               // 类型标志
  u8 enc;                  // 编码 (UTF-8/UTF-16)
  u8 eSubtype;             // 子类型
  Mem *pScopyFrom;         // 浅拷贝源
  void (*xDel)(void *);    // 析构函数
#ifdef SQLITE_DEBUG
  Mem *pFiller;            // Debug填充
#endif
};

类型标志:

c 复制代码
#define MEM_Null      0x0001   // NULL值
#define MEM_Str       0x0002   // 字符串
#define MEM_Int       0x0004   // 整数
#define MEM_Real      0x0008   // 浮点数
#define MEM_Blob      0x0010   // BLOB
#define MEM_AffMask   0x001f   // 类型掩码
#define MEM_RowSet    0x0020   // 行集合
#define MEM_Frame     0x0040   // 栈帧
#define MEM_Undefined 0x0080   // 未定义
#define MEM_Cleared   0x0100   // 已清除
#define MEM_TypeMask  0x81ff   // 类型掩码
#define MEM_Static    0x1000   // 静态字符串
#define MEM_Ephem     0x2000   // 临时字符串
#define MEM_Agg       0x4000   // 聚合上下文
#define MEM_Zero      0x8000   // 零终止
2.4 游标系统

VdbeCursor 结构:

c 复制代码
typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
  u8 eCurType;             // 游标类型
  i8 iDb;                  // 数据库索引
  u8 nullRow;              // 是否空行
  u8 deferredMoveto;       // 延迟移动
  u8 isTable;              // 是否表游标
  u8 seekOp;               // 最近的seek操作
  Bool isOrdered;          // 是否有序
  Bool isSorter;           // 是否排序器
  Bool multiPseudo;        // 是否多伪表
  Bool seekHit;            // Seek命中
  Bool isEphemeral;        // 是否临时表
  Bool useRandomRowid;     // 使用随机rowid
  Bool isOrdered;          // 是否有序
  Pgno pgnoRoot;           // B-tree根页号
  i16 nField;              // 列数量
  u16 nHdrParsed;          // 已解析的头部列数
  BtCursor *pBtx;          // B-tree游标
  // ... 更多字段
};

游标类型:

c 复制代码
#define CURTYPE_BTREE      0   // B-tree游标
#define CURTYPE_SORTER     1   // 排序器游标
#define CURTYPE_VTAB       2   // 虚拟表游标
#define CURTYPE_PSEUDO     3   // 伪表游标

3. B-树存储引擎

3.1 B-树架构

位置 : btree.c, btreeInt.h
行数: ~28,000 行

设计目标:

  • 高效的磁盘访问(最小化I/O)
  • 支持范围查询
  • 支持快速插入/删除
  • 保持数据有序

B+树特性:

复制代码
• 所有数据存储在叶子节点
• 内部节点仅存储键和指针
• 叶子节点通过链表连接
• 树高度通常 2-4 层(百万行数据)
• 页面大小默认 4KB(可配置)

节点结构:

复制代码
Internal Node (内部节点):
┌──────────────────────────────────┐
│ Header (页头)                     │
├──────────────────────────────────┤
│ Cell Pointer Array (单元指针数组) │
├──────────────────────────────────┤
│ Unallocated Space (未分配空间)    │
├──────────────────────────────────┤
│ Cell Content Area (单元内容区)    │
│   Cell 1: Key + Left Child Ptr   │
│   Cell 2: Key + Left Child Ptr   │
│   ...                            │
└──────────────────────────────────┘

Leaf Node (叶子节点):
┌──────────────────────────────────┐
│ Header (页头)                     │
├──────────────────────────────────┤
│ Cell Pointer Array (单元指针数组) │
├──────────────────────────────────┤
│ Unallocated Space (未分配空间)    │
├──────────────────────────────────┤
│ Cell Content Area (单元内容区)    │
│   Cell 1: Key + Data             │
│   Cell 2: Key + Data             │
│   ...                            │
└──────────────────────────────────┘
3.2 核心数据结构

Btree 结构:

c 复制代码
typedef struct Btree Btree;
struct Btree {
  sqlite3 *db;              // 数据库连接
  BtShared *pBt;            // 共享B-tree状态
  u8 inTrans;               // 事务状态
  u8 sharable;              // 是否可共享
  u8 locked;                // 是否锁定
  u8 hasIncrblobCur;        // 是否有增量BLOB游标
  int wantToLock;           // 期望锁定的表数量
  int nBackup;              // 备份计数
  Btree *pNext;             // 链表
  Btree *pPrev;             // 链表
  BtLock *pLock;            // 锁列表
};

BtCursor 结构:

c 复制代码
typedef struct BtCursor BtCursor;
struct BtCursor {
  Btree *pBtree;            // B-tree实例
  BtShared *pBt;            // 共享状态
  BtCursor *pNext;          // 游标链表
  Pgno *aOverflow;          // 溢出页数组
  CellInfo info;            // 当前单元信息
  i64 nKey;                 // 键值(INTKEY表)
  Pgno pgnoRoot;            // 根页号
  u8 curFlags;              // 游标标志
  u8 eState;                // 游标状态
  u8 hints;                 // 查询优化提示
  u8 curPagerFlags;         // Pager标志
  i16 iPage;                // 当前页索引
  MemPage *pPage;           // 当前页
  // ... 更多字段
};
3.3 表类型

INTKEY 表(普通表):

c 复制代码
// 使用64位整数作为键(rowid)
// 键隐式存储,不占用数据空间
// 数据直接存储在叶子节点
CREATE TABLE users (
  id INTEGER PRIMARY KEY,  -- INTKEY
  name TEXT,
  age INTEGER
);

BLOBKEY 表(索引):

c 复制代码
// 使用任意字节序列作为键
// 键显式存储
// 用于实现索引
CREATE INDEX idx_name ON users(name);
// 内部使用BLOBKEY B-tree
3.4 核心操作

打开B-tree:

c 复制代码
int sqlite3BtreeOpen(
  sqlite3_vfs *pVfs,        // VFS接口
  const char *zFilename,    // 数据库文件名
  sqlite3 *db,              // 数据库连接
  Btree **ppBtree,          // 输出B-tree实例
  int flags,                // 标志
  int vfsFlags              // VFS标志
);

创建表:

c 复制代码
int sqlite3BtreeCreateTable(
  Btree *p,                 // B-tree实例
  int *piTable,             // 输出根页号
  int flags                 // BTREE_INTKEY 或 BTREE_BLOBKEY
);

游标操作:

c 复制代码
// 创建游标
int sqlite3BtreeCursor(
  Btree *p,                 // B-tree实例
  int iTable,               // 表根页号
  int wrFlag,               // 读/写标志
  struct KeyInfo *pKeyInfo, // 键信息(索引)
  BtCursor *pCur            // 输出游标
);

// 定位到第一条记录
int sqlite3BtreeFirst(BtCursor *pCur, int *pRes);

// 移动到下一条记录
int sqlite3BtreeNext(BtCursor *pCur, int flags);

// 读取数据
const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt);

// 插入记录
int sqlite3BtreeInsert(
  BtCursor *pCur,           // 游标
  const BtreePayload *pX,   // 数据
  int flags,                // 标志
  int seekResult            // Seek结果
);

// 删除记录
int sqlite3BtreeDelete(BtCursor *pCur, u8 flags);

事务操作:

c 复制代码
// 开始事务
int sqlite3BtreeBeginTrans(Btree *p, int wrflag);

// 提交事务
int sqlite3BtreeCommit(Btree *p);

// 回滚事务
int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly);

4. 分页器与缓存系统

4.1 Pager 架构

位置 : pager.c, pager.h
行数: ~13,000 行

核心职责:

  1. 页面缓存管理: 内存中缓存数据库页面
  2. 事务控制: 支持ACID事务
  3. 崩溃恢复: 通过日志实现原子提交
  4. 锁管理: 多进程并发访问控制
  5. 写入优化: 批量写入、延迟刷新

Pager 结构:

c 复制代码
typedef struct Pager Pager;
struct Pager {
  sqlite3_vfs *pVfs;        // VFS接口
  u8 exclusiveMode;         // 独占模式
  u8 journalMode;           // 日志模式
  u8 useJournal;            // 是否使用日志
  u8 noSync;                // 禁用同步
  u8 fullSync;              // 完全同步
  u8 extraSync;             // 额外同步
  u8 ckptSyncFlags;         // 检查点同步标志
  u8 walSyncFlags;          // WAL同步标志
  u8 syncFlags;             // 同步标志
  u8 tempFile;              // 临时文件
  u8 noLock;                // 无锁模式
  u8 readOnly;              // 只读模式
  u8 memDb;                 // 内存数据库
  u8 eState;                // Pager状态
  u8 eLock;                 // 锁状态
  u8 changeCountDone;       // 变更计数完成
  u8 setMaster;             // 设置主日志
  u8 doNotSpill;            // 不溢出
  u8 subjInMemory;          // 子日志在内存
  u8 bUseFetch;             // 使用fetch
  u8 hasHeldSharedLock;     // 持有共享锁
  Pgno dbSize;              // 数据库大小(页数)
  Pgno dbOrigSize;          // 原始大小
  Pgno dbFileSize;          // 文件大小
  Pgno dbHintSize;          // 提示大小
  int errCode;              // 错误码
  int nRec;                 // 日志记录数
  u32 cksumInit;            // 校验和初始值
  u32 nSubRec;              // 子事务记录数
  Bitvec *pInJournal;       // 日志页位图
  sqlite3_file *fd;         // 数据库文件
  sqlite3_file *jfd;        // 日志文件
  sqlite3_file *sjfd;       // 子日志文件
  i64 journalOff;           // 日志偏移
  i64 journalHdr;           // 日志头偏移
  sqlite3_backup *pBackup;  // 备份实例
  PagerSavepoint *aSavepoint; // 保存点数组
  int nSavepoint;           // 保存点数量
  u32 iDataVersion;         // 数据版本
  char dbFileVers[16];      // 文件版本字符串
  // ... 更多字段
};
4.2 日志模式

DELETE 模式(默认):

复制代码
1. 开始事务 → 创建 .db-journal 文件
2. 写入原始页面到日志
3. 修改数据库页面
4. 提交事务 → 删除日志文件
5. 如果崩溃 → 从日志恢复原始页面

TRUNCATE 模式:

复制代码
• 提交时截断日志文件(不删除)
• 减少文件系统元数据操作
• 适合频繁事务的场景

PERSIST 模式:

复制代码
• 保留日志文件但清空内容
• 避免文件创建/删除开销
• 首次事务后性能更好

MEMORY 模式:

复制代码
• 日志存储在内存中
• 崩溃后无法恢复
• 临时数据库或性能优先场景

WAL 模式(Write-Ahead Logging):

复制代码
• 写入操作记录到 .db-wal 文件
• 读写并发(读不阻塞写)
• 更好的并发性能
• 需要定期检查点(checkpoint)
4.3 WAL 模式详解

WAL 文件结构:

复制代码
┌─────────────────────┐
│ WAL Header (32字节)  │
│ - Magic number      │
│ - File format       │
│ - Page size         │
│ - Checkpoint seq    │
│ - Salt values       │
│ - Checksum          │
├─────────────────────┤
│ Frame 1             │
│ - Page number (4B)  │
│ - DB size (4B)      │
│ - Salt (8B)         │
│ - Checksum (8B)     │
│ - Page data (4KB)   │
├─────────────────────┤
│ Frame 2             │
├─────────────────────┤
│ Frame 3             │
│ ...                 │
└─────────────────────┘

WAL 优势:

  • 读写并发: 读操作不阻塞写操作
  • 更快的事务: 顺序写入日志,减少随机I/O
  • 原子提交: 通过检查点实现
  • 更少的fsync: 批量刷新

WAL 缺点:

  • 额外文件: .db-wal 和 .db-shm(共享内存)
  • 网络文件系统: 不支持某些NFS
  • 单向传播: 不适合主从复制

检查点操作:

c 复制代码
// 自动检查点(WAL达到1000页时)
PRAGMA wal_autocheckpoint=1000;

// 手动检查点
PRAGMA wal_checkpoint;
PRAGMA wal_checkpoint(PASSIVE);  // 被动模式
PRAGMA wal_checkpoint(FULL);     // 完全模式
PRAGMA wal_checkpoint(RESTART);  // 重启模式
PRAGMA wal_checkpoint(TRUNCATE); // 截断模式
4.4 页面缓存

PCache 接口:

c 复制代码
typedef struct PCache PCache;
typedef struct PgHdr PgHdr;

// 页头结构
struct PgHdr {
  sqlite3_pcache_page *pPage; // 缓存页
  void *pData;                // 页数据
  void *pExtra;               // 额外数据
  PgHdr *pDirty;              // 脏页链表
  Pager *pPager;              // 所属Pager
  Pgno pgno;                  // 页号
  u16 flags;                  // 标志
  i16 nRef;                   // 引用计数
  PCache *pCache;             // 所属缓存
  PgHdr *pDirtyNext;          // 下一个脏页
  PgHdr *pDirtyPrev;          // 上一个脏页
};

缓存策略(pcache1.c):

c 复制代码
// LRU (Least Recently Used) 替换算法
// 配置缓存大小
PRAGMA cache_size = 2000;  // 2000页 = 8MB(4KB页)

// 查询缓存大小
PRAGMA cache_size;

缓存统计:

c 复制代码
// 查看缓存命中率
PRAGMA cache_stats;
// 输出: 命中数, 未命中数, 写入数, 脏页数

5. 查询优化器

5.1 查询规划器架构

位置 : where.c, whereInt.h, whereCode.c, whereexpr.c
行数: ~35,000 行

核心任务:

  1. 分析 WHERE 子句
  2. 选择最优索引
  3. 确定表连接顺序
  4. 生成循环嵌套代码
  5. 估算查询成本

WhereInfo 结构:

c 复制代码
typedef struct WhereInfo WhereInfo;
struct WhereInfo {
  Parse *pParse;            // 解析器上下文
  SrcList *pTabList;        // FROM子句表列表
  ExprList *pOrderBy;       // ORDER BY子句
  ExprList *pResultSet;     // 结果集
  Expr *pWhere;             // WHERE子句
  int aiCurOnePass[2];      // 单遍优化游标
  int iContinue;            // 继续地址
  int iBreak;               // 中断地址
  int savedNQueryLoop;      // 保存的查询循环计数
  u16 wctrlFlags;           // 控制标志
  u8 nLevel;                // 嵌套层数
  i8 nOBSat;                // ORDER BY满足数量
  u8 eOnePass;              // 单遍删除/更新
  u8 eDistinct;             // DISTINCT处理
  u8 bOrderedInnerLoop;     // 内层循环有序
  WhereLevel a[1];          // 每层信息(柔性数组)
};
5.2 索引选择

成本估算模型:

c 复制代码
// 成本 = 磁盘I/O次数 + CPU周期
Cost = (DiskIO * 1.0) + (CPUCycles * 0.001)

// 全表扫描成本
FullScanCost = TablePages * 1.0

// 索引扫描成本
IndexScanCost = IndexPages + (MatchedRows * 1.0)

索引类型:

单列索引:

sql 复制代码
CREATE INDEX idx_age ON users(age);

-- 可用于:
SELECT * FROM users WHERE age = 18;
SELECT * FROM users WHERE age > 18;
SELECT * FROM users WHERE age BETWEEN 18 AND 30;

多列索引:

sql 复制代码
CREATE INDEX idx_age_name ON users(age, name);

-- 可用于:
SELECT * FROM users WHERE age = 18 AND name = 'Alice';
SELECT * FROM users WHERE age = 18;  -- 使用索引前缀
-- 不可用于:
SELECT * FROM users WHERE name = 'Alice';  -- 跳过前导列

覆盖索引:

sql 复制代码
CREATE INDEX idx_age_name ON users(age, name);

-- 覆盖索引查询(不需要回表)
SELECT name FROM users WHERE age = 18;
-- 所有需要的列都在索引中

部分索引(Partial Index):

sql 复制代码
CREATE INDEX idx_active_users ON users(name) WHERE active = 1;
-- 仅索引活跃用户,减少索引大小
5.3 连接优化

嵌套循环连接(Nested Loop Join):

sql 复制代码
SELECT * FROM users u, orders o WHERE u.id = o.user_id;

生成代码逻辑:

python 复制代码
for row_u in users:           # 外层循环
    for row_o in orders:      # 内层循环
        if row_u.id == row_o.user_id:
            output(row_u, row_o)

连接顺序选择:

复制代码
• 成本模型: 选择最小化总行数的顺序
• 小表驱动大表原则
• 考虑索引可用性

示例:
  users: 1000行
  orders: 100000行
  users.id 上有索引

最优顺序: orders → users
• 外层: orders (100000行)
• 内层: users (通过索引查找, 每次O(log N))
• 总成本: 100000 * log(1000) ≈ 1,000,000

差的顺序: users → orders
• 外层: users (1000行)
• 内层: orders (全表扫描, 每次100000行)
• 总成本: 1000 * 100000 = 100,000,000

Bloom Filter 优化:

c 复制代码
// 对于大表连接,使用Bloom Filter预过滤
// 在内存中构建小表的Bloom Filter
// 扫描大表时先检查Bloom Filter,减少无效连接
5.4 查询重写

子查询扁平化:

sql 复制代码
-- 原始查询
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);

-- 重写为连接
SELECT DISTINCT users.* FROM users JOIN orders ON users.id = orders.user_id;

常量折叠:

sql 复制代码
-- 原始
SELECT * FROM users WHERE age > 10 + 8;

-- 优化为
SELECT * FROM users WHERE age > 18;

布尔代数简化:

sql 复制代码
-- 原始
SELECT * FROM users WHERE (age > 18 OR age > 21);

-- 优化为
SELECT * FROM users WHERE age > 18;

BETWEEN 展开:

sql 复制代码
-- 原始
SELECT * FROM users WHERE age BETWEEN 18 AND 30;

-- 展开为
SELECT * FROM users WHERE age >= 18 AND age <= 30;
-- 便于索引范围扫描
5.5 EXPLAIN QUERY PLAN

查看查询计划:

sql 复制代码
EXPLAIN QUERY PLAN SELECT * FROM users WHERE age > 18;

输出示例:

复制代码
QUERY PLAN
|--SEARCH TABLE users USING INDEX idx_age (age>?)

计划类型:

  • SCAN TABLE: 全表扫描
  • SEARCH TABLE USING INDEX: 使用索引
  • SEARCH TABLE USING INTEGER PRIMARY KEY: 使用rowid
  • USE TEMP B-TREE FOR ORDER BY: 临时B-tree排序
  • USE TEMP B-TREE FOR DISTINCT: 临时B-tree去重

SQL执行流程

完整执行流程

复制代码
1. 应用调用 sqlite3_prepare_v2()
    ↓
2. Tokenizer: SQL文本 → Token流
    ↓
3. Parser: Token流 → AST
    ↓
4. Resolver: 解析表名、列名、函数
    ↓
5. Query Planner: 生成执行计划
    ↓
6. Code Generator: AST → VDBE字节码
    ↓
7. sqlite3_step(): 执行字节码
    ↓
8. VDBE执行:
   - 打开B-tree游标
   - 执行查询/修改操作
   - 管理事务
    ↓
9. B-tree层: 读写数据页
    ↓
10. Pager层: 缓存管理、日志记录
    ↓
11. VFS层: 文件I/O
    ↓
12. sqlite3_column_*(): 获取结果
    ↓
13. sqlite3_finalize(): 释放资源

示例:SELECT 查询

SQL:

sql 复制代码
SELECT name, age FROM users WHERE age > 18 ORDER BY name;

执行步骤:

  1. Prepare 阶段:

    c 复制代码
    sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    • Tokenizer: 识别关键字、标识符
    • Parser: 构建 Select AST
    • Planner: 选择索引(如果有 idx_age)
    • Code Generator: 生成 VDBE 字节码
  2. 生成的 VDBE 字节码(简化):

    复制代码
    Init           0    20    0                0
    OpenRead       0    2     0    3            root=2; users表
    OpenEphemeral  1    2     0    keyinfo(1,BINARY)  临时表用于排序
    Rewind         0    12    0                遍历users表
    Column         0    2     1                r[1]=age
    Ge             18   11    1    (BINARY)    if r[1]>=18 goto 11
    Column         0    1     2                r[2]=name
    Column         0    2     3                r[3]=age
    MakeRecord     2    2     4                r[4]=record(r[2],r[3])
    IdxInsert      1    4     2    2           插入临时表
    Next           0    5     0
    Close          0    0     0
    Sort           1    17    0                排序临时表
    Column         1    0     5                r[5]=name
    Column         1    1     6                r[6]=age
    ResultRow      5    2     0                输出r[5],r[6]
    Next           1    13    0
    Close          1    0     0
    Halt           0    0     0
    Transaction    0    0     1    0
    Goto           0    1     0
  3. Step 阶段:

    c 复制代码
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        const char *name = sqlite3_column_text(stmt, 0);
        int age = sqlite3_column_int(stmt, 1);
        printf("%s, %d\n", name, age);
    }
  4. Finalize 阶段:

    c 复制代码
    sqlite3_finalize(stmt);

示例:INSERT 操作

SQL:

sql 复制代码
INSERT INTO users (name, age) VALUES ('Alice', 25);

执行步骤:

  1. 开始事务(隐式)
  2. 分配 rowid :
    • 查找最大 rowid
    • 递增生成新 rowid
  3. 构造记录 :
    • MakeRecord 指令创建行数据
  4. 插入 B-tree :
    • 定位插入位置
    • 插入叶子节点
    • 如果需要,分裂节点
  5. 更新索引 :
    • 为每个索引插入条目
  6. 写入日志 :
    • 记录修改的页面到 .db-journal
  7. 提交事务(隐式)

VDBE 字节码(简化):

复制代码
Init           0    15    0
Transaction    0    1     1    0           开始写事务
Integer        25   1     0                r[1]=25
String8        0    2     0    Alice       r[2]='Alice'
NewRowid       0    3     0                r[3]=new_rowid
MakeRecord     2    2     4                r[4]=record(r[2],r[1])
Insert         0    4     3                插入rowid=r[3], data=r[4]
Halt           0    0     0

构建系统

Android.bp 配置

主库配置 (dist/Android.bp):

go 复制代码
cc_library {
    name: "libsqlite",
    host_supported: true,
    vendor_available: true,
    product_available: true,
    vndk: {
        enabled: true,
    },

    srcs: ["sqlite3.c"],

    cflags: [
        "-DSQLITE_THREADSAFE=2",              // 序列化访问模式
        "-DSQLITE_TEMP_STORE=3",              // 临时表存储在内存
        "-DSQLITE_ENABLE_FTS3",               // 全文搜索v3
        "-DSQLITE_ENABLE_FTS3_BACKWARDS",     // FTS1/2兼容
        "-DSQLITE_ENABLE_FTS4",               // 全文搜索v4
        "-DSQLITE_ENABLE_ICU",                // Unicode支持
        "-DSQLITE_ENABLE_DBSTAT_VTAB",        // 数据库统计虚拟表
        "-DSQLITE_ENABLE_BATCH_ATOMIC_WRITE", // 批量原子写
        "-DSQLITE_DEFAULT_AUTOVACUUM=1",      // 自动清理
        "-DSQLITE_POWERSAFE_OVERWRITE=1",     // 电源安全覆写
        "-DSQLITE_SECURE_DELETE",             // 安全删除
        "-DSQLITE_DEFAULT_FILE_PERMISSIONS=0600", // 文件权限
        "-DUSE_PREAD64",                      // 64位pread
        "-DHAVE_MALLOC_H",
        "-DHAVE_MALLOC_USABLE_SIZE",
        "-DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD",
    ],

    shared_libs: [
        "libandroidicu",                      // ICU库
        "liblog",                             // Android日志
        "libdl",                              // 动态链接
    ],

    whole_static_libs: [
        "libsqlite3_android",                 // Android扩展
    ],

    export_include_dirs: ["."],

    target: {
        android: {
            cflags: [
                "-DSQLITE_OMIT_BUILTIN_TEST",
                "-DSQLITE_OMIT_COMPILEOPTION_DIAGS",
                "-DSQLITE_OMIT_LOAD_EXTENSION",
            ],
        },
    },
}

编译选项详解

线程安全模式:

c 复制代码
-DSQLITE_THREADSAFE=2  // Serialized模式
// 0: 无线程安全(单线程)
// 1: Multi-threaded(多个数据库连接可跨线程)
// 2: Serialized(单个连接可跨线程,自动加锁)

临时存储:

c 复制代码
-DSQLITE_TEMP_STORE=3  // 全部在内存
// 0: 总是使用文件
// 1: 默认文件,可配置内存
// 2: 默认内存,可配置文件
// 3: 总是使用内存

功能启用:

c 复制代码
-DSQLITE_ENABLE_FTS3          // 全文搜索v3
-DSQLITE_ENABLE_FTS4          // 全文搜索v4(推荐)
-DSQLITE_ENABLE_FTS5          // 全文搜索v5(最新)
-DSQLITE_ENABLE_ICU           // ICU Unicode支持
-DSQLITE_ENABLE_RTREE         // R-tree空间索引
-DSQLITE_ENABLE_JSON1         // JSON函数
-DSQLITE_ENABLE_DBSTAT_VTAB   // 数据库统计

性能优化:

c 复制代码
-DSQLITE_ENABLE_BATCH_ATOMIC_WRITE  // 批量原子写入
-DSQLITE_DEFAULT_AUTOVACUUM=1       // 自动vacuum
-DSQLITE_POWERSAFE_OVERWRITE=1      // 假设电源安全

安全选项:

c 复制代码
-DSQLITE_SECURE_DELETE                    // 覆写已删除数据
-DSQLITE_DEFAULT_FILE_PERMISSIONS=0600    // 仅所有者可读写
-DSQLITE_OMIT_LOAD_EXTENSION              // 禁用动态扩展加载

功能裁剪:

c 复制代码
-DSQLITE_OMIT_BUILTIN_TEST           // 移除内置测试
-DSQLITE_OMIT_COMPILEOPTION_DIAGS    // 移除编译选项诊断
-DSQLITE_OMIT_DEPRECATED             // 移除废弃API

编译产物

共享库:

  • libsqlite.so: 主SQLite库(~2-3 MB)
  • 位置: /system/lib64/libsqlite.so (64位)
  • 位置: /system/lib/libsqlite.so (32位)

静态库:

  • libsqlite_static_noicu.a: 无ICU版本(~1.5 MB)
  • 用于最小化环境(microdroid)

命令行工具:

  • sqlite3: 交互式SQL shell
  • 位置: /system/bin/sqlite3

Android集成

位置与层次

复制代码
Java Framework Layer:
  android.database.sqlite.*
    ↓ JNI
Native Layer:
  frameworks/base/core/jni/android_database_SQLite*.cpp
    ↓
SQLite Library:
  external/sqlite/dist/sqlite3.c
  external/sqlite/android/sqlite3_android.cpp
    ↓
Disk:
  /data/data/<package>/databases/*.db

Java API

SQLiteDatabase 类:

java 复制代码
// 打开/创建数据库
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
    "/data/data/com.example/databases/app.db",
    null
);

// 执行查询
Cursor cursor = db.rawQuery(
    "SELECT name, age FROM users WHERE age > ?",
    new String[]{"18"}
);

while (cursor.moveToNext()) {
    String name = cursor.getString(0);
    int age = cursor.getInt(1);
}
cursor.close();

// 插入数据
ContentValues values = new ContentValues();
values.put("name", "Alice");
values.put("age", 25);
long rowId = db.insert("users", null, values);

// 事务
db.beginTransaction();
try {
    db.execSQL("INSERT INTO users VALUES (...)");
    db.execSQL("UPDATE users SET ...");
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

db.close();

SQLiteOpenHelper 类:

java 复制代码
public class MyDatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "app.db";
    private static final int DATABASE_VERSION = 1;

    public MyDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE users (" +
            "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
            "name TEXT NOT NULL, " +
            "age INTEGER)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS users");
        onCreate(db);
    }
}

// 使用
MyDatabaseHelper helper = new MyDatabaseHelper(context);
SQLiteDatabase db = helper.getWritableDatabase();

Android 扩展功能

位置 : external/sqlite/android/

1. 电话号码比较函数

sqlite3_android.cpp 提供:

c 复制代码
// 函数: PHONE_NUMBERS_EQUAL(num1, num2)
// 宽松匹配(默认)
SELECT * FROM contacts WHERE PHONE_NUMBERS_EQUAL(phone, '1234567890');

// 函数: PHONE_NUMBERS_EQUAL(num1, num2, use_strict)
// 严格匹配
SELECT * FROM contacts
WHERE PHONE_NUMBERS_EQUAL(phone, '1234567890', 1);

// 函数: PHONE_NUMBERS_EQUAL(num1, num2, use_strict, min_match)
// 指定最小匹配位数
SELECT * FROM contacts
WHERE PHONE_NUMBERS_EQUAL(phone, '1234567890', 0, 7);

// 函数: _PHONE_NUMBER_STRIPPED_REVERSED(number)
// 电话号码标准化
SELECT _PHONE_NUMBER_STRIPPED_REVERSED('(123) 456-7890');
-- 返回: '0987654321'(去除格式,反转)

PhoneNumberUtils.cpp 实现:

cpp 复制代码
// 宽松匹配
bool phone_number_compare_loose(
    const char* num1,
    const char* num2
) {
    // 1. 去除非数字字符 (-, (), 空格等)
    // 2. 比较后N位数字(通常7位)
    // 3. 忽略国家/地区代码差异
}

// 严格匹配
bool phone_number_compare_strict(
    const char* num1,
    const char* num2
) {
    // 1. 完整号码比较
    // 2. 考虑国家代码
    // 3. 所有位都必须匹配
}

使用场景:

  • 联系人应用搜索
  • 来电号码匹配
  • 短信发送人识别
2. Unicode 排序规则

注册 Collator:

c 复制代码
int register_localized_collators(
    sqlite3* handle,
    const char* systemLocale,
    int utf16Storage
);

可用 Collator:

UNICODE:

sql 复制代码
-- 标准Unicode排序
CREATE INDEX idx_name ON users(name COLLATE UNICODE);
SELECT * FROM users ORDER BY name COLLATE UNICODE;

LOCALIZED:

sql 复制代码
-- 本地化排序(根据系统语言)
CREATE INDEX idx_name ON users(name COLLATE LOCALIZED);
SELECT * FROM users ORDER BY name COLLATE LOCALIZED;

-- 示例:中文拼音排序
-- 系统语言=zh_CN时,按拼音排序
-- 张三 < 李四 < 王五

实现(基于 ICU):

cpp 复制代码
// 使用 libicuuc 和 libicui18n
UCollator* collator = ucol_open(locale, &status);
ucol_setStrength(collator, UCOL_PRIMARY);  // 忽略大小写

int compareUTF8(
    const char* str1, int len1,
    const char* str2, int len2
) {
    return ucol_strcollUTF8(collator,
        str1, len1, str2, len2, &status);
}
3. 文件删除函数

_DELETE_FILE(path):

sql 复制代码
-- 安全删除文件
SELECT _DELETE_FILE('/data/data/com.example/cache/temp.txt');

安全限制:

cpp 复制代码
// 仅允许删除以下路径的文件:
// - /data/data/<package>/
// - /sdcard/Android/data/<package>/
//
// 拒绝删除系统文件或其他应用文件

JNI 集成

位置 : frameworks/base/core/jni/android_database_SQLite*.cpp

主要JNI函数:

cpp 复制代码
// 打开数据库
static jlong nativeOpen(JNIEnv* env, jclass clazz,
    jstring pathStr, jint openFlags, jstring labelStr,
    jboolean enableTrace, jboolean enableProfile,
    jint lookasideSlotSize, jint lookasideSlotCount)

// 准备语句
static jlong nativePrepareStatement(JNIEnv* env, jclass clazz,
    jlong connectionPtr, jstring sqlString)

// 执行语句
static void nativeExecute(JNIEnv* env, jclass clazz,
    jlong connectionPtr, jlong statementPtr)

// 执行查询
static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
    jlong connectionPtr, jlong statementPtr)

// 绑定参数
static void nativeBindNull(JNIEnv* env, jclass clazz,
    jlong connectionPtr, jlong statementPtr, jint index)

static void nativeBindLong(JNIEnv* env, jclass clazz,
    jlong connectionPtr, jlong statementPtr, jint index, jlong value)

static void nativeBindString(JNIEnv* env, jclass clazz,
    jlong connectionPtr, jlong statementPtr, jint index, jstring valueString)

系统数据库

联系人数据库:

复制代码
/data/data/com.android.providers.contacts/databases/contacts2.db

表结构:

  • contacts - 联系人
  • raw_contacts - 原始联系人数据
  • data - 联系人详细信息(电话、邮箱等)
  • groups - 联系人分组

浏览器数据库:

复制代码
/data/data/com.android.browser/databases/browser.db

表结构:

  • bookmarks - 书签
  • history - 浏览历史
  • searches - 搜索历史

通话记录:

复制代码
/data/data/com.android.providers.contacts/databases/calllog.db

表结构:

  • calls - 通话记录

设置数据库:

复制代码
/data/system/users/0/settings.db

表结构:

  • system - 系统设置
  • secure - 安全设置
  • global - 全局设置

性能优化

1. 索引优化

创建索引:

sql 复制代码
-- 单列索引
CREATE INDEX idx_age ON users(age);

-- 多列索引
CREATE INDEX idx_age_name ON users(age, name);

-- 部分索引(节省空间)
CREATE INDEX idx_active_users ON users(name) WHERE active = 1;

-- 唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);

-- 表达式索引
CREATE INDEX idx_lower_name ON users(LOWER(name));

索引最佳实践:

sql 复制代码
-- ✅ 好的索引使用
SELECT * FROM users WHERE age > 18;  -- 使用 idx_age
SELECT * FROM users WHERE age = 18 AND name = 'Alice';  -- 使用 idx_age_name

-- ❌ 无法使用索引
SELECT * FROM users WHERE LOWER(name) = 'alice';  -- 函数包裹列
SELECT * FROM users WHERE age + 1 > 18;  -- 表达式包裹列

-- ✅ 修正:使用表达式索引或重写查询
CREATE INDEX idx_lower_name ON users(LOWER(name));
SELECT * FROM users WHERE LOWER(name) = 'alice';
-- 或
SELECT * FROM users WHERE age > 17;  -- 重写表达式

查看索引使用情况:

sql 复制代码
-- 查看索引列表
PRAGMA index_list('users');

-- 查看索引信息
PRAGMA index_info('idx_age');

-- 查看索引统计
ANALYZE;
SELECT * FROM sqlite_stat1 WHERE tbl = 'users';

2. 查询优化

使用 EXPLAIN QUERY PLAN:

sql 复制代码
EXPLAIN QUERY PLAN SELECT * FROM users WHERE age > 18;

**避免 SELECT ***:

sql 复制代码
-- ❌ 低效
SELECT * FROM users WHERE age > 18;

-- ✅ 高效(仅选择需要的列)
SELECT id, name FROM users WHERE age > 18;

使用 LIMIT:

sql 复制代码
-- ❌ 低效(返回所有结果)
SELECT * FROM users ORDER BY name;

-- ✅ 高效(仅返回前10条)
SELECT * FROM users ORDER BY name LIMIT 10;

批量操作:

sql 复制代码
-- ❌ 低效(多次事务)
FOR EACH row:
    INSERT INTO users VALUES (...);

-- ✅ 高效(单个事务)
BEGIN TRANSACTION;
FOR EACH row:
    INSERT INTO users VALUES (...);
COMMIT;

3. 事务优化

显式事务:

sql 复制代码
-- ❌ 默认每条语句一个事务
INSERT INTO users VALUES ('Alice', 25);
INSERT INTO users VALUES ('Bob', 30);
-- 2次磁盘同步

-- ✅ 批量事务
BEGIN TRANSACTION;
INSERT INTO users VALUES ('Alice', 25);
INSERT INTO users VALUES ('Bob', 30);
COMMIT;
-- 1次磁盘同步(快100倍)

性能对比:

复制代码
单条插入(自动提交):  ~100 插入/秒
批量事务(10000条):   ~50,000 插入/秒
使用WAL模式:           ~100,000 插入/秒

4. WAL 模式优化

启用 WAL:

sql 复制代码
PRAGMA journal_mode = WAL;

WAL 配置:

sql 复制代码
-- 自动检查点阈值(默认1000页)
PRAGMA wal_autocheckpoint = 1000;

-- 设置更大的阈值以提高写入性能
PRAGMA wal_autocheckpoint = 10000;

-- 禁用自动检查点(手动控制)
PRAGMA wal_autocheckpoint = 0;

手动检查点:

sql 复制代码
-- 被动检查点(不阻塞)
PRAGMA wal_checkpoint(PASSIVE);

-- 完全检查点(等待完成)
PRAGMA wal_checkpoint(FULL);

-- 重启检查点(重置WAL)
PRAGMA wal_checkpoint(RESTART);

5. 缓存优化

页面缓存大小:

sql 复制代码
-- 查看当前缓存大小
PRAGMA cache_size;

-- 设置缓存大小(单位:页,负数表示KB)
PRAGMA cache_size = 10000;   -- 10000页 = 40MB(4KB页)
PRAGMA cache_size = -10000;  -- 10MB

推荐配置:

sql 复制代码
-- 移动设备(内存受限)
PRAGMA cache_size = -2000;   -- 2MB

-- 桌面应用
PRAGMA cache_size = -10000;  -- 10MB

-- 服务器
PRAGMA cache_size = -100000; -- 100MB

6. 预编译语句

重用预编译语句:

java 复制代码
// ❌ 低效(每次都编译)
for (int i = 0; i < 1000; i++) {
    db.execSQL("INSERT INTO users VALUES (" + i + ", 'User" + i + "')");
}

// ✅ 高效(编译一次,重用多次)
SQLiteStatement stmt = db.compileStatement(
    "INSERT INTO users VALUES (?, ?)"
);
db.beginTransaction();
try {
    for (int i = 0; i < 1000; i++) {
        stmt.bindLong(1, i);
        stmt.bindString(2, "User" + i);
        stmt.executeInsert();
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
    stmt.close();
}

7. 数据类型优化

选择合适的数据类型:

sql 复制代码
-- ✅ 使用INTEGER存储日期(Unix时间戳)
CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    timestamp INTEGER  -- 4-8字节
);

-- ❌ 使用TEXT存储日期
CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    timestamp TEXT     -- 19字节 '2024-01-01 12:00:00'
);

类型亲和性:

sql 复制代码
-- SQLite类型亲和性(动态类型)
-- INTEGER: 整数
-- REAL: 浮点数
-- TEXT: 文本
-- BLOB: 二进制
-- NUMERIC: 数值(尝试转换为INTEGER或REAL)

8. 自动清理优化

自动 VACUUM:

sql 复制代码
-- 启用自动vacuum(建议在创建数据库时设置)
PRAGMA auto_vacuum = FULL;   -- 完全自动vacuum
PRAGMA auto_vacuum = INCREMENTAL;  -- 增量vacuum

-- 手动vacuum(回收空闲空间)
VACUUM;

-- 增量vacuum(释放N页)
PRAGMA incremental_vacuum(100);

9. 分析与统计

ANALYZE 命令:

sql 复制代码
-- 分析所有表
ANALYZE;

-- 分析特定表
ANALYZE users;

-- 查看统计信息
SELECT * FROM sqlite_stat1;
SELECT * FROM sqlite_stat4;  -- 如果启用

统计信息作用:

  • 帮助查询优化器选择最佳索引
  • 估算查询成本
  • 决定连接顺序

更新频率:

sql 复制代码
-- 数据变化超过10%时重新分析
-- 或定期(如每天)执行ANALYZE

10. 内存管理

Lookaside 内存:

sql 复制代码
-- 配置lookaside缓冲区(快速小对象分配)
PRAGMA lookaside = 128,250;  -- 128字节 × 250个槽位

软堆限制:

c 复制代码
// 设置软堆限制(限制内存使用)
sqlite3_soft_heap_limit64(100 * 1024 * 1024);  // 100MB

高级特性

1. 全文搜索 (FTS)

FTS4 使用

创建FTS表:

sql 复制代码
-- FTS4虚拟表
CREATE VIRTUAL TABLE documents_fts USING fts4(
    title,
    content,
    tokenize=porter  -- Porter词干算法
);

-- 插入数据
INSERT INTO documents_fts VALUES ('Hello World', 'This is a test document');

-- 全文搜索
SELECT * FROM documents_fts WHERE documents_fts MATCH 'test';

-- 短语搜索
SELECT * FROM documents_fts WHERE documents_fts MATCH '"hello world"';

-- 布尔查询
SELECT * FROM documents_fts WHERE documents_fts MATCH 'test AND document';
SELECT * FROM documents_fts WHERE documents_fts MATCH 'test OR hello';
SELECT * FROM documents_fts WHERE documents_fts MATCH 'test NOT hello';

-- 前缀搜索
SELECT * FROM documents_fts WHERE documents_fts MATCH 'hel*';

高级功能:

sql 复制代码
-- Snippet(摘要)
SELECT snippet(documents_fts, '[', ']', '...', -1, 20)
FROM documents_fts
WHERE documents_fts MATCH 'test';
-- 输出: "This is a [test] document"

-- Offsets(匹配位置)
SELECT offsets(documents_fts)
FROM documents_fts
WHERE documents_fts MATCH 'test';
-- 输出: "1 0 10 4"(列号 词号 字节偏移 长度)
FTS5 使用

创建FTS5表:

sql 复制代码
CREATE VIRTUAL TABLE documents_fts5 USING fts5(
    title,
    content,
    tokenize='porter ascii'
);

-- 排名
SELECT *, rank FROM documents_fts5
WHERE documents_fts5 MATCH 'test'
ORDER BY rank;

-- BM25排名算法
SELECT *, bm25(documents_fts5) AS score
FROM documents_fts5
WHERE documents_fts5 MATCH 'test'
ORDER BY score;

注意: Android 默认不启用 FTS5,需要自定义编译。

2. R-Tree 空间索引

创建R-Tree表:

sql 复制代码
CREATE VIRTUAL TABLE locations USING rtree(
    id,                    -- 整数主键
    min_lat, max_lat,      -- 纬度范围
    min_lon, max_lon       -- 经度范围
);

-- 插入位置数据
INSERT INTO locations VALUES (
    1,                     -- ID
    40.7127, 40.7129,     -- 纬度范围(纽约)
    -74.0060, -74.0058    -- 经度范围
);

-- 查询范围内的位置
SELECT id FROM locations
WHERE min_lat >= 40.0 AND max_lat <= 41.0
  AND min_lon >= -75.0 AND max_lon <= -73.0;

使用场景:

  • 地理位置查询(附近的POI)
  • 游戏中的碰撞检测
  • 图像区域搜索

3. JSON 支持

JSON1 扩展:

sql 复制代码
-- 创建带JSON列的表
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT,
    metadata JSON  -- 实际存储为TEXT
);

-- 插入JSON数据
INSERT INTO users VALUES (
    1,
    'Alice',
    '{"age": 25, "city": "NYC", "tags": ["developer", "reader"]}'
);

-- 提取JSON值
SELECT name, json_extract(metadata, '$.age') AS age
FROM users;

-- JSON路径查询
SELECT name FROM users
WHERE json_extract(metadata, '$.city') = 'NYC';

-- 数组操作
SELECT name, json_extract(metadata, '$.tags[0]') AS first_tag
FROM users;

-- 修改JSON
UPDATE users SET metadata = json_set(metadata, '$.age', 26)
WHERE id = 1;

-- 添加字段
UPDATE users SET metadata = json_insert(metadata, '$.country', 'USA')
WHERE id = 1;

-- 删除字段
UPDATE users SET metadata = json_remove(metadata, '$.tags')
WHERE id = 1;

JSON函数:

  • json_extract(json, path) - 提取值
  • json_set(json, path, value) - 设置值
  • json_insert(json, path, value) - 插入值
  • json_remove(json, path) - 删除值
  • json_type(json, path) - 获取类型
  • json_valid(json) - 验证JSON
  • json_array(...) - 创建数组
  • json_object(...) - 创建对象

注意: Android 需要 API 30+ 或自定义编译启用 JSON1。

4. 窗口函数

Window Functions(SQLite 3.25+):

sql 复制代码
-- ROW_NUMBER
SELECT name, age,
       ROW_NUMBER() OVER (ORDER BY age) AS row_num
FROM users;

-- RANK
SELECT name, score,
       RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

-- PARTITION BY
SELECT name, department, salary,
       AVG(salary) OVER (PARTITION BY department) AS dept_avg
FROM employees;

-- LEAD/LAG(获取上下行数据)
SELECT date, sales,
       LAG(sales, 1) OVER (ORDER BY date) AS prev_sales,
       LEAD(sales, 1) OVER (ORDER BY date) AS next_sales
FROM daily_sales;

5. Common Table Expressions (CTE)

WITH 子句:

sql 复制代码
-- 简单CTE
WITH active_users AS (
    SELECT * FROM users WHERE active = 1
)
SELECT * FROM active_users WHERE age > 18;

-- 递归CTE(组织结构树)
WITH RECURSIVE org_chart(id, name, manager_id, level) AS (
    SELECT id, name, manager_id, 0
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    SELECT e.id, e.name, e.manager_id, oc.level + 1
    FROM employees e
    JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT * FROM org_chart ORDER BY level, name;

6. 触发器

创建触发器:

sql 复制代码
-- AFTER INSERT触发器
CREATE TRIGGER users_audit_insert
AFTER INSERT ON users
BEGIN
    INSERT INTO audit_log VALUES (
        NEW.id,
        'INSERT',
        datetime('now')
    );
END;

-- BEFORE UPDATE触发器
CREATE TRIGGER users_updated_at
BEFORE UPDATE ON users
BEGIN
    UPDATE users SET updated_at = datetime('now')
    WHERE id = NEW.id;
END;

-- INSTEAD OF触发器(视图)
CREATE TRIGGER users_view_insert
INSTEAD OF INSERT ON users_view
BEGIN
    INSERT INTO users (name, age) VALUES (NEW.name, NEW.age);
    INSERT INTO user_profiles (user_id) VALUES (last_insert_rowid());
END;

7. 视图

创建视图:

sql 复制代码
-- 简单视图
CREATE VIEW active_users AS
SELECT id, name, age FROM users WHERE active = 1;

-- 使用视图
SELECT * FROM active_users WHERE age > 18;

-- 物化视图(手动维护)
CREATE TABLE active_users_cache AS
SELECT id, name, age FROM users WHERE active = 1;

-- 定期刷新
DELETE FROM active_users_cache;
INSERT INTO active_users_cache
SELECT id, name, age FROM users WHERE active = 1;

8. 备份与恢复

在线备份 API:

c 复制代码
// C API
sqlite3 *pSource = ...; // 源数据库
sqlite3 *pDest = ...;   // 目标数据库

sqlite3_backup *pBackup = sqlite3_backup_init(
    pDest, "main",
    pSource, "main"
);

if (pBackup) {
    sqlite3_backup_step(pBackup, -1);  // -1表示全部复制
    sqlite3_backup_finish(pBackup);
}

SQL 备份:

sql 复制代码
-- 导出到SQL文件
.output backup.sql
.dump

-- 恢复
.read backup.sql

-- VACUUM INTO(复制并压缩)
VACUUM INTO 'backup.db';

9. 附加数据库

ATTACH DATABASE:

sql 复制代码
-- 附加另一个数据库
ATTACH DATABASE 'other.db' AS other_db;

-- 跨数据库查询
SELECT u.name, o.total
FROM main.users u
JOIN other_db.orders o ON u.id = o.user_id;

-- 跨数据库插入
INSERT INTO other_db.backup_users SELECT * FROM main.users;

-- 分离数据库
DETACH DATABASE other_db;

使用场景:

  • 数据迁移
  • 跨库查询
  • 数据备份

10. 虚拟表

创建自定义虚拟表:

c 复制代码
// C API
typedef struct my_vtab my_vtab;
struct my_vtab {
    sqlite3_vtab base;
    // 自定义字段
};

static int myVtabCreate(
    sqlite3 *db,
    void *pAux,
    int argc, const char *const*argv,
    sqlite3_vtab **ppVTab,
    char **pzErr
) {
    // 创建虚拟表
}

static sqlite3_module myModule = {
    0,                    // iVersion
    myVtabCreate,         // xCreate
    myVtabConnect,        // xConnect
    myVtabBestIndex,      // xBestIndex
    myVtabDisconnect,     // xDisconnect
    myVtabDestroy,        // xDestroy
    myVtabOpen,           // xOpen
    myVtabClose,          // xClose
    myVtabFilter,         // xFilter
    myVtabNext,           // xNext
    myVtabEof,            // xEof
    myVtabColumn,         // xColumn
    myVtabRowid,          // xRowid
    // ...
};

// 注册虚拟表模块
sqlite3_create_module(db, "my_module", &myModule, NULL);

使用场景:

  • CSV文件访问
  • 系统信息查询(/proc、/sys)
  • REST API包装
  • 日志文件解析

测试基础设施

Android CTS 测试

TEST_MAPPING 配置:

json 复制代码
{
  "presubmit": [
    {
      "name": "CtsDatabaseTestCases"
    }
  ],
  "hwasan-postsubmit": [
    {
      "name": "CtsDatabaseTestCases"
    }
  ]
}

CTS 测试覆盖:

  • android.database.sqlite.* API测试
  • CRUD操作测试
  • 事务测试
  • 游标测试
  • 并发测试
  • 性能测试

单元测试

PhoneNumberUtilsTest.cpp:

cpp 复制代码
// 电话号码比较测试
TEST(PhoneNumberUtils, CompareLoose) {
    EXPECT_TRUE(phone_number_compare_loose(
        "1234567890",
        "(123) 456-7890"
    ));
}

TEST(PhoneNumberUtils, CompareStrict) {
    EXPECT_TRUE(phone_number_compare_strict(
        "+1-234-567-8900",
        "12345678900"
    ));
}

TEST(PhoneNumberUtils, MinMatch) {
    EXPECT_TRUE(phone_number_compare(
        "1234567890",
        "7890",
        false,  // use_strict
        4       // min_match
    ));
}

性能测试

基准测试:

sql 复制代码
-- 插入性能
.timer on
BEGIN TRANSACTION;
-- 插入10000行
COMMIT;

-- 查询性能
EXPLAIN QUERY PLAN SELECT * FROM users WHERE age > 18;
SELECT * FROM users WHERE age > 18;

-- 索引影响
DROP INDEX IF EXISTS idx_age;
SELECT * FROM users WHERE age > 18;  -- 全表扫描
CREATE INDEX idx_age ON users(age);
SELECT * FROM users WHERE age > 18;  -- 索引扫描

调试工具

sqlite3 CLI:

bash 复制代码
# 进入交互模式
sqlite3 app.db

# 执行命令
sqlite3 app.db "SELECT * FROM users"

# 导出数据
sqlite3 app.db .dump > backup.sql

# 导入数据
sqlite3 new.db < backup.sql

常用命令:

sql 复制代码
.tables              -- 列出所有表
.schema users        -- 查看表结构
.indices users       -- 查看表的索引
.mode column         -- 列模式显示
.headers on          -- 显示列名
.timer on            -- 显示执行时间
.stats on            -- 显示统计信息
.explain on          -- 显示VDBE字节码

adb 调试:

bash 复制代码
# 查看应用数据库
adb shell
cd /data/data/com.example.app/databases
sqlite3 app.db

# 复制数据库到本地
adb pull /data/data/com.example.app/databases/app.db .

# 查看数据库文件信息
adb shell ls -l /data/data/com.example.app/databases/

最佳实践

1. 数据库设计

表设计原则:

sql 复制代码
-- ✅ 使用INTEGER PRIMARY KEY(rowid别名)
CREATE TABLE users (
    id INTEGER PRIMARY KEY,  -- 自动递增,无需AUTOINCREMENT
    name TEXT NOT NULL,
    email TEXT UNIQUE
);

-- ❌ 避免AUTOINCREMENT(除非必要)
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,  -- 额外开销
    name TEXT
);

-- ✅ 使用NOT NULL约束
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,      -- 防止NULL值
    age INTEGER NOT NULL DEFAULT 0
);

-- ✅ 使用外键约束
PRAGMA foreign_keys = ON;
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL,
    total REAL NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

规范化:

sql 复制代码
-- ✅ 第三范式(3NF)
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE addresses (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL,
    street TEXT NOT NULL,
    city TEXT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

-- ❌ 反规范化(存储冗余)
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    street TEXT,
    city TEXT,
    -- 地址信息冗余存储
);

2. 索引策略

何时创建索引:

sql 复制代码
-- ✅ WHERE子句常用列
CREATE INDEX idx_age ON users(age);
SELECT * FROM users WHERE age > 18;

-- ✅ JOIN条件列
CREATE INDEX idx_user_id ON orders(user_id);
SELECT * FROM orders o JOIN users u ON o.user_id = u.id;

-- ✅ ORDER BY列
CREATE INDEX idx_name ON users(name);
SELECT * FROM users ORDER BY name;

-- ✅ GROUP BY列
CREATE INDEX idx_category ON products(category);
SELECT category, COUNT(*) FROM products GROUP BY category;

何时避免索引:

sql 复制代码
-- ❌ 小表(<1000行)
-- 全表扫描可能更快

-- ❌ 频繁写入的列
-- 索引维护开销 > 查询收益

-- ❌ 低选择性列(如性别)
-- 索引效果不明显

-- ❌ 从不查询的列
-- 浪费空间

3. 查询优化

避免全表扫描:

sql 复制代码
-- ❌ 函数包裹列
SELECT * FROM users WHERE LOWER(name) = 'alice';

-- ✅ 使用表达式索引或重写查询
CREATE INDEX idx_lower_name ON users(LOWER(name));
-- 或存储小写版本
ALTER TABLE users ADD COLUMN name_lower TEXT;
UPDATE users SET name_lower = LOWER(name);
CREATE INDEX idx_name_lower ON users(name_lower);

使用参数化查询:

java 复制代码
// ❌ SQL注入风险 + 性能差
String sql = "SELECT * FROM users WHERE name = '" + userName + "'";
db.rawQuery(sql, null);

// ✅ 安全 + 性能好(预编译)
String sql = "SELECT * FROM users WHERE name = ?";
db.rawQuery(sql, new String[]{userName});

4. 事务管理

显式事务:

java 复制代码
// ✅ 批量操作使用事务
db.beginTransaction();
try {
    for (User user : users) {
        db.insert("users", null, user.toContentValues());
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

// ❌ 每次插入自动提交
for (User user : users) {
    db.insert("users", null, user.toContentValues());
}

嵌套事务(Savepoint):

sql 复制代码
BEGIN TRANSACTION;
  INSERT INTO users VALUES (...);
  SAVEPOINT sp1;
    UPDATE users SET age = 30 WHERE id = 1;
    -- 如果失败
    ROLLBACK TO sp1;
  RELEASE sp1;
COMMIT;

5. 并发控制

WAL模式:

java 复制代码
// ✅ 启用WAL模式(读写并发)
db.enableWriteAheadLogging();

// 或通过PRAGMA
db.execSQL("PRAGMA journal_mode = WAL");

连接池:

java 复制代码
// Android SQLiteDatabase内部使用连接池
// 默认配置:
// - 最大连接数 = CPU核心数 + 1
// - 主线程使用主连接
// - 其他线程共享连接池

6. 内存管理

关闭资源:

java 复制代码
// ✅ 总是关闭Cursor
Cursor cursor = db.rawQuery("SELECT * FROM users", null);
try {
    while (cursor.moveToNext()) {
        // 处理数据
    }
} finally {
    cursor.close();  // 释放内存
}

// ✅ 使用try-with-resources(API 16+)
try (Cursor cursor = db.rawQuery("SELECT * FROM users", null)) {
    while (cursor.moveToNext()) {
        // 处理数据
    }
}

7. 升级策略

版本管理:

java 复制代码
public class MyDatabaseHelper extends SQLiteOpenHelper {
    private static final int VERSION_1 = 1;
    private static final int VERSION_2 = 2;
    private static final int VERSION_3 = 3;

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < VERSION_2) {
            // 升级到版本2
            db.execSQL("ALTER TABLE users ADD COLUMN email TEXT");
        }
        if (oldVersion < VERSION_3) {
            // 升级到版本3
            db.execSQL("CREATE INDEX idx_email ON users(email)");
        }
    }
}

迁移最佳实践:

java 复制代码
// ✅ 使用ALTER TABLE ADD COLUMN
db.execSQL("ALTER TABLE users ADD COLUMN phone TEXT");

// ✅ 新表创建
db.execSQL("CREATE TABLE user_profiles (...)");

// ❌ 避免DROP TABLE(数据丢失)
// 如必须重建:
db.execSQL("ALTER TABLE users RENAME TO users_old");
db.execSQL("CREATE TABLE users (...)");
db.execSQL("INSERT INTO users SELECT id, name, ... FROM users_old");
db.execSQL("DROP TABLE users_old");

8. 安全建议

防止SQL注入:

java 复制代码
// ❌ 危险
String sql = "SELECT * FROM users WHERE name = '" + input + "'";
// 如果input = "'; DROP TABLE users; --"
// 实际执行: SELECT * FROM users WHERE name = ''; DROP TABLE users; --'

// ✅ 使用参数化查询
db.rawQuery("SELECT * FROM users WHERE name = ?", new String[]{input});

权限控制:

java 复制代码
// ✅ 设置数据库文件权限
SQLiteDatabase.openOrCreateDatabase(
    dbPath,
    null,
    Context.MODE_PRIVATE  // 仅应用可访问
);

加密(需要第三方库,如SQLCipher):

java 复制代码
// 使用SQLCipher加密数据库
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
    dbPath,
    password,  // 加密密码
    null
);

9. 性能监控

查询分析:

sql 复制代码
-- 启用查询日志
PRAGMA vdbe_trace = ON;

-- 查看查询计划
EXPLAIN QUERY PLAN SELECT * FROM users WHERE age > 18;

-- 查看执行统计
.stats on
SELECT * FROM users WHERE age > 18;

Android Profiler:

java 复制代码
// 使用Android Studio Profiler监控:
// - 数据库查询时间
// - 内存使用
// - I/O操作

10. 错误处理

异常捕获:

java 复制代码
try {
    db.beginTransaction();
    // 数据库操作
    db.setTransactionSuccessful();
} catch (SQLiteException e) {
    Log.e(TAG, "Database error", e);
    // 错误处理
} finally {
    db.endTransaction();
}

约束冲突处理:

sql 复制代码
-- 插入或忽略
INSERT OR IGNORE INTO users (id, name) VALUES (1, 'Alice');

-- 插入或替换
INSERT OR REPLACE INTO users (id, name) VALUES (1, 'Alice');

-- 插入或回滚
INSERT OR ROLLBACK INTO users (id, name) VALUES (1, 'Alice');

总结

SQLite 核心价值

  1. 零配置: 无需安装、配置、管理服务器
  2. 自包含: 单文件数据库,易于部署和备份
  3. 跨平台: 在所有主流平台上运行
  4. 可靠性: 经过数十亿次部署验证
  5. 高性能: 针对嵌入式环境优化
  6. 完整SQL: 支持SQL-92及扩展
  7. ACID: 完整的事务支持
  8. 小巧: 编译后仅几MB

技术亮点

架构设计:

  • ✅ 分层清晰:SQL编译器 → VDBE → B-tree → Pager → VFS
  • ✅ 模块化:每层职责明确,易于维护
  • ✅ 可扩展:虚拟表、自定义函数、Collator

性能优化:

  • ✅ 基于成本的查询优化器
  • ✅ B+树索引(O(log N)查找)
  • ✅ 页面缓存(LRU替换)
  • ✅ WAL模式(读写并发)
  • ✅ 批量原子写入

Android集成:

  • ✅ 电话号码比较函数
  • ✅ Unicode排序规则(ICU)
  • ✅ FD Sanitizer集成
  • ✅ Java Framework包装
  • ✅ 连接池管理

高级特性:

  • ✅ 全文搜索(FTS3/4/5)
  • ✅ 空间索引(R-Tree)
  • ✅ JSON支持(JSON1)
  • ✅ 窗口函数
  • ✅ CTE(递归查询)
  • ✅ 触发器和视图

使用建议

适用场景:

  • ✅ 移动应用数据存储
  • ✅ 嵌入式设备
  • ✅ 桌面应用
  • ✅ 原型开发
  • ✅ 测试和CI/CD
  • ✅ 缓存存储
  • ✅ 配置管理

不适用场景:

  • ❌ 高并发写入(每秒数千次写入)
  • ❌ 客户端-服务器架构(使用PostgreSQL/MySQL)
  • ❌ 大数据分析(使用专用OLAP数据库)
  • ❌ 需要复杂权限管理

性能对比

操作 DELETE模式 WAL模式 相对速度
单条插入 ~150/秒 ~150/秒 1x
批量插入(事务) ~50,000/秒 ~100,000/秒 2x
简单查询 ~100,000/秒 ~100,000/秒 1x
索引查询 ~500,000/秒 ~500,000/秒 1x
读写并发 不支持 支持

最终建议

数据库设计:

  1. 使用规范化设计(3NF)
  2. 合理使用NOT NULL约束
  3. 启用外键约束(PRAGMA foreign_keys = ON)
  4. 为常用查询列创建索引

性能优化:

  1. 启用WAL模式(读写并发)
  2. 批量操作使用显式事务
  3. 增大页面缓存(PRAGMA cache_size)
  4. 定期执行ANALYZE更新统计
  5. 使用预编译语句

安全性:

  1. 使用参数化查询(防SQL注入)
  2. 设置文件权限(MODE_PRIVATE)
  3. 敏感数据考虑加密(SQLCipher)
  4. 定期备份数据库

可维护性:

  1. 使用SQLiteOpenHelper管理版本
  2. 妥善处理数据库升级
  3. 总是关闭Cursor和Statement
  4. 记录数据库错误日志

参考资源

官方文档

Android文档

源码

工具

学习资源


"SQLite不是为了与Oracle竞争,而是为了取代fopen()"

相关推荐
无限码力6 小时前
华为OD技术面真题 - 数据库MySQL - 3
数据库·mysql·华为od·八股文·华为od技术面八股文
2501_944525546 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 预算详情页面
android·开发语言·前端·javascript·flutter·ecmascript
heartbeat..6 小时前
Redis 中的锁:核心实现、类型与最佳实践
java·数据库·redis·缓存·并发
Prince-Peng6 小时前
技术架构系列 - 详解Redis
数据结构·数据库·redis·分布式·缓存·中间件·架构
虾说羊6 小时前
redis中的哨兵机制
数据库·redis·缓存
_F_y6 小时前
MySQL视图
数据库·mysql
2301_790300966 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
九章-6 小时前
一库平替,融合致胜:国产数据库的“统型”范式革命
数据库·融合数据库
清蒸鳜鱼7 小时前
【Mobile Agent——Droidrun】MacOS+Android配置、使用指南
android·macos·mobileagent
2401_838472517 小时前
使用Scikit-learn构建你的第一个机器学习模型
jvm·数据库·python