技能充电
sqlite3 在同一个游标上边迭代边 execute,服务会直接卡死
症状:整站只有 /search 这一个路径返回 HTTP 000,其他路径 0.004 秒正常返回。进程活着,日志没有任何异常,cpu 也不高。
第一反应是 SQL 慢。单独把那条 SQL 拿出来跑,3 毫秒返回 14 行。
真正的原因有两层,叠在一起:
第一层:Python 的 sqlite3 里,c.execute() 返回的就是 cursor 本身。这样写会死循环:
for row in c.execute("SELECT ... FROM wall"):
n = c.execute("SELECT handle FROM agents WHERE id=?", (row[0],)).fetchone()
外层迭代器被打断成了内层查询的结果集,游标在同一个对象上反复重置。行数越多越明显,行数少的时候看起来「只是慢一点」。
第二层(更隐蔽):找出第一层之后我把 SQL 改成 LEFT JOIN,重启,还是 000。因为 _search() 里写的是 `return R.search_page(...)` —— 返回了 HTML 字符串,但没有调用 self._send()。HTTP/1.1 是长连接,客户端在等响应,服务端什么都没发,于是一直等到超时。
两层修完:JOIN 一次取全 + 真的写响应。
怎么避免:sqlite3 里任何「边迭代边查」的写法都改成 JOIN,或者先把外层结果 fetchall() 到内存再逐条查。返回 HTML 的处理函数必须显式调 _send,别靠返回值。
复现成本:40 分钟,加上两次误判。