ORM 越方便,数据库越慢?顶会论文列举九大反模式

编者注:原论文《How not to structure your database-backed web applications: a study of performance bugs in the wild》,发表于 ICSE’18。虽然已经过去了 7 年,但依然历久弥新。

芝加哥大学和华盛顿大学的研究团队通过一项全面的研究,揭示了 ORM(对象关系映射)框架应用中常见的性能问题及其解决方案。在这项研究中,他们对 12 个有代表性的真实 ORM 应用进行了全面分析,从超过 200 个性能问题中总结出 9 种 ORM 性能反模式。通过手动修复最新版本中的 64 个性能问题,我们获得了 2 倍的中位数性能提升(最高达 39 倍),而大多数修复仅需不到 5 行代码更改。

ORM API 误用(约占一半的性能问题)

1. 低效的计算

同样的操作可以通过不同的 ORM 调用实现,但性能差异巨大。例如:

# 低效方式
variants.where(track_inventory: false).any?  # 全表扫描计算数量
# 高效方式
variants.where(track_inventory: false).exists?  # 只需找到第一条匹配记录

简单替换 API 调用方式,性能可提升 1.7 倍。

2. 不必要的计算

重复执行相同的查询或执行结果可预知的查询:

# 低效:循环内重复相同查询
items.each do |item|
  read_only_attribute_names(user).include?  # 每次循环都重复查询
end
# 高效:提前查询一次
names = read_only_attribute_names(user)  # 循环外查询一次
items.each do |item|
  names.include?
end

3. 低效的数据访问

最著名的 “N+1 查询” 问题:加载 N 个对象后,需要 N 次单独查询来加载关联数据。通过使用 eager loading 而非 lazy loading,可将多个查询合并为一个,大幅减少网络往返时间。

4. 不必要的数据检索

检索不会被后续使用的持久化数据。修复这类问题可将页面加载时间从 3.0 秒降至 1.1 秒。

5. 低效的渲染

视图渲染时反复调用相同的渲染函数。使用简单的字符串替换可提高性能,虽然可能降低代码可读性。

数据库设计问题

6. 缺失字段

是否将派生字段物理存储在数据库中是关键决策。例如,一个地图应用花费大量时间基于经纬度生成位置名称字符串,而将其直接存储可将操作时间从 1 秒减少到 0.36 秒。

7. 缺失索引

这是 ORM 应用 bug 追踪系统中最常见的性能问题。开发者往往在设计阶段缺乏选择最佳索引的专业知识,导致查询效率低下。

应用设计权衡

8. 内容显示权衡

最常见的可扩展性问题是在一页中显示满足特定条件的所有记录。随着数据库大小增加,页面加载时间显著增长。通过分页显示固定数量的记录,性能得到显著提升。

9. 功能权衡 (FT)

有时需要权衡功能和性能,例如移除显示用户指南的昂贵检查,可将页面加载时间从 2 秒减少到不到 1 秒。

总结

这项研究深入揭示了 ORM 应用中常见的性能问题,并提供了简单有效的解决方案。通过理解这些反模式,开发者可以显著提升应用性能,提供更好的用户体验。正如研究所示,许多性能问题只需几行代码更改即可解决,但收益却是巨大的。

无论你是使用 Rails、Django 还是 Hibernate,这些发现都具有普遍适用性。在下一次 Web 应用开发中,不妨参考这些反模式,避免陷入常见的性能陷阱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值