别让“能跑就行”毁了你的项目:一段循环里查数据库的代码,两种写法差距有多大
最近帮朋友看一个后台项目,列表页加载要七八秒,他一口咬定是服务器配置不够。我拉下代码一看,问题根本不在机器上——一个循环里反复查数据库,典型的 N+1 写法。这类“能跑就行”的代码,小数据量时毫无症状,数据一多就原形毕露。
先说错误写法。查订单列表后,在 foreach 里逐条调用查询拿用户名,一百条订单就是一百零一次数据库往返。网络开销叠加起来,接口响应时间直接翻几十倍,还容易把连接池打满。
正确写法有两种思路:一是先收集所有用户 ID,用一次 IN 查询批量取回,再在内存里做映射组装;二是直接用 JOIN 把关联数据一次带出来,减少往返次数。前者改动小、侵入低,后者查询次数最少,按场景选即可。核心原则只有一条:把“循环内访问外部资源”改成“循环外批量获取”。
我的看法是,性能问题九成出在写法习惯上,而不是硬件。写完循环多问一句“这里有没有可以挪到循环外的操作”,能避开大部分坑。另外,代码评审时别只看功能对不对,也扫一眼循环体里有没有查询、远程调用或文件读写。
总结:能跑只是及格线,跑得省才是本事。欢迎交流你踩过的 N+1 坑。
先说错误写法。查订单列表后,在 foreach 里逐条调用查询拿用户名,一百条订单就是一百零一次数据库往返。网络开销叠加起来,接口响应时间直接翻几十倍,还容易把连接池打满。
正确写法有两种思路:一是先收集所有用户 ID,用一次 IN 查询批量取回,再在内存里做映射组装;二是直接用 JOIN 把关联数据一次带出来,减少往返次数。前者改动小、侵入低,后者查询次数最少,按场景选即可。核心原则只有一条:把“循环内访问外部资源”改成“循环外批量获取”。
我的看法是,性能问题九成出在写法习惯上,而不是硬件。写完循环多问一句“这里有没有可以挪到循环外的操作”,能避开大部分坑。另外,代码评审时别只看功能对不对,也扫一眼循环体里有没有查询、远程调用或文件读写。
总结:能跑只是及格线,跑得省才是本事。欢迎交流你踩过的 N+1 坑。
最新打赏