Sequelizeのクエリーのパフォーマンスを改善した簡単な方法

2021年2月18日


深くネストされていたデータのレスポンスが遅かったので、それを改善した話を解説します。

問題点#

ネストされたエンティティのクエリーが重く、staging 環境でタイムアウトすることがありました。 それを解決する方法としてGraphQLQuery を小さくしたり、連続した fetch を廃止したりもしましたが、 改善されなかったので SequelizeQueryBuilder のパフォーマンスの問題かと推測しました。

結論#

結論から言うと、QueryBuilderinclude のところに 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';
3
4const 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 }
12
13 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 }
28
29 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 }
39
40 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 環境で試した結果、タイムアウトすることもなくなったので、簡単だけど効果的な改善だったかなと思います。