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

  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에 복합 인덱스를 사용하지 않습니다. 내가해야합니까?



답변

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

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 차원을 쿼리하는 경우 복합 색인을 사용하는 것이 가장 중요합니다 (아마도 가장 먼저).


답변

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

쿼리 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이 인덱스를 사용 하는 방법 매뉴얼 섹션을 살펴보십시오 .


답변

복합 인덱스의 기능에 대한 오해가있을 수 있습니다. 많은 사람들이 지수가 긴만큼 검색 쿼리를 최적화 할 수 있다고 생각 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 바이트 배정도 숫자를 사용했기 때문에 공간 포인트가 훨씬 더 많은 메모리소비 한다는 것 입니다. 내가 틀렸다면 나를 바로 잡으십시오.


답변

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

  • 구조 무결성 강화
  • 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하므로 복합 키를 사용할 것 입니다.”라는 단순한 문제보다 더 적합 합니다. 현재 데이터베이스 스키마에는 단일 키만큼 많은 복합 키가 있습니다. 간과하지 마십시오. 복합 키 사용!


답변

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

  • 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에 단일 열 인덱스를 가지는 것이 중요합니다.


답변

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

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

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

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

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


답변

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

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