Stories

Detail Return Return

ORM 越方便,數據庫越慢?頂會論文列舉九大反模式 - Stories Detail

編者注:原論文《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 應用開發中,不妨參考這些反模式,避免陷入常見的性能陷阱。


💡 更多資訊,請關注 Bytebase 公號:Bytebase

user avatar cshopping Avatar zyx178 Avatar motianlun_5d0766992e67a Avatar shuyixiaobututou Avatar 6fafa Avatar beishangdeniuroumian Avatar xiaoyi_ces Avatar mengxiang_592395ab95632 Avatar euphoria Avatar zhuyundataflux Avatar abai_681266b7f0de8 Avatar
Favorites 11 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.