복합 인덱스는 언제 사용해야합니까?


133
  1. 데이터베이스에서 복합 인덱스를 언제 사용해야합니까?
  2. 복합 인덱스를 사용하여 성능에 미치는 영향은 무엇입니까?)
  3. 왜 복합 인덱스를 사용해야합니까?

예를 들어 homes테이블이 있습니다.

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

그것은 나를 모두 복합 인덱스를 사용하는 의미가 있는가 geolatgeolng같은 그 :

나는 다음을 바꾼다 :

  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),

와:

KEY `geolat_geolng` (`geolat`, `geolng`)

그렇다면:

  • 왜?
  • 복합 인덱스를 사용하여 성능에 미치는 영향은 무엇입니까?)

최신 정보:

많은 사람들이 내가 수행하는 쿼리에 전적으로 의존한다고 말 했으므로 아래는 가장 일반적인 쿼리입니다.

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

업데이트 2 :

다음 데이터베이스 스키마를 사용하십시오.

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `primary_photo_group_id` int(10) unsigned NOT NULL default '0',
  `customer_id` bigint(20) unsigned NOT NULL,
  `account_type_id` int(11) NOT NULL,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `num_of_beds` tinyint(3) unsigned NOT NULL,
  `num_of_baths` decimal(3,1) unsigned NOT NULL,
  `num_of_floors` tinyint(3) unsigned NOT NULL,
  `description` text collate utf8_unicode_ci,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  `display_status` tinyint(1) NOT NULL,
  `date_listed` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `contact_email` varchar(100) collate utf8_unicode_ci NOT NULL,
  `contact_phone_number` varchar(15) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`home_id`),
  KEY `customer_id` (`customer_id`),
  KEY `city` (`city`),
  KEY `num_of_beds` (`num_of_beds`),
  KEY `num_of_baths` (`num_of_baths`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
  KEY `account_type_id` (`account_type_id`),
  KEY `display_status` (`display_status`),
  KEY `sqft` (`sqft`),
  KEY `price` (`price`),
  KEY `primary_photo_group_id` (`primary_photo_group_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;

다음 SQL 사용

EXPLAIN SELECT  homes.home_id,
                    address,
                    city,
                    state,
                    zip,
                    price,
                    sqft,
                    year_built,
                    account_type_id,
                    num_of_beds,
                    num_of_baths,
                    geolat,
                    geolng,
                    photo_id,
                    photo_url_dir
            FROM homes
            LEFT OUTER JOIN home_photos ON homes.home_id = home_photos.home_id
                AND homes.primary_photo_group_id = home_photos.home_photo_group_id
                AND home_photos.home_photo_type_id = 2
            WHERE homes.display_status = true
            AND homes.geolat BETWEEN -100 AND 100
            AND homes.geolng BETWEEN -100 AND 100

EXPLAIN은 다음을 반환합니다.

id  select_type  table        type  possible_keys                                    key                  key_len  ref     rows  Extra
----------------------------------------------------------------------------------------------------------
1   SIMPLE       homes        ref   geolat,geolng,display_status                     display_status       1        const   2     Using where
1  SIMPLE        home_photos  ref   home_id,home_photo_type_id,home_photo_group_id   home_photo_group_id  4        homes.primary_photo_group_id   4  

EXPLAIN 명령을 읽는 방법을 잘 모르겠습니다. 이것이 좋거나 나빠 보입니까? 현재 geolat 및 geolng에 복합 인덱스를 사용하지 않습니다. 내가해야합니까?

답변:


111

이점이있는 쿼리를 사용할 때는 복합 인덱스를 사용해야합니다. 다음과 같은 복합 인덱스 :

index( column_A, column_B, column_C )

이러한 필드를 사용하여 조인, 필터링 및 때로는 선택하는 쿼리에 도움이됩니다. 또한 해당 컴포지트에서 열의 가장 왼쪽에있는 하위 집합을 사용하는 쿼리에도 도움이됩니다. 따라서 위의 색인은 필요한 쿼리를 만족시킵니다.

index( column_A, column_B, column_C )
index( column_A, column_B )
index( column_A )

그러나 필요한 쿼리에는 도움이되지 않습니다 (적어도 직접적이지는 않지만 더 나은 지수가 없으면 부분적으로 도움이 될 수 있음)

index( column_A, column_C )

column_B가 어떻게 누락되었는지 확인하십시오.

원래 예에서 2 차원의 복합 색인은 대부분의 차원이나 가장 왼쪽의 차원을 쿼리하지만 가장 오른쪽의 차원 자체는 쿼리하지 않는 쿼리에 주로 도움이됩니다. 항상 2 차원을 쿼리하는 경우 복합 색인을 사용하는 것이 가장 중요합니다 (아마도 가장 먼저).


1
Mark, 원래 게시물을 업데이트했습니다 (업데이트 2). 이것은 내 실제 쿼리입니다. 내 실제 DB 스키마. 그리고 EXPLAIN 명령이 반환하는 것. 따라서이 정보로 복합 색인을 사용해야합니다. 나는 아직도 불분명하다. 미리 감사드립니다.
Teddy

Mark, 귀하의 답변에있는 복합 지수는 index (column_C)를 만족합니까?
Boris D. Teoharov

귀하의 질문을 이해하지 못했습니다. 그러나 index (A, B, C)가 C 열에서 필터링하는 쿼리를 도울 수 있는지 묻는 경우 일반적으로 아니요, 필터링에 인덱스를 사용하지 않습니다. 그러나 ABC의 서브 세트에서만 선택하는 경우 인덱스를 사용하여 테이블 스캔을 제거 할 수 있습니다. 그래서 그것은 다르지만 관련이 있습니다. 그러나 일반적으로 인덱스를 사용하여 필터링을 사용하는 경우 대답은 '아니요'입니다.
Mark Canlas

1
복합 색인이 도움 이 되지 않기 때문에 -1 입니다 WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???. 첫 번째 필드 이후에 중지됩니다. "질문 오버플로"의 대답은 그 이유를 설명합니다.
Rick James

1
@felwithe MySQL은 쿼리의 각 테이블 당 하나의 인덱스 만 사용할 수 있습니다 (예 : 인덱스 병합). 이는 쿼리의 테이블이 모든 where-clause, 테이블 조인, 그룹 별 및 순서별로 단일 인덱스를 사용해야 함을 의미합니다. 따라서 각 열의 개별 색인이 항상 작동하지는 않지만 복합 색인이 마법을 수행 할 수 있습니다.
AKHIL MATHEW

56

다음과 같은 세 가지 쿼리가 있다고 가정합니다.

쿼리 I :

SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4

쿼리 II :

SELECT * FROM homes WHERE `geolat`=42.9

쿼리 III :

SELECT * FROM homes WHERE `geolng`=36.4

열당 별도의 인덱스가있는 경우 세 쿼리 모두 인덱스를 사용합니다. MySQL에서 복합 인덱스 ( geolat, geolng)가있는 경우 쿼리 I 및 쿼리 II (복합 인덱스의 첫 번째 부분을 사용) 만 인덱스를 사용합니다. 이 경우 쿼리 III에는 전체 테이블 검색이 필요합니다.

다중 열 인덱스의 설명서의 부분, 내가 설명서를 다시 입력하지 않아도 여러 열 인덱스가 어떻게 작동하는지 설명 명확하게된다.

로부터 MySQL을 참조 설명서 페이지 :

다중 컬럼 인덱스는 인덱스 컬럼의 값을 연결하여 작성된 값을 포함하는 정렬 된 배열로 간주 될 수 있습니다 .

geolat 및 geolng 열에 분리 된 인덱스를 사용하는 경우 테이블에 독립적으로 검색 할 수있는 두 개의 다른 인덱스가 있습니다.

INDEX geolat
-----------
VALUE RRN
36.4  1
36.4  8
36.6  2
37.8  3
37.8  12
41.4  4

INDEX geolng
-----------
VALUE RRN
26.1  1
26.1  8
29.6  2
29.6  3
30.1  12
34.7  4

복합 색인을 사용하는 경우 두 열 모두에 대해 하나의 색인 만 있습니다.

INDEX (geolat, geolng)
-----------
VALUE      RRN
36.4,26.1  1
36.4,26.1  8
36.6,29.6  2
37.8,29.6  3
37.8,30.1  12
41.4,34.7  4

RRN은 상대 레코드 번호입니다 (간단히 말하면 ID를 말할 수 있음). 처음 두 인덱스는 별도로 생성되고 세 번째 인덱스는 복합입니다. 보시다시피 geolat에 의해 색인화되어 있기 때문에 geolng를 기준으로 검색 할 수 있지만 geolng는 2 단계 색인이므로 geolat 또는 "geolat AND geolng"로 검색 할 수 있습니다.

또한 MySQL이 인덱스를 사용 하는 방법 매뉴얼 섹션을 살펴보십시오 .


1
실제로, 나는 그러한 쿼리가 없습니다. 내 쿼리는 원래 게시물에 나열됩니다. 내 쿼리는 사각형 격자 안에 집을 반환하는 것입니다. 나는 공간에 대해 알고 거리를 계산하지 않습니다. 특정 지오그리드 (예 : 이웃 / 도시 / 군) 내 모든 집을 표시하려고 할 때 복합 색인을 사용하는 것이 합리적인지 알고 싶습니다.
Teddy

Eyazici, 나는 원래 게시물을 업데이트했습니다 (업데이트 2). 이것은 내 실제 쿼리입니다. 내 실제 DB 스키마. 그리고 EXPLAIN 명령이 반환하는 것. 따라서이 정보로 복합 색인을 사용해야합니다. 나는 아직도 불분명하다. 미리 감사드립니다
Teddy

@ "실제로 이러한 쿼리가 없습니다." 실제로 당신은 기본 논리를 설명하기 위해 간단한 WHERE 조건을 사용했습니다. 열에서 조건부 (즉, WHERE)를 사용할 때 MySQL은 가능할 때마다 색인을 사용하려고합니다. "x AND AND b 사이"는 "x> a AND x <b"와 유사합니다. 쿼리 조건부에서 geolng 및 geolat 열을 모두 사용했습니다. 복합 색인 "(geolat, geolng)"을 사용하는 경우 "AND geolng와 ??? AND ???" 조건부 인덱스의 장점을 얻지 못합니다 (MySQL의 경우). 따라서 시나리오에 대해 열당 별도의 인덱스 를 사용해야 합니다.
Emre Yazici

이해가 안 돼요 항상 두 열을 모두 포함하는 쿼리를 수행 할 때 geolat과 geolng에 대해 별도의 인덱스를 사용해야하는 이유
Teddy

1
아니요. "와 같이" "범위"가 발생 BETWEEN하면 인덱스의 추가 필드는 고려되지 않습니다! 따라서 복합 지수는 나아지지 않습니다.
Rick James

19

복합 인덱스의 기능에 대한 오해가있을 수 있습니다. 많은 사람들이 지수가 긴만큼 검색 쿼리를 최적화 할 수 있다고 생각 where조항은 귀하의 경우, 인덱스 컬럼을 포함 geolat하고 geolng. 더 깊이 탐구합시다 :

가정의 좌표에 대한 데이터는 다음과 같이 임의의 소수입니다.

home_id  geolat  geolng
   1    20.1243  50.4521
   2    22.6456  51.1564
   3    13.5464  45.4562
   4    55.5642 166.5756
   5    24.2624  27.4564
   6    62.1564  24.2542
...

그 이후 geolatgeolng가치는 거의 반복되지 않습니다. 에 지수 geolatgeolng같이 보일 것입니다 :

index_id  geolat  geolng
   1     20.1243  50.4521
   2     20.1244  61.1564
   3     20.1251  55.4562
   4     20.1293  66.5756
   5     20.1302  57.4564
   6     20.1311  54.2542
...

따라서 복합 인덱스의 두 번째 열은 기본적으로 쓸모없습니다 ! 복합 인덱스를 사용한 쿼리 속도는 아마도 geolat열의 인덱스와 비슷할 것 입니다.

Will이 언급했듯이 MySQL은 공간 확장 지원을 제공합니다 . 공간 포인트는 두 개의 개별 lat lng열 대신 단일 열에 저장됩니다 . 이러한 컬럼에 공간 인덱스를 적용 할 수 있습니다. 그러나 내 개인적인 경험에 따라 효율성이 과대 평가 될 수 있습니다. 공간 인덱스는 2 차원 문제를 해결하지 않고 2 차 분할을 사용하는 R- 트리를 사용하여 검색 속도를 높이는 것일 수 있습니다.

단점 좌표 저장에 8 바이트 배정도 숫자를 사용했기 때문에 공간 포인트가 훨씬 더 많은 메모리소비 한다는 것 입니다. 내가 틀렸다면 나를 바로 잡으십시오.


5

복합 인덱스는 다음과 같이 매우 강력합니다.

  • 구조 무결성 강화
  • FILTERED ID에서 정렬 사용

구조적 무결성 강화

복합 색인은 다른 유형의 색인이 아닙니다. 무결성을 기본 키로 적용하여 테이블에 NECESSARY 구조를 제공 할 수 있습니다.

MySQL의 Innodb는 클러스터링을 지원하며 다음 예제는 복합 인덱스가 필요한 이유를 보여줍니다.

친구 테이블 (예 : 소셜 네트워크)을 만들려면 2 개의 열이 필요합니다 user_id, friend_id.

테이블 구조

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

기본적으로 기본 키 (PK)는 고유하며 복합 PK를 생성하여 Innodb는 user_id, friend_id새 레코드가 추가 될 때 중복이 존재 하지 않는지 자동으로 확인합니다 . friend_id = 2예를 들어 사용자가 둘 이상의 레코드 (관계 링크)를 가져서는 안되기 때문에 이것은 예상 된 동작 입니다.

복합 PK가 없으면 대리 키를 사용하여이 스키마를 만들 수 있습니다.

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

이제 새 레코드가 추가 될 때마다 조합 user_id, friend_id이 포함 된 이전 레코드가 없는지 확인해야 합니다.

따라서 복합 인덱스는 구조 무결성을 강화할 수 있습니다.

필터링 된 ID에서 정렬 가능

게시물 시간 (타임 스탬프 또는 날짜 / 시간)별로 레코드 집합을 정렬하는 것이 매우 일반적입니다. 일반적으로 이것은 주어진 ID에 게시하는 것을 의미합니다. 여기에 예가 있습니다

User_Wall_Posts 테이블 (Facebook의 벽 게시물인지 생각)

user_id (medium_int)
timestamp (timestamp)
author_id (medium_int)
comment_post (text)

Primary Key -> (user_id, timestamp, author_id)

우리는 (날짜) user_id = 10별로 댓글 게시물에 대한 모든 게시물을 쿼리하고 찾고 싶습니다 timestamp.

SQL 쿼리

SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES

복합 PK를 통해 Mysql은 색인을 사용하여 결과를 필터링하고 정렬 할 수 있습니다. MySQL은 결과를 가져 오기 위해 임시 파일이나 파일 정렬을 사용할 필요가 없습니다. 복합 키가 없으면 이것이 불가능하며 매우 비효율적 인 쿼리를 유발할 수 있습니다.

따라서 복합 키는 매우 강력하며 "검색을 원 column_a, column_b하므로 복합 키를 사용할 것 입니다."라는 단순한 문제보다 더 적합 합니다. 현재 데이터베이스 스키마에는 단일 키만큼 많은 복합 키가 있습니다. 간과하지 마십시오. 복합 키 사용!


5

복합 인덱스 는 다음에 유용합니다.

  • 0 개 이상의 "="절과
  • 최대 하나의 범위 절.

복합 인덱스는 범위를 처리 할 수 ​​없습니다 . 인덱스 쿡북 에서 더 자세히 설명하겠습니다 .

가까운 찾기 - 질문 인 경우 정말 최적화에 대해

WHERE geolat BETWEEN ??? AND ???
  AND geolng BETWEEN ??? AND ???

다음 아무 지수는 정말 두 차원을 처리 할 수 없습니다.

대신, '상자 밖으로 생각해야'합니다. 분할을 통해 한 차원을 구현하고을 신중하게 선택하여 다른 차원을 구현하면 PRIMARY KEY매우 큰 위도 / 경도 조회 테이블의 효율성이 크게 향상 될 수 있습니다. 내 latlng 블로그 는 전 세계에서 "가장 가까운 위치 찾기"를 구현하는 방법에 대해 자세히 설명합니다. 코드가 포함되어 있습니다.

PARTITIONs위도 범위의 줄무늬입니다. 는 PRIMARY KEY의도적 때문에 유용 행은 동일한 블록에있을 가능성이 높다는 경도 시작한다. Stored Routine order by... limit...은 충분한 커피 숍 (또는 무엇이든)이 생길 때까지 대상 주변에서 '사각형' 을 만들고 늘리기 위한 지저분한 코드를 오케스트레이션합니다 . 또한 대원 계산을 처리하고 날짜 표시 줄과 극점을 처리합니다.

다른 블로그를 작성했습니다. : 그것은 위도 / 일 검색 LNG의 5 가지 비교 http://mysql.rjweb.org/doc.php/latlng#representation_choices을 다른 방법 중 하나이며,이 (이것은 제 중 하나로서 상기 소정의 링크를 참조) 그리고 특정 경우에 가장 적합 하다고 지적합니다 .

INDEX(geolat, geolng),
INDEX(geolng, geolat)

두 지수 모두 열을 가지고하고, 그 되지 geolat 및 geolng에 단일 열 인덱스를 가지는 것이 중요합니다.


1

흑백은 없으며 한 가지 크기가 모든 답에 적합합니다.

쿼리 작업로드가 이점을 얻을 때 복합 인덱스를 사용해야합니다.

이를 판별하기 위해 조회 작업로드를 프로파일 링해야합니다.

복합 인덱스는 해당 인덱스에서 쿼리를 완전히 만족시킬 수있을 때 작동합니다.

업데이트 (게시 된 질문에 대한 편집에 대한 응답으로) : 표에서 *를 선택하면 복합 색인이 사용될 수 있습니다. EXPLAIN PLAN 을 실행 하여 확인해야합니다.


지리적 위치 데이터 (위도 및 경도)에 대해 복합 색인을 사용하는 것이 합리적입니까?
Teddy

1
해당 테이블에 대해 수행되는 쿼리에 전적으로 의존합니다.
Mitch Wheat

가장 일반적인 검색어를 포함하도록 원본 게시물을 업데이트했습니다. 위 참조.
Teddy

1

공간 검색을 수행하려면 지리적 영역을 매우 빠르게 검색 할 수 있는 R- 트리 알고리즘 이 필요합니다 . 이 직업에 꼭 필요한 것.

일부 데이터베이스에는 공간 인덱스가 내장되어 있습니다. 빠른 Google 검색을 통해 MySQL 5에 MySQL이 있음을 알 수 있습니다 (SQL을 보면 MySQL을 사용하고 있다고 생각합니다).


1

복합 색인은 group by절 을 최적화 할 때 유용 할 수 있습니다 (이 기사 http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html 확인 ). 주의하십시오 :

GROUP BY에 색인을 사용하기위한 가장 중요한 전제 조건은 모든 GROUP BY 열이 동일한 색인의 속성을 참조하고 색인이 키를 순서대로 저장하는 것입니다 (예 : 이것은 ASHREE 색인이며 HASH 색인이 아님)


GROUP BY언급되지 않았습니다.
Rick James

어디서 언급되지 않았습니까? :) 그것은 내가 언급 한 기사에서 분명히 언급되었습니다. 그리고 질문에 대한 답변입니다. 데이터베이스에서 복합 인덱스를 언제 사용해야합니까? 복합 인덱스를 사용하여 성능에 미치는 영향은 무엇입니까?) 왜 복합 인덱스를 사용해야합니까?
Alexander

수정 : GROUP BYOP는 언급하지 않았습니다.
Rick James

물론, 이것이 대답이었습니다. 데이터베이스에서 복합 인덱스를 사용하는 경우 중 하나입니다.
Alexander

0

나는 @Mitch와 함께 전적으로 귀하의 쿼리에 달려 있습니다. 다행스럽게도 언제든지 인덱스를 작성하고 삭제할 수 있으며 EXPLAIN 키워드를 쿼리 앞에 추가하여 쿼리 분석기가 인덱스를 사용하는지 확인할 수 있습니다.

정확한 위도 / 경도 쌍을 찾는다면 이 지수가 의미가있을 것입니다. 그러나 특정 장소에서 일정 거리 내에서 집을 찾게 될 것이므로 쿼리는 다음과 같습니다 ( 출처 참조 ).

select *, sqrt(  pow(h2.geolat - h1.geolat,  2) 
               + pow(h2.geolng - h1.geolng, 2) ) as distance
from homes h1, homes h2
where h1.home_id = 12345 and h2.home_id != h1.home_id
order by distance

색인은 전혀 도움이되지 않을 것입니다. 지리 공간 쿼리의 경우 다음과 같은 것이 필요 합니다 .

업데이트 :이 쿼리로 :

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

쿼리 분석기는 geolat 단독 또는 geolng 단독 또는 둘 다의 인덱스를 사용할 수 있습니다. 복합 인덱스를 사용한다고 생각하지 않습니다. 그러나 실제 데이터 세트에서 이러한 순열을 각각 시도한 다음 (a) EXPLAIN이 알려주는 내용을 확인하고 (b) 쿼리에 실제로 걸리는 시간을 측정하는 것은 쉽습니다.


나는 단순히 사각형 격자 내에서 집으로 돌아가고 싶어합니다. 공간에 대해 알고 있으므로 거리를 계산하지 않습니다. 나는 단순히 사각형 격자 안에있는 집으로 돌아가고 싶어서 빨리 수행하고 싶다. 따라서 인덱스를 올바르게 설정했는지 확인하고 싶습니다. 도움이 되나요?
Teddy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.