2009-12-23 4 views
1

3 백만 레코드가있는 테이블에서 가장 효율적인 선택을 수행하려고합니다.복수/단일 열 인덱스가있는 범위 쿼리에서 MySQL 효율적인 선택/정렬

우선 몇 가지 자세한 정보를 원하시면

테이블 :

CREATE TABLE IF NOT EXISTS `activities_index` (
    `id` int(9) NOT NULL auto_increment, 
    `activity_id` int(6) NOT NULL, 
    `activity_status_id` int(2) NOT NULL, 
    `activity_source_id` int(6) default NULL, 
    `account_id` int(6) default NULL, 
    `owner_account_id` int(4) default NULL, 
    `date` date NOT NULL, 
    `is_event` int(1) NOT NULL, 
    `name` varchar(255) collate utf8_unicode_ci NOT NULL, 
    `content` longtext collate utf8_unicode_ci, 
    `location_name` varchar(255) collate utf8_unicode_ci default NULL, 
    `location_content` longtext collate utf8_unicode_ci, 
    `meta_keywords` varchar(255) collate utf8_unicode_ci default NULL, 
    `thumb_filename` varchar(255) collate utf8_unicode_ci default NULL, 
    `popular` int(1) NOT NULL default '0', 
    `price` float default NULL, 
    `city_id` int(9) default NULL, 
    `province_id` int(4) default NULL, 
    `country_id` int(4) default NULL, 
    `activity_location_id` int(6) NOT NULL, 
    `lat` decimal(10,6) default NULL, 
    `lng` decimal(10,6) default NULL, 
    `activity_modified` datetime default NULL, 
    `activity_created` datetime NOT NULL, 
    `activity_location_modified` datetime default NULL, 
    `activity_location_created` datetime NOT NULL, 
    `modified` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`), 
    KEY `is_event_idx` (`is_event`), 
    KEY `activity_id_idx` (`activity_id`), 
    KEY `status_city_idx` (`activity_status_id`, `city_id`), 
    KEY `date_idx` (`date`), 
    FULLTEXT KEY `txt_fields_idx` (`name`,`location_name`,`meta_keywords`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=14865 ; 

검색어 : 내 인덱스 선택에 대해

SELECT SQL_NO_CACHE * FROM `activities_index` WHERE 
date BETWEEN '2009-12-23' AND '2010-1-23' AND 
activity_status_id = 1 AND 
city_id IN ('86', '84', '87', '2381', '453', '137', '1561', '1116', '1614', '2456', '512', '305', '443', '1182', '2229') 
ORDER BY date 
LIMIT 25 

:
주요 문제는 DATE의 범위 선택입니다. 왜 내가 믿는 것을 기반으로 다중 컬럼 인덱스를 사용하지 않는가? 내가 틀렸다면 나를 바로 잡아라. MySQL은 범위 뒤의 인덱스를 사용하지 않는다. 따라서 색인 (DATE, ACTIVITY_STATUS_ID, CITY_ID)은 쓸모가 없습니다. 오른쪽 접두사를 사용할 때만 인덱스 테이블에서 순서가 올바릅니다. 따라서 CITY_ID, ACTIVITY_STATUS_ID, DATE의 다중 컬럼 인덱스는 DATE 컬럼에 데이터를 정렬하고자하므로 정확한 정렬 결과를 제공하지 않습니다.

설명 :
것은이 DATE에 의해 주문시 대신 내가 DATE_IDX에 그 순서를 뒤집어 생각 DATE_STATUS_IDX이 CITY_IDX보다 효율적으로 될 것이라고이 possible_keys 순서가 CITY_IDX 인 쿼리에 EXPLAIN 수행 할 때.

id select_type table type possible_keys key key_len ref rows Extra<br /> 
1 SIMPLE activities_index range city_idx,date_idx city_idx 5 NULL 1363 Using where; Using filesort 

내 질문 :
어떻게 내가이 possible_keys의 순서를 전환 할 수 있습니다?
문제를 해결하는 더 좋은 방법 : 300 만 레코드가있는 테이블을 효율적으로 선택 하시겠습니까?
내 사고 방식이 올바른 것입니까?

+0

테이블 정의에는 도시에 대한 단일 색인이 없지만 Explain 출력이있는 것처럼 보입니다. 다른 버전의 테이블에 출력 된 것입니까? –

답변

0

필자는 sql-query-analyzer가 오른쪽에서 왼쪽으로 쿼리를 구문 분석한다는 것을 알기 때문에 그가 만났던 첫 번째 인덱스는 가장 적합한 것이므로 city-one입니다. 어쩌면 당신은 in 절과 between 절의 위치를 ​​바꿈으로써 인덱스를 뒤집을 수 있습니다. 테이블의 모든 정보가 필요합니까? 그렇지 않다면 필요한 열만 선택하여 속도를 높일 수 있습니다.

+0

아. 작동하지 않는 것 같습니다. 티 그는 특정 분야를 정의하는 어플리케이션입니다. 나는 그 들판을 포함시키지 않으면 읽는 것이 더 쉬울 것이라고 생각했습니다. –

0

저는 이제 완전히 다른 것을 생각하고 있습니다. city_ids는 base_city + 범위의 결과이므로 base_city -> activity의 거리를 정의하기 위해 where 절에 날짜와 알고리즘 만 사용할 수 있습니다. 완료까지 약 0.009 초가 소요됩니다. 단점은 우리가 때로는 여전히 city_ids를 사용한다는 것입니다. 흠.

SQL_NO_CACHE * 
FROM `activities_index` AS idx 
WHERE 
ROUND(
((acos(sin((52.220818*pi()/180)) * sin((idx.lat *pi()/180)) + cos((52.220818*pi()/180)) * cos((idx.lat *pi()/180)) * cos(((6.891140 - idx.lng)*pi()/180)))) 
*180/pi()) *60*1.1515*1.609344 
) < 15 AND idx.date BETWEEN '2009-12-23' AND '2010-1-23' 
ORDER BY idx.date 
LIMIT 25 
0

index mergining에 대한 몇 가지 흥미로운 정보가 있습니다. 불행히도 쿼리는 나열된 결함 (단일 범위 스캔) 중 하나의 완벽한 예입니다.

회신 쿼리가 더 나은 당신이 확실히 그 알고리즘에서 어떤 최적화를 얻을 수 없기 때문에, 당신은 지정된 날짜 범위에서 얼마나 많은 행에 많은 의존 여부. 그러나 날짜 범위가 행을 충분히 좁힐 수 있다면 가장 효과적 일 수 있습니다.

참고 : EXPLAIN 출력의 possible_keys 순서는 중요하지 않습니다. 귀하의 문구는 또한 EXPLAIN 출력을 해석하여 마치 date을 사용하여 범위 선택을하고 있다고 말하는 것처럼 들리게합니다. 그렇지 않습니다. city_id에서 범위 선택을 수행하고 있습니다 (IN() 절의 최소값과 최대 값 사이의 city_id 값을 가진 모든 행을 검색합니다) 이렇게하는 효율은 값의 분포에 따라 크게 달라집니다.

ANALYZE TABLE activities_index을 실행하여 쿼리 속도 및/또는 EXPLAIN의 출력이 변경되는지 확인하십시오 .MySQL은 열 유형에 따라 값 분포를 예측하려고 시도하지만 실제로 테이블을 분석하면 사용할 실제 배포가 제공되므로 더 나은 결과를 얻을 수 있습니다