深くネストされていたデータのレスポンスが遅かったので、それを改善した話を解説します。
問題点#
ネストされたエンティティのクエリーが重く、staging
環境でタイムアウトすることがありました。
それを解決する方法としてGraphQL
の Query
を小さくしたり、連続した fetch
を廃止したりもしましたが、
改善されなかったので Sequelize
の QueryBuilder
のパフォーマンスの問題かと推測しました。
結論#
結論から言うと、QueryBuilder
の include
のところに separate: true
を追加しました。
このオプションは 多対多(many-to-many)
リレーションのエンティティに使用可能です。
js
1const asset = await Asset.findAll({2 order: [['id', 'ASC']],3 where: {4 id: req.params.id,5 },6 include: [7 {8 model: User,9 as: 'owner',10 },11 {12 model: AssetProperty,13 as: 'properties',14 include: [15 {16 model: AssetImages.scope('active'),17 as: 'images',18 separate: true, // <-- ここ19 },20 ],21 },22 {23 model: AssetChildren,24 as: 'children',25 separate: true, // <-- ここ26 },27 {28 model: AssetReputation.scope('active'),29 as: 'reputations',30 separate: true, // <-- ここ31 include: [32 {33 model: AssetReputationForm.scope('active'),34 as: 'forms',35 separate: true, // <-- ここ36 include: [37 {38 model: Authority,39 as: 'writableAuthorities',40 separate: true, // <-- ここ41 },42 ],43 },44 ],45 },46 ]47})
パフォーマンス改善#
Sequelize
のオプションに logging
を渡して、パフォーマンスの調査をしやすくします。
js
1import { Sequelize } from 'sequelize';2import colors from 'colors/safe';34const createConnection = (database, user, password, options) => {5 const loggingOptions = {6 benchmark: true,7 logging: (logStr, execTime, options) => {8 if (!options) {9 options = execTime;10 execTime = undefined;11 }1213 let col = null;14 switch (options.type) {15 case 'SELECT':16 col = colors.blue.bold;17 break;18 case 'UPDATE':19 col = colors.yellow.bold;20 break;21 case 'INSERT':22 col = colors.green.bold;23 break;24 default:25 col = colors.white.bold;26 break;27 }2829 if (execTime) {30 if (execTime >= 10) {31 col = colors.red.bold;32 console.log(colors.magenta.bold(`[${execTime} ms]`), col(logStr));33 } else {34 console.log(col(logStr));35 }36 }37 }38 }3940 return new Sequelize(database, user, password, { ...options, ...loggingOptions })41}
改善前#
terminal
1[107 ms] Executed (default): SELECT `Asset`.* ...
問題のクエリーのパフォーマンスは 107ms
でした。
ローカル環境での少ないデータ数で試しているので、より膨大なデータを扱うときはより時間がかかるでしょう。
改善後#
terminal
1[27 ms] Executed (default): SELECT `Asset`.* ...
ネストされたクエリーを分離することで、メインクエリーの実行時間が 80ms
減少しました。
separate: true
したところは、細かいクエリーに分解されるらしいです。
staging
環境で試した結果、タイムアウトすることもなくなったので、簡単だけど効果的な改善だったかなと思います。