N+1 到底有多慢?怎么发现它藏在哪一行、哪个模板里?数据库不在本机时会放大多少?这篇用真实的 PostgreSQL 和一个加了延迟的 TCP 代理,把这些量出来。
/bad → X-Query-Count = 101 /good → X-Query-Count = 2 /tpl-bad → X-Query-Count = 101 /tpl-good → X-Query-Count = 2 /books-bad → X-Query-Count = 101 (/tpl-bad 的 N+1 藏在模板里:视图函数里没有任何循环,是模板里 a.books|length 触发的)
上一篇:joinedload() 与 selectinload();下一篇:SQLAlchemy Identity Map。
['SELECT author.id, author.name FROM auth', 'f87.py:71 · where'] ['SELECT book.id, book.title, book.author_', 'f87.py:71 · where'] ['SELECT book.id, book.title, book.author_', 'f87.py:71 · where']
InvalidRequestError: 'Author.books' is not available due to lazy='raise'
单向延迟 /bad(1+N 条) /good(2 条) 倍数
0 ms 23.1 ms 3.0 ms 7.7x
1 ms 408.6 ms 16.3 ms 25.1x
5 ms 1481.8 ms 70.8 ms 20.9x延迟代理是本篇自己用 asyncio 写的,只是模拟「数据库不在本机」,不代表某个真实网络的数字。
/good 查询数 2,阈值 3 → 通过 /bad 查询数 101,阈值 3 → 失败(N+1)