V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ziXiong
V2EX  ›  Python

大家在使用 ORM 时,是怎么处理 N+1 问题的?

  •  2
     
  •   ziXiong ·
    ziXiong · 2016-08-15 12:40:19 +08:00 · 7432 次点击
    这是一个创建于 3058 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在大部分ORM(关系对象映射中)框架中,如果要获取N个Model的关联对象,一般要进行N+1次数据库的查询操作,而最佳的解决方案是一次查询就够了。这就是ORM的N+1问题。

    总结了一篇 django 中处理 N+1 问题的文章,欢迎大家来讨论。 http://www.z1xiong.me/blog/2016/08/15/django-n-plus-one-problem.html

    21 条回复    2016-08-16 01:20:33 +08:00
    ahcat
        1
    ahcat  
       2016-08-15 12:45:41 +08:00 via iPhone
    就这一句 select_related ,贴到正文就好了。
    georgema1982
        2
    georgema1982  
       2016-08-15 12:51:40 +08:00   ❤️ 2
    显然你对 django 的 orm 了解还不够深入,所以得出“只能解决单值关系的问题”这样的结论。多值可以通过 prefetch_related 来解决: https://docs.djangoproject.com/en/1.10/ref/models/querysets/#prefetch-related
    ruandao
        3
    ruandao  
       2016-08-15 13:37:31 +08:00
    不建议用 orm
    后面换工作你会吃亏的
    tanteng
        4
    tanteng  
       2016-08-15 13:46:15 +08:00
    Laravel 的 ORM 有预加载概念
    jy01264313
        5
    jy01264313  
       2016-08-15 13:57:30 +08:00
    @ruandao 为什么不建议使用 ORM 呢?
    NaVient
        6
    NaVient  
       2016-08-15 14:41:51 +08:00
    @jy01264313 他的意思应该是用好 SQL 语句,不管去哪都能用
    lynnworld
        7
    lynnworld  
       2016-08-15 14:54:13 +08:00
    eager load 和 lazy load
    hantsy
        8
    hantsy  
       2016-08-15 15:07:09 +08:00
    fetch join.
    ziXiong
        9
    ziXiong  
    OP
       2016-08-15 15:15:42 +08:00
    @georgema1982 多谢提醒,最近才开始关注 N+1 问题, prefetch_related 的文档我也看过,但是显然之前没有看懂。我在 shell 中打印 print(User.objects.prefetch_related('articles').all().query),发现只执行了一条查询所有 user 的 sql, 还没有预加载多值关系,就理解错了 prefetch_related 。现在发现是 QuerySet.query 的问题。 现在博客也已经修正了这个问题。
    ziXiong
        10
    ziXiong  
    OP
       2016-08-15 15:17:54 +08:00
    @jy01264313 习惯了 ORM, 会让自己写 SQL 的能力变差吧。 不过业务量比较大的项目中,不用 ORM 也是白白增添了很多工作量。 就看使用者怎么平衡吧。
    ziXiong
        11
    ziXiong  
    OP
       2016-08-15 15:18:54 +08:00
    @hantsy 能不能再解释得详细点?
    est
        12
    est  
       2016-08-15 15:21:52 +08:00   ❤️ 1
    「不用 ORM 」 == 「修车太复杂了我不用干脆走路吧」
    msg7086
        13
    msg7086  
       2016-08-15 15:23:39 +08:00 via Android
    @ruandao 不用 orm ,迁移数据库平台或者改逻辑的时候你会哭死…
    yzongyue
        14
    yzongyue  
       2016-08-15 15:27:13 +08:00 via Android   ❤️ 1
    上缓存,要的就是 N+1 ,少用 join 之类的
    ziXiong
        15
    ziXiong  
    OP
       2016-08-15 15:31:50 +08:00
    @yzongyue 恩,在大型项目中不鼓励外键和 join , 确实就没有 N+1 问题一说了。
    est
        16
    est  
       2016-08-15 17:16:09 +08:00
    @ziXiong 是的。大家都是 for 循环然后 select ... where id in ...
    jy01264313
        17
    jy01264313  
       2016-08-15 22:03:02 +08:00
    @ziXiong 其实我个人感觉主要的业务都用 ORM ,但是报表一类的肯定还是 SQL 。
    1. SQL 关键字都大写
    2. 不用 SELECT * 这样的语法
    3. 所有的表前面都加上库命,字段前面都加上表名,用 `` 来包裹
    我看见的 SQL ,上一点都做不到,我觉得还是用 ORM 规范一点
    chaegumi
        18
    chaegumi  
       2016-08-15 22:20:39 +08:00
    复杂的查询 ORM 能胜任?还是直接 sql 会顺手很多
    rainybowe
        19
    rainybowe  
       2016-08-15 22:40:30 +08:00
    batch_select( https://github.com/lilspikey/django-batch-select)同样能解决 n+1 问题,针对单值以及多值关系。
    fx
        20
    fx  
       2016-08-15 23:18:00 +08:00
    select in
    georgema1982
        21
    georgema1982  
       2016-08-16 01:20:33 +08:00
    @rainybowe 这个项目都 5 年之久没更新了,不建议使用。如果 django 自己的 prefetch_related 不能满足需求,我推荐另外一个项目: https://github.com/ionelmc/django-prefetch
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2749 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 51ms · UTC 09:01 · PVG 17:01 · LAX 01:01 · JFK 04:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.