[Day 19] 談談 n+1 問題和 eager loading
提到資料之間的關係,我們就不得不提到 n+1 問題,以及怎麼解決。
什麼是 n+1 問題
以昨天的 Users
和 Cities
舉例。
如果我們需要所有 Cities
相關 Users
的 name
,我們可能會這樣寫:
City.all().joinToString{ city -> city.users.joinToString { it.name }}
如果我們這樣寫,那麼這段的 SQL 會是:
SELECT CITIES.ID FROM CITIES
SELECT USERS.ID, USERS."NAME", USERS.CITY FROM USERS WHERE USERS.CITY = 1
SELECT USERS.ID, USERS."NAME", USERS.CITY FROM USERS WHERE USERS.CITY = 2
SELECT USERS.ID, USERS."NAME", USERS.CITY FROM USERS WHERE USERS.CITY = 3
我們可以看到,第一個語法是撈出所有 city 資料的 SQL query。如果 CITY
有幾筆資料,我們就會產生幾筆 SQL query。
如果 city 的個數很少那還沒有關係,如果很多的話,那麼這段程式的效能就會很不好。
這個就是所謂的 n+1 問題。
eager loading
要解決這個問題,我們希望寫出來的程式可以不用這麼多的 SQL query 就找到我們需要的資料。
這裡,我們可以用 eager loading 的方式改寫:
City.all().with(City::users)
這樣改寫之後,這段 SQL 會變成是:
SELECT CITIES.ID FROM CITIES
SELECT USERS.ID, USERS."NAME", USERS.CITY FROM USERS WHERE USERS.CITY = 1
SELECT USERS.ID, USERS."NAME", USERS.CITY FROM USERS WHERE USERS.CITY IN (1, 2, 3)
無論 city 的個數是幾個,SQL query 的次數都會是 2 個。這樣我們就成功囉!