ORM:甜蜜的陷阱,还是生产力杀手?

2026-03-11 16 0

ORM:甜蜜的陷阱,还是生产力杀手?

声明:本文不推荐任何 ORM,也不反对任何 ORM。本文只是想把那些你在入门教程里看不到的真相,撕开给你看。

一、我们先来玩个游戏

假设你现在要查一个订单详情,需要:

  1. 订单基本信息(订单号、金额、状态)
  2. 下单用户信息(名字、手机号、会员等级)
  3. 订单关联的商品列表(商品名称、数量、单价)
  4. 每个商品的库存情况
  5. 优惠券信息
  6. 物流信息

请问:用 ORM 怎么写?

order = Order.objects.select_related('user').prefetch_related(
    'items__product__inventory',
    'coupon',
    'logistics'
).get(id=order_id)

优雅,太优雅了。三行代码搞定一切。

但你知道这里发生了什么吗?

  • 1 次主查询
  • 1 次 user 表 join
  • N 次 items 查询(prefetch)
  • N 次 product 查询
  • N 次 inventory 查询
  • 1 次 coupon 查询
  • 1 次 logistics 查询

如果一个订单有 5 个商品,恭喜你,一次页面请求触发 13 次数据库查询

这就是我要说的第一件事:ORM 最大的谎言,就是让你以为查询是免费的。

二、你看文档里不是这么写的

你可能用过 Django ORM 的 select_related 和 prefetch_related,觉得可以解决 N+1 问题。

实际开发中,至少 80% 的 ORM 性能问题,都来自于开发者以为自己在写高效查询,其实只是在写「看起来像查询」的代码。

三、类型系统的消失

ORM 第二个坑:类型安全感。

Python 是动态类型,Java 是静态类型。但无论哪种语言,一旦进了 ORM 的世界,你的数据库类型就变成了薛定谔的猫。

原始 SQL 至少明确:INT 就是 INT,VARCHAR 就是 VARCHAR。ORM 给你的是一种虚假的抽象。

很多人说 ORM 可以换数据库。兄弟,你换过吗?

真正换过数据库的人都知道,90% 的代码得重写,不是因为 SQL 语法,而是因为 ORM 的那些「高级特性」在另一个数据库里根本不支持。

四、事务的温柔陷阱

with transaction.atomic():
    order = Order.objects.create(...)
    Inventory.objects.filter(product_id=id).update(stock=F('stock') - 1)

看起来原子性有了。但你知道 F() 表达式在并发情况下可能出问题吗?

高并发秒杀场景下,F() 更新库存不是原子的。

这就是丢失更新问题。

正确做法是用原生 SQL 加上条件判断:

cursor.execute(
    "UPDATE inventory SET stock = stock - %s WHERE id = %s AND stock >= %s",
    [quantity, product_id, quantity]
)

ORM 掩盖了并发问题的复杂性,让你在开发环境里岁月静好,然后在生产环境里定时爆炸。

五、我什么时候会用 ORM

说了这么多 ORM 的坏话,是不是该反转了?其实不是。我用 ORM,但有条件:

1. 简单的 CRUD 系统
后台管理系统、内部工具、数据展示页面——这种场景 ORM 就是爽。

2. 团队里都是 junior
ORM 至少能让你不写 SQL 注入漏洞。

3. 性能不敏感的 MVP
快速原型阶段,ORM 能让你少写 50% 代码。先跑起来再说。

但如果是:

  • 高并发交易系统
  • 复杂查询报表
  • 需要深度优化的核心业务

兄dei,回到 SQL 吧。

六、正确的使用姿势

如果你决定用 ORM,请记住这几点:

1. 永远知道最终生成的 SQL 是什么
Django: print(query.query)
SQLAlchemy: print(str(statement))

2. 复杂查询该写 raw 就写 raw
别为了「优雅」硬凑 ORM 方法,那点语法糖不够赔的。

3. 做好监控和慢查询日志
不是你的代码慢,是你的代码不知道自己在写什么。

4. 单元测试里加上 SQL 计数

with self.assertNumQueries(2):  # 必须是 2 条,多一条都不行
    ...

写在最后

ORM 就像方便面包装上的牛肉——图片仅供参考。

它确实简化了入门门槛,但同时也隐藏了太多复杂度。当你觉得 ORM 不够用的时候,通常不是 ORM 的问题,而是你面对的问题本身就很复杂。

解决复杂问题的第一步,是承认它复杂。

不要因为写起来爽,就以为它真的是简单的。

共勉。

本文可能引发 ORM 信仰者不适,但这就是真实的工程选择。peace。

相关文章

还在自己折腾部署?让小龙虾帮你搞定!OpenClaw代部署服务来了
API 设计的十大谎言——别被”最佳实践”带进沟里
你的API接口,简直是新一代的回调地狱
花39块让人帮你干活,还是自己熬夜敲命令?
你的SQL有多慢?反正我的能跑完马拉松
定时任务这种小事,也能让我写出生产事故?

发布评论