mysql in 문장 조회 효율 느린 최적화 기술 예시

10150 단어
표의 구조는 다음과 같다. 문장은 690편에 불과하다.

   article(id,title,content)
   tag(tid,tag_name)
       article_tag(id,tag_id,article_id)

그중에 탭이 있는 tid는 135이고, 탭을 조회하는 tid는 135의 글 목록입니다.
690편의 문장, 아래의 문장으로 조회, 기만:

select id,title from article where id in(
select article_id from article_tag where tag_id=135
)

그 중에서 이 속도는 매우 빠르다.

select article_id from article_tag where tag_id=135

조회 결과 문장 5편, id 4284294304332
다음 sql로 문장을 찾는 것도 매우 빠르다.

select id,title from article where id in(
428,429,430,431,432
)

해결 방법:

select id,title from article where id in(
select article_id from (select article_id from article_tag where tag_id=135) as tbt
)

기타 해결 방법: (예)

mysql> select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839');

편폭을 절약하기 위해 출력 내용을 생략하였습니다. 아래와 같습니다.
67 rows in set (12.00 sec)
67줄의 데이터만 되돌아오는데 12초가 걸렸고 시스템에 이런 조회가 동시에 많이 있을 수 있으니 시스템은 감당할 수 없을 것이다.desc로 보기 (주: explain도 가능)

mysql> desc select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839');
+----+--------------------+------------------+--------+-----------------+-------+---------+------------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------------+--------+-----------------+-------+---------+------------+---------+--------------------------+
| 1 | PRIMARY | abc_number_prop | ALL | NULL | NULL | NULL | NULL | 2679838 | Using where |
| 2 | DEPENDENT SUBQUERY | abc_number_phone | eq_ref | phone,number_id | phone | 70 | const,func | 1 | Using where; Using index |
+----+--------------------+------------------+--------+-----------------+-------+---------+------------+---------+--------------------------+
2 rows in set (0.00 sec)

이 검색을 실행할 때 2백만 줄을 스캔할 수 있음을 알 수 있습니다. 색인을 만들지 않았습니까? 한번 보십시오.

mysql>show index from abc_number_phone;
+------------------+------------+-------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+-------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| abc_number_phone | 0 | PRIMARY | 1 | number_phone_id | A | 36879 | NULL | NULL | | BTREE | | |
| abc_number_phone | 0 | phone | 1 | phone | A | 36879 | NULL | NULL | | BTREE | | |
| abc_number_phone | 0 | phone | 2 | number_id | A | 36879 | NULL | NULL | | BTREE | | |
| abc_number_phone | 1 | number_id | 1 | number_id | A | 36879 | NULL | NULL | | BTREE | | |
| abc_number_phone | 1 | created_by | 1 | created_by | A | 36879 | NULL | NULL | | BTREE | | |
| abc_number_phone | 1 | modified_by | 1 | modified_by | A | 36879 | NULL | NULL | YES | BTREE | | |
+------------------+------------+-------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.06 sec)
mysql>show index from abc_number_prop;
+-----------------+------------+-------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------+------------+-------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| abc_number_prop | 0 | PRIMARY | 1 | number_prop_id | A | 311268 | NULL | NULL | | BTREE | | |
| abc_number_prop | 1 | number_id | 1 | number_id | A | 311268 | NULL | NULL | | BTREE | | |
| abc_number_prop | 1 | created_by | 1 | created_by | A | 311268 | NULL | NULL | | BTREE | | |
| abc_number_prop | 1 | modified_by | 1 | modified_by | A | 311268 | NULL | NULL | YES | BTREE | | |
+-----------------+------------+-------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.15 sec)

위의 출력에서 알 수 있듯이 이 두 장의 표는number 에 있다id 필드에 색인을 만들었습니다.하위 조회 자체에 문제가 있는지 없는지를 봐라.

mysql> desc select number_id from abc_number_phone where phone = '82306839';
+----+-------------+------------------+------+---------------+-------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+------+---------------+-------+---------+-------+------+--------------------------+
| 1 | SIMPLE | abc_number_phone | ref | phone | phone | 66 | const | 6 | Using where; Using index |
+----+-------------+------------------+------+---------------+-------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)


문제없습니다. 몇 줄의 데이터만 스캔하면 색인이 작용합니다.
검색해 보십시오:

mysql> select number_id from abc_number_phone where phone = '82306839';
+-----------+
| number_id |
+-----------+
| 8585 |
| 10720 |
| 148644 |
| 151307 |
| 170691 |
| 221897 |
+-----------+
6 rows in set (0.00 sec)


직접 하위 조회에서 얻은 데이터를 위의 조회에 넣다

mysql> select * from abc_number_prop where number_id in (8585, 10720, 148644, 151307, 170691, 221897);
67 rows in set (0.03 sec)


속도도 빨라서 MySQL이 하위 조회를 처리할 때 부족한 것 같습니다.나는 MySQL 5.1.42와 MySQL 5.5.19에서 모두 이 문제를 시도했다.
인터넷을 검색해 보니 많은 사람들이 이 문제를 겪었다.
참고 자료 1: MySQL 최적화 사용 연결(join) 대신 하위 검색
참고 자료 2: MYSQL 하위 조회 및 네스트된 조회 최적화 실례 분석
인터넷에 올라온 이 자료들의 건의에 따라join으로 바꾸어 시험해 보자.수정 전:

select * from abc_number_prop where number_id in (select number_id from abc_number_phone where phone = '82306839');

수정 후:

select a.* from abc_number_prop a inner join abc_number_phone b on a.number_id = b.number_id where phone = '82306839';
mysql> select a.* from abc_number_prop a inner join abc_number_phone b on a.number_id = b.number_id where phone = '82306839';
67 rows in set (0.00 sec)

효과가 좋아서 조회에 걸리는 시간이 거의 0이다.MySQL이 이 검색어를 어떻게 실행하는지 한번 볼게요.

mysql>desc select a.* from abc_number_prop a inner join abc_number_phone b on a.number_id = b.number_id where phone = '82306839';
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
| 1 | SIMPLE | b | ref | phone,number_id | phone | 66 | const | 6 | Using where; Using index |
| 1 | SIMPLE | a | ref | number_id | number_id | 4 | eap.b.number_id | 3 | |
+----+-------------+-------+------+-----------------+-----------+---------+-----------------+------+--------------------------+
2 rows in set (0.00 sec)

요약: 하위 조회 속도가 느릴 때 JOIN이 이 조회를 고쳐서 최적화할 수 있습니다.
인터넷에 JOIN 문구를 사용하는 검색이 반드시 하위 검색어를 사용하는 문구보다 빠른 것은 아니라는 글도 있다.
mysql 매뉴얼도 언급되었는데 구체적인 원문은 mysql 문서의 이 장에서 다음과 같다. I.3.Restrictions on Subqueries 13.2.8. Subquery Syntax
발췌문:
1) IN 사용 하위 질의:
Subquery optimization for IN is not as effective as for the = operator or for IN(value_list) constructs.
A typical case for poor IN subquery performance is when the subquery returns a small number of rows but the outer query returns a large number of rows to be compared to the subquery result.
The problem is that, for a statement that uses an IN subquery, the optimizer rewrites it as a correlated subquery. Consider the following statement that uses an uncorrelated subquery:
SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);
The optimizer rewrites the statement to a correlated subquery:
SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);
If the inner and outer queries return M and N rows, respectively, the execution time becomes on the order of O(M×N), rather than O(M+N) as it would be for an uncorrelated subquery.
An implication is that an IN subquery can be much slower than a query written using an IN(value_list) construct that lists the same values that the subquery would return.
2) 하위 쿼리를 Join으로 변환하는 방법:
The optimizer is more mature for joins than for subqueries, so in many cases a statement that uses a subquery can be executed more efficiently if you rewrite it as a join.
An exception occurs for the case where an IN subquery can be rewritten as a SELECT DISTINCT join. Example:
SELECT col FROM t1 WHERE id_col IN (SELECT id_col2 FROM t2 WHERE condition);
That statement can be rewritten as follows:
SELECT DISTINCT col FROM t1, t2 WHERE t1.id_col = t2.id_col AND condition;
But in this case, the join requires an extra DISTINCT operation and is not more efficient than the subquery
총결산
이상은 본고에서 mysql in 문장 조회 효율이 느린 최적화 기교에 대한 예시의 전체 내용입니다. 관심 있는 분들은 mysql의 하위 조회 연합과 in의 효율, 기업 생산 MysQL 최적화 소개 등을 간단히 말씀드리고 어떤 문제가 있으면 댓글을 남겨주시면 참고하시기 바랍니다.
본문에서 서술한 것이 모두에게 도움이 되기를 바란다.

좋은 웹페이지 즐겨찾기