在 SQLite 中使用 UUID 扩展
为什么使用UUID
UUID (Universally Unique Identifier) 作为主键在数据库中有几个显著优势,特别是在 SQLite 中使用 BLOB 类型存储时:
UUID 的主要优势
- 全局唯一性:
- 几乎可以保证在不同时间、不同机器上生成的ID都是唯一的
- 避免了分布式系统中的ID冲突问题
- 安全性:
- 比自增整数更难猜测,减少信息泄露风险
- 适用于需要隐藏数据规模的场景
- 离线生成:
- 客户端可以在不连接数据库的情况下生成有效的ID
- 适合离线应用或同步场景
- 无中心化需求:
- 不需要中央服务器分配ID,适合分布式系统
在 SQLite 中使用 BLOB 存储 UUID 的优势
- 存储效率:
- BLOB(16字节)比文本表示(36字符)更紧凑
- 节省存储空间,特别是大量记录时
- 性能优势:
- 二进制比较比字符串比较更快
- 索引效率更高
- 一致性:
- 避免文本表示的大小写和格式问题
使用 Run-Time Loadable Extensions 使 SQLite3 支持 UUID
编译 .so 共享库
下载 uuid.c https://www.sqlite.org/src/file?name=ext/misc/uuid.c
编译成共享库(shared library file)并移动到/usr/local/lib(可选)
gcc -fPIC -shared uuid.c -o uuid.so sudo mkdir -p /usr/local/lib/sqlite3 sudo cp uuid.so /usr/local/lib/sqlite3/uuid.so
在 sqlite3
命令行中使用
(可选)修改 ~/.sqliterc 使在使用
sqlite3
命令行时自动加载扩展echo ".load /usr/local/lib/sqlite3/uuid" >> ~/.sqliterc
检查扩展功能
$ sqlite3 -- Loading resources from /home/duzhuo/.sqliterc SQLite version 3.45.1 2024-01-30 16:01:20 Enter ".help" for usage hints. Connected to a transient in-memory database. Use ".open FILENAME" to reopen on a persistent database. sqlite> .load /usr/local/lib/sqlite3/uuid sqlite> SELECT uuid(); 65a54293-036f-4aec-869b-2dcdcd523555 sqlite>
在 Python 标准库中使用 SQLite 加载扩展
注意:Python 默认禁用 SQLite 扩展加载(安全原因)。如需启用,必须重新编译 Python 并加上 –enable-loadable-sqlite-extensions 选项。
产生的报错信息 :
AttributeError: 'sqlite3.Connection' object has no attribute 'enable_load_extension'
AttributeError: 'sqlite3.Connection' object has no attribute 'load_extension'
这里以 pyenv 重新安装示例:
$ PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions" pyenv install 3.13
pyenv: /home/duzhuo/.pyenv/versions/3.13.3 already exists
continue with installation? (y/N) y
Installing Python-3.13.3...
Installed Python-3.13.3 to /home/duzhuo/.pyenv/versions/3.13.3
$ python3
Python 3.13.3 (main, Jun 28 2025, 14:46:43) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> con = sqlite3.connect(":memory:")
>>> con.enable_load_extension(True)
>>> con.load_extension("/usr/local/lib/sqlite3/uuid.so")
>>> con.close()
UUID 扩展的 Python 使用示例:
import sqlite3
from sqlite3 import Cursor, Connection
from typing import Dict, Optional
def init_db() -> Connection:
"""初始化数据库并加载 UUID 扩展"""
conn: Connection = sqlite3.connect(":memory:")
# 启用字典游标,使fetch不返回元组返回字典
conn.row_factory = sqlite3.Row
# 加载扩展(路径需根据实际情况调整)
conn.enable_load_extension(True)
conn.load_extension("/usr/local/lib/sqlite3/uuid") # 或 .dll
# 创建测试表
conn.execute(
"""
CREATE TABLE items (
id BLOB PRIMARY KEY, -- 存储为 16 字节 BLOB
name TEXT
)
"""
)
return conn
def demo_uuid_operations(conn: Connection) -> None:
"""演示纯 SQLite UUID 扩展操作"""
cursor: Cursor = conn.cursor()
# 插入数据(完全使用 SQLite 函数)
cursor.execute(
"""
INSERT INTO items (id, name)
VALUES
(uuid_blob(uuid()), 'Item1'), -- 生成随机 UUID 并转为 BLOB
(uuid_blob('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'), 'Item2') -- 从字符串转换
"""
)
# 查询并格式化 UUID
query_statement: str = """
SELECT
name,
uuid_str(id) AS uuid -- 将 BLOB 转为标准字符串
FROM items
"""
print("\n所有物品:")
for row in cursor.execute(query_statement).fetchall():
print(row['name'], row['uuid'])
# 按 UUID 查询
input_uuid: str = "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11" # 支持各种格式
cursor.execute(
"""
SELECT name FROM items
WHERE id = uuid_blob(?)
""",
(input_uuid,),
)
result: Optional[Dict[str, str]] = cursor.fetchone()
print(f"\n查询 UUID '{input_uuid}' 的结果:{result["name"] if result else '未找到'}")
if __name__ == "__main__":
db: Connection = init_db()
demo_uuid_operations(db)
db.close()