所以,就只是一行代码的事。
import sqlite3
接着,整个的世界都陷入了安静之中。无需去安装MySQL,不必去调试Postgres,不用去央求运维人员开放端口,不用跟Docker Compose闹脾气摔键盘。
它就在那儿,一个文件,几百K,陪你从深夜写到天亮。
到底什么是SQLite
很多人以为它是“玩具”。
是。它的确是。
是那种小孩攥在手里不放的、缺了轮子但跑得飞快的玩具车。
不是特斯拉。是小时候拿电池盒和小马达自己缠线圈做的那种。
你不怕它坏,因为你知道它怎么转的。
为什么现在的AI又开始“往回”走了
你知道吗。
最近我看了一个开源项目叫OpenClaw。
它干了一件特别老土的事——用SQLite给AI当长期记忆。
不是那种被称作向量数据库的东西,不是名为Redis的事物,不是云端存在的那个所谓“持久化层”。
只有一个.sqlite文件,它安安静静地放置在~/.openclaw/memory/当中。
像个老派的日记本。
他们说这叫“Local-First”。

我说这叫:不想把自己的脑子交给别人托管。
谁还在用这玩意儿
我翻旧账的时候发现,Firefox的书签用它。
iOS的短信用它。
import sqlite3
你手机里点外卖的那个App,点餐记录也在用它。
甚至欧洲核子中心,对,造大型强子对撞机的那帮人。
他们在2023年的粒子物理数据表,也用SQLite存。
你以为的“玩具”,是世界运行的底噪。
conn = sqlite3.connect('database.db')
那些代码里藏着的笨拙温柔
我最喜欢的一段代码,来自那个AI记忆项目。
cursor = conn.cursor()
try:
# 极速路径:用向量扩展直接算距离
db.execute(vec_query)
except:
# 安全路径:哦你装不了扩展是吧,
# 没关系,我替你用JavaScript硬算。
# 慢一点,但不会丢下你。
这哪是容错。
这是写代码的人在朝屏幕另一端点头:
cursor.execute('''CREATE TABLE students (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')
知道你不容易,我有plan B。
人们总问:它能撑多大?
官方文档说,140TB也没问题。
cursor.execute("INSERT INTO students (name, age) VALUES ('John', 20)")
但我不关心这个。
我更在意的是另一件事——
有人写了个叫absurder-sql的东西。
conn.commit()
存有SQLite于IndexedDB之中,将IndexedDB佯装成磁盘模样,使得浏览器里的WASM能够运行具备全功能的SQLite。
还要跨标签页争leader,还要导出来能在命令行打开。
荒不荒谬。极度的荒谬。
但荒谬得,像有人真的在乎数据是属于用户的。
cursor.execute("SELECT * FROM students")
rows = cursor.fetchall()
for row in rows:
print(row)
为什么我们写不动那些“正确”的代码了
越来越多人开始在桌面应用里重新拥抱它。
不是因为它最强。
cursor.execute("SELECT * FROM students WHERE age > 18")
是因为它不欺负人。
你不用为了存几个联系人就去学Kubernetes。
你不用为了做个博客就去配读写分离。
你就想写个脚本,跑起来,别出错,早点睡。
cursor.execute("UPDATE students SET age = 21 WHERE name = 'John'")
那些你可能真的搜过的问题
### SQLite到底能不能用在生产环境
能。
cursor.execute("DELETE FROM students WHERE name = 'John'")
一旦你并非属于那种,每秒能够达到几万并发的情况,数据量达到上PB的程度,没有主从复制就会觉得难以入眠的生产环境。
就多数人的那种“生产环境”而言,存在几百个用户,有一个成为管理员的人,还有一个把自己搞得累死累活的个体。
它够了。
cursor.execute("PRAGMA table_info(students)")
columns = cursor.fetchall()
for column in columns:
print(column[1])
### 为什么我的数据库老是 locked
你开了连接忘了关。
你写了事务忘了commit。
你把简单的事情搞复杂了。
name = 'Alice'
age = 22
cursor.execute("INSERT INTO students (name, age) VALUES (?, ?)", (name, age))
### 能不能加密
原生不行。
但有SQLCipher,能AES-256。
age = 20
cursor.execute("SELECT * FROM students WHERE age > ?", (age,))
钥匙别写在代码里,钥匙放在系统钥匙串里。
### 分页很慢怎么办
别用OFFSET 10000。
记住你上次读到的那个ID。
with conn:
conn.execute("UPDATE students SET age = 23 WHERE name = 'Alice'")
这是SQLite教你的:别一次性要太多,记住你到过哪儿。
它的墓碑
等一下,写到这里我觉得不对。
conn.rollback()
SQLite不需要被悼念。
它还活得好好的。
每年几十亿份部署。
手机、浏览器、汽车、电视机顶盒、飞机上的娱乐系统。
with open('script.sql', 'r') as file:
sql_script = file.read()
cursor.executescript(sql_script)
它死不了。
死的是我们那种“非要用最重型武器解决最轻型问题”的执念。
尾声
我桌上有个硬盘。
last_row_id = cursor.lastrowid
里面存着十年前的博客备份,格式是.sqlite。
我打开过,还在。
所有文章、评论、留言板里陌生人的“加油”。
一行都没丢。
with sqlite3.connect('database.db') as conn:
cursor = conn.cursor()
# 执行数据库操作
那时候我不会写docker-compose.yml。
我只会:
conn = sqlite3.connect('blog.db')
c = conn.cursor()
c.execute('SELECT * FROM posts')
它从来没说过“版本不兼容”。
sql_query = "INSERT INTO students (name) VALUES (?);"
data = [('Bob',), ('Carol',), ('Dave',)]
cursor.executemany(sql_query, data)
它从来没说过“依赖缺失”。
它从来没问过我:你配得上我吗。
你不觉得这才是我们最初想要的“软件”吗。
打开,能用。关上,带走。
不索求,不审判,不消失。
data = [('Bob', 21), ('Carol', 22), ('Dave', 23)]
with conn:
conn.executemany("INSERT INTO students (name, age) VALUES (?, ?)", data)

Comments NOTHING