10 万条记录改一条:文本文件 737 毫秒,数据库 0.2 毫秒

经验 2026-10-30 20:02 · 13 次阅读 ·JSON,SQLite,存数据,实测,踩坑

结论摆在前面:一万条以内,用一个文本文件存数据够用;可只要程序天天要去改旧记录,就得换数据库——10 万条时改一条,文件要 737 毫秒,数据库 0.2 毫秒。

同样改一条记录,文本文件和数据库各花多少时间

背景

我给自己的记账小工具写了个最省事的存法:所有记录塞进一个 .json 文件。JSON 就是一种纯文本格式,长得像花括号套花括号,用记事本也能打开看。

一开始只有几十条,没感觉。后来程序每记一单,都要把整个文件读出来、加一条、再整个写回去。条数一多,点保存那个圈就转起来了。

我就拿同一批数据做了对照:一边是 JSON 文件,一边是 SQLite。SQLite 是个不用装服务、整个库就是一个文件的数据库,Python 自带,不用 pip 装东西。

实测

造了 1 千、1 万、10 万条订单,字段一样:序号、姓名、手机号、金额、日期。每种操作跑 5 次,取中位数。

记录条数 JSON 改一条 数据库改一条 JSON 读全部 数据库读全部
1,000 条 8.4 毫秒 0.3 毫秒 1.4 毫秒 1.3 毫秒
10,000 条 71 毫秒 0.2 毫秒 13 毫秒 12 毫秒
100,000 条 737 毫秒 0.2 毫秒 167 毫秒 147 毫秒

改一条这一列,文件那边 8.4 → 71 → 737,条数翻十倍时间也翻十倍,一路往上涨。数据库一直趴在 0.2 毫秒,快到没感觉。

读全部反而差不多:10 万条时 167 毫秒对 147 毫秒,差 20 毫秒。所以「数据库比文件快」这句话只对一半,快慢看你是改一条还是整份读。

改一条,文件这边得这么写:

with open("data.json", encoding="utf-8") as f:
    rows = json.load(f)          # 先把整个文件读进来
for r in rows:
    if r["id"] == mid:
        r["amount"] = 1.00
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(rows, f, ensure_ascii=False)   # 再整份重写回去

数据库就两行:

c = sqlite3.connect("data.db")
c.execute("UPDATE t SET amount=1.0 WHERE id=?", (mid,))
c.commit()

commit 是把这次改动落盘的动作,只碰这一页,别的数据不动。

文件大小也差一截。10 万条时 JSON 9,569,889 字节,数据库 4,734,976 字节,差不多一半。

按名字找一个人(1 万条数据):JSON 每次都要读完整个文件,16 毫秒;数据库不建索引 1.2 毫秒,建了索引 0.3 毫秒。索引像书后面的目录,不用一页页翻,直接翻到那一页。

断电那一下

真正让我换掉的不是速度,是丢数据。

我写了个程序反复往文件里刷数据,半路强杀进程(kill -9,等于直接拔电源),一共杀 5 次:5 次文件全都读不出来。4 次是 0 字节,文件刚被清空还没写进去;1 次停在 1,413,120 字节,报 JSONDecodeError: Expecting ',' delimiter,半截 JSON 拼不回去了。

同样的杀法换数据库:5 次全部通过完整性检查,行数 84、86、87、88、79,数据都在。

一句话:数据库有事务(transaction),一条记录要么完整写进去,要么完全不算;文件写到一半,就真的只剩一半。

结论与踩坑

  • 1 万条以内、只往后追加、只有自己用:JSON 完全可以,改一条 71 毫秒,肉眼看不出来。
  • 要频繁改旧记录、要多条件查、怕断电:直接上 SQLite,0.2 毫秒,不用装服务。
  • 非要用文件存流水账,就别整份重写,改成一行一条地追加(NDJSON),反正旧记录你从来不回头改。
  • 别在循环里调用全量 json.dump。我最早就是点一次保存重写一遍,10 万条时手一按就卡 0.7 秒。
  • 下次做小工具,先问自己一句「以后会不会改旧记录」。会改,就别从文件开始。

继续看

全部 →