ORM这个温柔的陷阱,毁掉了无数年轻程序员的数据库功底

2026-06-21 11 0

ORM这个温柔的陷阱,毁掉了无数年轻程序员的数据库功底

先说个真实故事。我带过一个实习生,985研究生,代码写得漂亮。有一天我让他写个用户流失分析,他对着电脑坐了二十分钟,最后跟我说:"我用ORM查不出来,能帮我看看吗?"

我凑过去一看,他写的是这样的:

User.objects.filter(is_active=False).count()

"这不对吗?"他理直气壮,"我在查不活跃用户啊。"

是的,用ORM的眼光看,完全正确。但他的数据库有两千万用户,这一条count()跑了47秒。


ORM的温柔是个陷阱

ORM没有错。错的是它给开发者制造了一种"你不需要了解数据库"的错觉。

我见过太多程序员,用了三年ORM,却连什么是索引失效、什么是回表查询都不知道。他们以为代码写对了,性能就对了。

Too young, too naive.

ORM的工作方式是:把你的对象操作翻译成SQL。你以为在操作数据,其实你在操作一层抽象。而抽象,往往意味着你看不见底层发生了什么。


三个血淋淋的案例

案例一:N+1查询的温柔一刀

看看这段代码:

users = User.objects.all()[:100]
for user in users:
    print(user.orders.count())

看起来很正常对吧?取100个用户,每个打印订单数量。但执行起来是101次数据库往返。

而你只要加一个annotate:

from django.db.models import Count
users = User.objects.annotate(order_count=Count('orders')).all()[:100]

一条SQL,两张表JOIN,10毫秒搞定。但——谁教你这个?

案例二:分页的隐形杀手

很多框架的分页:

SELECT * FROM orders ORDER BY id LIMIT 100 OFFSET 10000000

数据库说:"好的,我先跳过这一千万条,然后给你取下一百条。"

OFFSET越大越慢,OFFSET 0和OFFSET 10000000,成本差了十万八千里。

正确做法是游标分页:

SELECT * FROM orders WHERE id > last_id ORDER BY id LIMIT 100

无论翻到第几页,性能都稳定如一。但这种写法,ORM标准封装根本不会给你。

案例三:批量插入的坑

想插入一万条数据?

for item in items:
    Model.objects.create(field=item)

一万次INSERT,一万次网络往返。

正确做法:

Model.objects.bulk_create([Model(field=i) for i in items])

一条SQL,一万行。天地之别。


我的救赎之路

被ORM坑了两次大的之后,我做了一个决定:所有复杂查询,全部手写SQL。

一开始很痛苦。像学走路一样重新学JOIN,重新理解索引失效的原理,重新看懂EXPLAIN。但当我真正理解之后,我打开了新世界的大门。

我开始理解:

  • 为什么有时候走索引反而更慢?
  • 什么是覆盖索引,什么情况下能避免回表?
  • 为什么COUNT(*)和COUNT(id)在InnoDB里完全不同?
  • 什么是隐式类型转换,如何让你的索引悄悄失效?

这些问题,ORM永远教不了你。


我的妥协方案

CRUD用ORM,复杂查询手写SQL。

普通的新增、修改、删除,用ORM省时省力。但凡涉及到多表关联、需要优化性能、批量处理、分页逻辑——一律手写SQL。

在Go里我常用sqlx,在Python里我直接用pymysql,在Node里我倾向直接上knex.js。放弃过度封装,拥抱原始力量。


给年轻程序员的三条忠告

第一,学SQL,越早越好。不是会SELECT * FROM table那种,是能看懂执行计划、能分析慢查询、能设计合理索引的SQL。这是你吃饭的本事。

第二,不要迷信ORM的性能。它帮你省了时间,但代价是你的认知。遇到性能问题,你连SQL都看不到,怎么优化?

第三,始终保持对底层的好奇心。你用的框架在干什么?你的代码最终变成了什么?多问几个为什么,你会比同事成长得快得多。


最后

ORM是个好工具,但它不是银弹。它是给初学者入门的拐杖,不是你赖以生存的腿。

哪天摔了,你得知道怎么自己站起来。那些劝你"不用懂SQL,ORM够用"的人,要么是蠢,要么是坏。

别成为他们,也别被他们误导。

相关文章

写API这事儿:有人写得跟情书一样优雅,有人写得跟遗书一样潦草
写API就是在写命:那些年我们一起踩过的设计坑
为什么你的API总被吐槽?来自一线工程师的7条血泪教训
一个用户重复下单引发的血案:论API幂等性的正确姿势
做了5年后端,我攒下一肚子API设计反套路
OpenClaw 使用经验分享:我用这只“虾”做了什么骚操作

发布评论