SQLite作为轻量级嵌入式数据库,凭借其零配置、无服务端的特性,成为移动端和小型项目的首选

数据表创建与管理

基础建表语句

1
2
3
4
5
6
7
8
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
age INTEGER CHECK(age >= 18),
email TEXT DEFAULT 'unknown@domain.com',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

关键要素解析:
• PRIMARY KEY AUTOINCREMENT 自增主键
• NOT NULL 非空约束
• UNIQUE 唯一性约束
• CHECK 数据验证规则
• DEFAULT 默认值设置

数据类型详解

类型 存储格式 典型用途
INTEGER 整型数字 ID、年龄、状态码
REAL 浮点数 价格、地理坐标
TEXT UTF字符串 名称、描述信息
BLOB 二进制数据 图片、加密信息
NUMERIC 特殊数值 日期/时间、布尔值

类型亲和性:SQLite采用动态类型系统,建议显式声明类型以获得更好的优化效果

内置指令

指令 参数说明 功能描述 使用示例
.tables [?PATTERN] 显示匹配表名 .tables 'user%'
.schema [?TABLE] 显示建表语句 .schema users
.headers on/off 显示列标题 .headers on
.mode csv/json/column等 设置输出格式 .mode json
.dump [?TABLE] 导出SQL格式数据 .dump orders
.import FILE TABLE 导入CSV数据到表 .import data.csv temp_table
.output [FILENAME] 重定向输出到文件 .output backup.sql
.read FILENAME 执行SQL脚本文件 .read init.sql
.quit/.exit 无 退出命令行 .exit
.databases 无 显示所有附加数据库 .databases
.indexes [?TABLE] 显示表的索引 .indexes users
.nullvalue STRING 设置NULL的显示占位符 .nullvalue NULL
.timer on/off 显示查询执行时间 .timer on
.stats on/off 显示性能统计信息 .stats on
.width NUM1 NUM2... 设置列显示宽度 .width 10 15 20

常用场景

场景 指令组合
可读方式输出 .head on
.mode column
导出CSV数据 .headers on
.mode csv
.output data.csv
SELECT * FROM table;
快速备份数据库 .output backup.sql
.dump
查看表结构详情 .schema 表名
.indexes 表名
性能调试 .timer on
.stats on
模糊查询表结构 .tables '%log%'
.schema '%log%'

常用功能汇总

分析表占据空间

1
2
3
4
5
6
7
8
SELECT
name AS 表名,
ROUND(SUM(CASE WHEN pageno = 0 THEN pgsize ELSE 0 END) / 1024.0 / 1024.0, 2) AS 数据_MB,
ROUND(SUM(CASE WHEN pageno > 0 THEN pgsize ELSE 0 END) / 1024.0 / 1024.0, 2) AS 索引_MB
FROM dbstat
WHERE name NOT LIKE 'sqlite_%'
GROUP BY name
ORDER BY SUM(pgsize) DESC;

输出结果类似如下

1
2
3
4
5
6
7
表名                 字节数  MB  
------------------- ------ ----
tomato_task_record 188416 0.18
credit_log 110592 0.11
tomato_event 77824 0.07
idx_owner_time 61440 0.06
note 28672 0.03

通常情况下, 将表格中的字节数相加会小于实际占据的文件大小. sqlite的文件分配机制不会主动回收已经分配的页, 只会在后续复用, 因此数据库文件只会增加不会减小, 使用如下的指令可以查看页分配情况

1
2
3
PRAGMA page_size;        -- 每页大小(一般 4096)
PRAGMA page_count; -- 总页数
PRAGMA freelist_count; -- 空闲页数量(空洞)

如果空闲页面数非常大, 占比超过50%, 可以执行VACUUM;指令回收空闲页面. 此操作当然会阻塞数据库读写, 因此如果对空间不是特别敏感, 可以不管这个问题, 让sqlite自行复用空闲页.

线程模型

SQLite 的并发模型常被误解为「单线程执行」,实际上其采用多线程访问+单写者锁的机制。我们通过底层架构拆解其真实工作方式:

核心锁机制

SQLite 采用文件锁实现并发控制,包含5种锁状态升级路径:

锁状态 共享读 保留锁 未决锁 排他锁 描述
UNLOCKED ✔️ ❌ ❌ ❌ 初始状态,无锁
SHARED ✔️ ❌ ❌ ❌ 允许其他读,禁止写
RESERVED ✔️ ✔️ ❌ ❌ 准备写入,阻止其他保留锁
PENDING ❌ ✔️ ✔️ ❌ 等待现有读完成
EXCLUSIVE ❌ ❌ ❌ ✔️ 独占写入状态

关键特性:
• 读并行:多个连接可同时处于 SHARED 状态
• 写串行:同一时刻仅允许一个写操作(EXCLUSIVE 锁)
• 写优先:RESERVED 锁阻止新读连接进入

线程安全模式

通过编译时选项控制线程模型:

模式 线程安全等级 适用场景
单线程模式 完全无锁, 各个线程不能共享模块 嵌入式设备(关闭线程安全)
多线程模式 线程可以共享模块,但不能共享连接 默认配置(SQLITE_THREADSAFE=1)
串行化模式 线程可以共享模块、连接和游标 高并发需求(SQLITE_THREADSAFE=2)
1
2
3
import sqlite3
# 打印线程模式
print("SQLite连接的线程模式:", sqlite3.threadsafety)

当前Python中默认的线程模型是串行化模式, 即完全线程安全模式. 可参考官方文档

WAL模式优化

Write-Ahead Logging (预写日志) 大幅提升并发性能:

  1. 原理差异:
    • 传统模式:直接修改数据库文件
    • WAL模式:先写日志文件(-wal),后批量合并

  2. 并发提升:

    1
    2
    传统模式:读↔写互斥,写↔写互斥
    WAL模式:读↔写可并行,写↔写仍互斥
  3. 启用方式:

    1
    2
    PRAGMA journal_mode = WAL;  -- 启用WAL
    PRAGMA synchronous = NORMAL;-- 平衡性能与安全

事务类型与并发控制

不同事务类型影响锁行为:

事务类型 锁获取时机 并发性能 使用场景
DEFERRED (默认) 首次访问数据时 高 只读事务
IMMEDIATE 开始事务即获取保留锁 中 明确需要写操作
EXCLUSIVE 开始事务即获取排他锁 低 需要独占数据库

事务示例:

1
2
3
BEGIN IMMEDIATE;  -- 提前声明写意图
UPDATE accounts SET balance = balance - 100;
COMMIT;

性能优化实践

  1. 缩短事务时间:
    • 避免在事务中执行耗时操作(如文件IO)
    • 批量写入使用单一事务

  2. 连接池配置:

    1
    2
    3
    4
    5
    # Python示例:限制最大连接数
    from sqlite3 import connect
    import threading

    connection_pool = threading.BoundedSemaphore(5) # 最大5连接
  3. 监控锁竞争:

    1
    SELECT * FROM sqlite_stat1 WHERE tbl = '锁状态表';

扩展: TCL语言

在编译Sqlite时, 文档提到了一个称之为SQLite TCL extension的东西. 其中TCL指的是一种脚本语言. 启用该扩展后, 允许通过TCL语言来操作Sqlite.

TCL语言的理念是一切皆命令, 风格与bash的指令非常类似.

脚本语言已经看的够多了, 有需要的时候再深入学习TCL吧.

最后更新: 2026年09月28日 13:38

版权声明:本文为原创文章,转载请注明出处

原始链接: https://lizec.top/2025/03/23/Sqlite%E7%AC%94%E8%AE%B0%E4%B9%8B%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/