在 PostgreSQL 上用 8 个线程各加 200 次计数:读-改-写最终 335(丢失 1265 次,5 轮都丢约 1260 次),update(n=F("n") + 1) 精确得到 1600;并对比两种写法的 SQL。
实测 Q 对象:& | ~ 对应的 SQL、动态拼条件与空 Q 的陷阱(空 Q 在 | 里被忽略);多值关系上 filter(a, b) 与 filter(a).filter(b) 的语义差别;exclude 用 NOT EXISTS 子查询,与 filter 取反不同。
5 万行读入内存:模型对象 134 ms / 42 MiB,values() 29 ms / 17 MiB,values_list(flat=True) 18 ms / 5.1 MiB;only() 省内存但仍构造对象;values 系列拿不到模型上的方法。
20 万行上量 exists() / count() / len() / bool():exists 0.22 ms,count 2.48 ms,len 与 bool 各 615 ms 且占约 137 MiB;len(list(values_list)) 也要 59 ms;以及缓存对答案的影响。
在 PostgreSQL 上用 8 个线程并发 get_or_create:有唯一约束时每轮恰好 1 行、无异常;没有唯一约束时 20 轮全部出现重复行,之后 get_or_create 永久抛 MultipleObjectsReturned;update_or_create 同样依赖唯一约束。
实测 bulk_create / bulk_update:5000 条循环 save() 5000 次查询 1777 ms,bulk_create 1 次 66 ms;bulk 不触发信号;ignore_conflicts 不回填 pk;多表继承不支持;本机 SQLite 上 bulk_update 未必比逐个 save 快。
实测自定义 QuerySet + as_manager() 的链式调用、覆盖默认 Manager 做软删除;关系访问(book.author)与 JOIN 不走自定义 Manager,已软删除的对象仍能被取到;默认 Manager 是模型里第一个定义的。
写一个 list ↔ 逗号文本的自定义字段,记录 to_python / from_db_value / get_prep_value / value_to_string 在构造、保存、查询、values_list、full_clean、序列化时各自被调用的时刻;并验证 deconstruct 漏写参数会让 makemigrations 检测不到变化。
实测自定义 Lookup(__ne)与 Transform(__abs):生成的 SQL、叠加使用、在 values_list / order_by 里的使用;注册在 Field 基类上会让所有字段都有它;PostgreSQL EXPLAIN 显示 Lower(name) 函数索引对 annotate(Lower) 有效,对 __iexact(UPPER)无效。
拆开 Django ORM 生成 SQL 的过程:QuerySet 包着的 Query 是条件树而不是字符串;compiler 按数据库后端编译出不同方言(SQLite LIKE vs PostgreSQL UPPER(...) LIKE UPPER(...));构建 65 µs、编译 84 µs、整体 262 µs;看 SQL 的几种办法。
用 QuerySet.explain() 在 PostgreSQL 与 SQLite 上看执行计划:无索引 Seq Scan、加索引后 Bitmap Index Scan(估算成本 3774 → 536)、选择性差时仍全表扫描;analyze=True 给出实际耗时(0.175 ms),buffers / format 选项;explain 只能用在 QuerySet 上。
在 gunicorn + PostgreSQL 上观察 Django 的数据库连接生命周期:连接是惰性的;默认每个请求一个新的数据库后端进程(6 个请求 6 个 pid);请求结束连接被关闭;连接是线程局部的(4 个线程 = 4 个连接);CONN_MAX_AGE=None 时连接在请求结束后仍保持。