mysql> EXPLAIN
-> SELECT
-> a.article_id AS articleId,
-> a.title AS title,
-> a.board_id AS boardId,
-> b.name AS boardName,
-> b.slug AS boardSlug,
-> a.article_category_id AS articleCategoryId,
-> ac.name AS articleCategoryName,
-> ac.slug AS articleCategorySlug,
-> a.writer_id AS writerId,
-> a.writer_nickname AS writerNickname,
-> COALESCE(acc.comment_count, 0) AS articleCommentCount,
-> COALESCE(alc.like_count, 0) AS articleLikeCount,
-> COALESCE(adc.dislike_count, 0) AS articleDislikeCount,
-> a.created_at AS createdAt
-> FROM articles a
-> JOIN boards b ON b.board_id = a.board_id
-> JOIN article_categories ac ON ac.article_category_Id = a.article_category_id
-> LEFT JOIN article_comment_counts acc ON acc.article_id = a.article_id
-> LEFT JOIN article_like_counts alc ON alc.article_id = a.article_id
-> LEFT JOIN article_dislike_counts adc ON adc.article_id = a.article_id
-> WHERE
-> a.board_id = 1 AND a.article_id < 12663454
-> ORDER BY a.article_id DESC
-> LIMIT 51;
+----+-------------+-------+------------+--------+---------------------------------+---------+---------+-------------------------------------------+----------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------------------------+---------+---------+-------------------------------------------+----------+----------+----------------------------------+
| 1 | SIMPLE | b | NULL | const | PRIMARY | PRIMARY | 8 | const | 1 | 100.00 | NULL |
| 1 | SIMPLE | a | NULL | range | PRIMARY,idx_board_id_article_id | PRIMARY | 8 | NULL | 11919586 | 0.01 | Using where; Backward index scan |
| 1 | SIMPLE | ac | NULL | eq_ref | PRIMARY | PRIMARY | 8 | board_db_production.a.article_category_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | acc | NULL | eq_ref | PRIMARY | PRIMARY | 8 | board_db_production.a.article_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | alc | NULL | eq_ref | PRIMARY | PRIMARY | 8 | board_db_production.a.article_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | adc | NULL | eq_ref | PRIMARY | PRIMARY | 8 | board_db_production.a.article_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------------------------+---------+---------+-------------------------------------------+----------+----------+----------------------------------+
6 rows in set, 1 warning (0.01 sec)
- 기존의 게시글 무한스크롤 조회 쿼리(lastArticleId 있는 버전)에 대한 실행계획을 확인해보면 게시글 필터링 과정에서 인덱스를
idx_board_id_article_id
를 활용하지 않고 Primary Key Index 에서 Where 절 조건에서 필터링을 해서 게시글을 가져오는 것을 확인할 수 있다.
- filtered 에서는 0.01 정도의 효율을 보이고 있다.
mysql> EXPLAIN ANALYZE
-> SELECT
-> a.article_id AS articleId,
-> a.title AS title,
-> a.board_id AS boardId,
-> b.name AS boardName,
-> b.slug AS boardSlug,
-> a.article_category_id AS articleCategoryId,
-> ac.name AS articleCategoryName,
-> ac.slug AS articleCategorySlug,
-> a.writer_id AS writerId,
-> a.writer_nickname AS writerNickname,
-> COALESCE(acc.comment_count, 0) AS articleCommentCount,
-> COALESCE(alc.like_count, 0) AS articleLikeCount,
-> COALESCE(adc.dislike_count, 0) AS articleDislikeCount,
-> a.created_at AS createdAt
-> FROM articles a
-> JOIN boards b ON b.board_id = a.board_id
-> JOIN article_categories ac ON ac.article_category_Id = a.article_category_id
-> LEFT JOIN article_comment_counts acc ON acc.article_id = a.article_id
-> LEFT JOIN article_like_counts alc ON alc.article_id = a.article_id
-> LEFT JOIN article_dislike_counts adc ON adc.article_id = a.article_id
-> WHERE
-> a.board_id = 1 AND a.article_id < 12663454
-> ORDER BY a.article_id DESC
-> LIMIT 51;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Limit: 51 row(s) (cost=2.41e+6 rows=51) (actual time=0.0557..0.538 rows=51 loops=1)
-> Nested loop left join (cost=2.41e+6 rows=731) (actual time=0.0551..0.533 rows=51 loops=1)
-> Nested loop left join (cost=2.41e+6 rows=731) (actual time=0.0472..0.403 rows=51 loops=1)
-> Nested loop left join (cost=2.41e+6 rows=731) (actual time=0.0395..0.285 rows=51 loops=1)
-> Nested loop inner join (cost=2.41e+6 rows=731) (actual time=0.0309..0.161 rows=51 loops=1)
-> Filter: ((a.board_id = 1) and (a.article_id < 12663454)) (cost=2.41e+6 rows=731) (actual time=0.0242..0.107 rows=51 loops=1)
-> Index range scan on a using PRIMARY over (article_id < 12663454) (reverse) (cost=2.41e+6 rows=11.9e+6) (actual time=0.0227..0.0941 rows=102 loops=1)
-> Single-row index lookup on ac using PRIMARY (article_category_id=a.article_category_id) (cost=0.667 rows=1) (actual time=809e-6..843e-6 rows=1 loops=51)
-> Single-row index lookup on acc using PRIMARY (article_id=a.article_id) (cost=0.931 rows=1) (actual time=0.00224..0.00226 rows=0.373 loops=51)
-> Single-row index lookup on alc using PRIMARY (article_id=a.article_id) (cost=0.937 rows=1) (actual time=0.00214..0.00215 rows=0.294 loops=51)
-> Single-row index lookup on adc using PRIMARY (article_id=a.article_id) (cost=0.951 rows=1) (actual time=0.00235..0.00237 rows=0.529 loops=51)
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
- 상세 계획을 확인해봐도 PRIMARY KEY 인덱스를 활용하여 필터링하는 것처럼 보인다. (필터링이 끝나기까지 0.107 ms 소요)
- 하지만, 사실 이것만으로도 성능은 잘 나오긴 하고 굳이 쿼리를 바꿀 필요는 없어보인다. 20ms 정도면 충분히 좋은 성능이다.
mysql> EXPLAIN
-> WITH cte(article_id) AS (
-> SELECT a.article_id
-> FROM articles a
-> WHERE a.board_id = 1 AND a.article_id < 12663454
-> ORDER BY a.article_id DESC
-> LIMIT 51
-> )
-> SELECT
-> a.article_id AS articleId,
-> a.title AS title,
-> a.board_id AS boardId,
-> b.name AS boardName,
-> b.slug AS boardSlug,
-> a.article_category_id AS articleCategoryId,
-> ac.name AS articleCategoryName,
-> ac.slug AS articleCategorySlug,
-> a.writer_id AS writerId,
-> a.writer_nickname AS writerNickname,
-> COALESCE(acc.comment_count, 0) AS articleCommentCount,
-> COALESCE(alc.like_count, 0) AS articleLikeCount,
-> COALESCE(adc.dislike_count, 0) AS articleDislikeCount,
-> a.created_at AS createdAt
-> FROM articles a
-> JOIN cte ON cte.article_id = a.article_id
-> JOIN boards b ON b.board_id = a.board_id
-> JOIN article_categories ac ON ac.article_category_Id = a.article_category_id
-> LEFT JOIN article_comment_counts acc ON acc.article_id = a.article_id
-> LEFT JOIN article_like_counts alc ON alc.article_id = a.article_id
-> LEFT JOIN article_dislike_counts adc ON adc.article_id = a.article_id;
+----+-------------+------------+------------+--------+---------------------------------+-------------------------+---------+-------------------------------------------+----------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------------------------+-------------------------+---------+-------------------------------------------+----------+----------+--------------------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 51 | 100.00 | NULL |
| 1 | PRIMARY | a | NULL | eq_ref | PRIMARY,idx_board_id_article_id | PRIMARY | 8 | cte.article_id | 1 | 100.00 | NULL |
| 1 | PRIMARY | acc | NULL | eq_ref | PRIMARY | PRIMARY | 8 | cte.article_id | 1 | 100.00 | NULL |
| 1 | PRIMARY | alc | NULL | eq_ref | PRIMARY | PRIMARY | 8 | cte.article_id | 1 | 100.00 | NULL |
| 1 | PRIMARY | adc | NULL | eq_ref | PRIMARY | PRIMARY | 8 | cte.article_id | 1 | 100.00 | NULL |
| 1 | PRIMARY | b | NULL | eq_ref | PRIMARY | PRIMARY | 8 | board_db_production.a.board_id | 1 | 100.00 | NULL |
| 1 | PRIMARY | ac | NULL | eq_ref | PRIMARY | PRIMARY | 8 | board_db_production.a.article_category_id | 1 | 100.00 | NULL |
| 2 | DERIVED | a | NULL | range | PRIMARY,idx_board_id_article_id | idx_board_id_article_id | 16 | NULL | 11658266 | 100.00 | Using where; Using index |
+----+-------------+------------+------------+--------+---------------------------------+-------------------------+---------+-------------------------------------------+----------+----------+--------------------------+
8 rows in set, 1 warning (0.01 sec)
- 이왕이면 다홍치마. idx_board_id_article_id 를 활용하도록 쿼리를 개선해보자.
- WITH 절을 통해, 커버링인덱스를 활용하여 대상 article_id 를 바로 50건 특정하도록 한다. 인덱스만을 통해 조회 대상 게시글을 바로 50건을 찾을 수 있는 장점을 가진다.
-
filtered
값이 100.00 인 것을 보아, 필터링 효율은 기본에 비해 향상된 것을 알 수 있다.
mysql> EXPLAIN ANALYZE
-> WITH cte(article_id) AS (
-> SELECT a.article_id
-> FROM articles a
-> WHERE a.board_id = 1 AND a.article_id < 12663454
-> ORDER BY a.article_id DESC
-> LIMIT 51
-> )
-> SELECT
-> a.article_id AS articleId,
-> a.title AS title,
-> a.board_id AS boardId,
-> b.name AS boardName,
-> b.slug AS boardSlug,
-> a.article_category_id AS articleCategoryId,
-> ac.name AS articleCategoryName,
-> ac.slug AS articleCategorySlug,
-> a.writer_id AS writerId,
-> a.writer_nickname AS writerNickname,
-> COALESCE(acc.comment_count, 0) AS articleCommentCount,
-> COALESCE(alc.like_count, 0) AS articleLikeCount,
-> COALESCE(adc.dislike_count, 0) AS articleDislikeCount,
-> a.created_at AS createdAt
-> FROM articles a
-> JOIN cte ON cte.article_id = a.article_id
-> JOIN boards b ON b.board_id = a.board_id
-> JOIN article_categories ac ON ac.article_category_Id = a.article_category_id
-> LEFT JOIN article_comment_counts acc ON acc.article_id = a.article_id
-> LEFT JOIN article_like_counts alc ON alc.article_id = a.article_id
-> LEFT JOIN article_dislike_counts adc ON adc.article_id = a.article_id;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Nested loop inner join (cost=2.37e+6 rows=51) (actual time=0.112..0.629 rows=51 loops=1)
-> Nested loop inner join (cost=2.37e+6 rows=51) (actual time=0.108..0.581 rows=51 loops=1)
-> Nested loop left join (cost=2.37e+6 rows=51) (actual time=0.104..0.559 rows=51 loops=1)
-> Nested loop left join (cost=2.37e+6 rows=51) (actual time=0.097..0.427 rows=51 loops=1)
-> Nested loop left join (cost=2.37e+6 rows=51) (actual time=0.0893..0.305 rows=51 loops=1)
-> Nested loop inner join (cost=2.37e+6 rows=51) (actual time=0.0804..0.178 rows=51 loops=1)
-> Table scan on cte (cost=2.37e+6..2.37e+6 rows=51) (actual time=0.0709..0.0775 rows=51 loops=1)
-> Materialize CTE cte (cost=2.37e+6..2.37e+6 rows=51) (actual time=0.0695..0.0695 rows=51 loops=1)
-> Limit: 51 row(s) (cost=2.37e+6 rows=51) (actual time=0.0282..0.0553 rows=51 loops=1)
-> Filter: ((a.board_id = 1) and (a.article_id < 12663454)) (cost=2.37e+6 rows=11.7e+6) (actual time=0.0276..0.0512 rows=51 loops=1)
-> Covering index range scan on a using idx_board_id_article_id over (board_id = 1 AND 12663454 < article_id) (cost=2.37e+6 rows=11.7e+6) (actual time=0.0256..0.0429 rows=51 loops=1)
-> Single-row index lookup on a using PRIMARY (article_id=cte.article_id) (cost=0.998 rows=1) (actual time=0.00172..0.00176 rows=1 loops=51)
-> Single-row index lookup on acc using PRIMARY (article_id=cte.article_id) (cost=0.933 rows=1) (actual time=0.0023..0.00231 rows=0.373 loops=51)
-> Single-row index lookup on alc using PRIMARY (article_id=cte.article_id) (cost=0.939 rows=1) (actual time=0.00221..0.00222 rows=0.294 loops=51)
-> Single-row index lookup on adc using PRIMARY (article_id=cte.article_id) (cost=0.953 rows=1) (actual time=0.00238..0.0024 rows=0.529 loops=51)
-> Single-row index lookup on b using PRIMARY (board_id=a.board_id) (cost=0.439 rows=1) (actual time=217e-6..252e-6 rows=1 loops=51)
-> Single-row index lookup on ac using PRIMARY (article_category_id=a.article_category_id) (cost=0.669 rows=1) (actual time=733e-6..772e-6 rows=1 loops=51)
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
- 이번엔 대상 추출 과정(filtering)까지 0.0512 ms 정도 소요된 것을 알 수 있다. (커버링 인덱스를 활용하기 때문에 매우 빠름.)
- 전반적인 성능은 기존 방식과 큰 차이 없어보이지만, 처리 효율을 놓고보면 새로운 쿼리 쪽이 좀 더 효율이 괜찮아 보인다.