はじめに
Desksetapp作成で開発したTBLを使って、N+1問題に取り組みました。
背景
Indexの整備をしたさい、使用したTBLを用いて、N+1問題に取り組みました。
たとえIndexを解決しても別の原因で処理時間がかかることがあるため、今回取り組んでいます。
(今回も開発用のデータを用いて確認しました。)
N+1問題とは、親TBLデータをN件取得したのち、子TBLデータを1件1件検索するため、処理時間がかかってしまう問題です。
今回使用したTBL
workspace_sets_dev
| Name | Type | option |
|---|---|---|
| id | int8 | |
| title | varchar | |
| budget | int8 | non-null |
| space | int8 | non-null |
| color | varchar | non-null |
| use_case | varchar | non-null |
N+1の実行時間
NPlusOne
import { supabase } from "../utils/supabase";
export async function testNPlusOne() {
console.time("N+1");
const { data: sets, error } = await supabase
.from("workspace_sets_dev")
.select("*");
if (error) {
console.log("set取得エラー", error);
console.timeEnd("N+1");
return;
}
console.log(`workspace_sets: ${sets.length}件`);
for (const set of sets) {
const start = performance.now();
const { data: items, error } = await supabase
.from("workspace_items_dev")
.select("*")
.eq("workspace_set_dev_id", set.id);
const end = performance.now();
if (error) {
console.log("item取得エラー", error);
continue;
}
console.log(`set_id=${set.id}, items=${items.length}件`);
console.log(
`set_id=${set.id} : ${(end - start).toFixed(2)} ms`
);
}
console.timeEnd("N+1");
}
Logをみると、1件1件TBL検索がかけられていることがわかります。
~
set_id=97 : 290.57 ms
set_id=98, items=3件
set_id=98 : 284.33 ms
set_id=99, items=3件
~
set_id=1000, items=3件
set_id=1000 : 324.23 ms
N+1: 4:18.760 (m:ss.mmm)
N+1にかかった時間は4分18秒でした。
すべての処理でN+1があってはいけないということではないですが、処理時間を縮めることも必要だと痛感しました。
仮に一括取得時間も確認しました。
N
const ids = sets.map((set) => set.id);
const { data: items } = await supabase
.from("workspace_items_dev")
.select("*")
.in("workspace_set_dev_id", ids);
一括取得: 2.055s
でした。
おわりに
次はConnection Poolです。
参考文献
