2009-11-22 4 views
3

다음 쿼리는 매우 간단합니다. 페이징 시나리오에서 사용하기 위해 메시지 테이블의 마지막 20 개 레코드를 선택합니다. 처음으로이 쿼리를 실행하면 15-30 초가 걸립니다. 이후의 실행에는 1 초도 채 걸리지 않습니다 (일부 캐싱이 필요합니다). 처음으로 왜 그렇게 오래 걸리는지 알아 내려고합니다. 테이블 여기간단한 쿼리는 15-30 초 걸림

을 4.0.26이-로그 것 :

table type possible_keys key  key_len ref  rows Extra 
------ ------ ------------- -------- ------- ------ ------ -------------------------------------------- 
m  ref  List,ListOnly ListOnly 10  const 18002 Using where; Using temporary; Using filesort 

이유 :

여기
messages CREATE TABLE `messages` (
    `ID` int(10) unsigned NOT NULL auto_increment, 
    `List` varchar(10) NOT NULL default '', 
    `MessageId` varchar(128) NOT NULL default '', 
    `From` varchar(128) NOT NULL default '', 
    `Subject` varchar(128) NOT NULL default '', 
    `MsgDate` datetime NOT NULL default '0000-00-00 00:00:00', 
    `TextBody` longtext NOT NULL, 
    `HtmlBody` longtext NOT NULL, 
    `Headers` text NOT NULL, 
    `UserID` int(10) unsigned default NULL, 
    PRIMARY KEY (`ID`), 
    UNIQUE KEY `List` (`List`,`MsgDate`,`MessageId`), 
    KEY `From` (`From`), 
    KEY `UserID` (`UserID`,`List`,`MsgDate`), 
    KEY `MsgDate` (`MsgDate`), 
    KEY `ListOnly` (`List`) 
) TYPE=MyISAM ROW_FORMAT=DYNAMIC 

는 설명이있어

SELECT DISTINCT ID,List,`From`,Subject, UNIX_TIMESTAMP(MsgDate) AS FmtDate 
FROM messages 
WHERE List='general' 
ORDER BY MsgDate 
LIMIT 17290,20; 

MySQL 버전 : 여기

는 쿼리의 filesort를 사용할 때 모든 관련 열에 대한 색인이 있습니까? ListOnly 인덱스를 추가하여 도움이되는지 확인했습니다. 나는 원래 목록 인덱스가 목록 선택과 MsgDate에서의 정렬을 처리 할 것이라고 생각했지만 그렇지 않았습니다. 이제는 ListOnly 인덱스를 추가 했으므로 MsgDate에서 여전히 파일롯을 수행합니다. MsgDate는 오래 걸리는 것으로 의심됩니다. 이 인덱스를 사용하는 MySQL을 강제로 보인다, 그러나 모든 쿼리 속도를하지 않습니다

SELECT DISTINCT ID,List,`From`,Subject, UNIX_TIMESTAMP(MsgDate) AS FmtDate 
FROM messages 
FORCE INDEX (List) 
WHERE List='general' 
ORDER BY MsgDate 
LIMIT 17290,20; 

:

나는 다음과 같이 FORCE의 INDEX를 사용했습니다.

다음은이 쿼리에 대한 설명입니다 :

table type possible_keys key  key_len ref  rows Extra      
------ ------ ------------- ------ ------- ------ ------ ---------------------------- 
m  ref  List   List 10  const 18002 Using where; Using temporary 

업데이트합니다

내가 쿼리에서 DISTINCT 제거. 그것은 성능에 전혀 도움이되지 못했습니다.

UNIX_TIMESTAMP 호출을 제거했습니다. 또한 성능에 영향을주지 않았습니다.

SELECT m.ID,List,From,Subject,MsgDate 
FROM messages 
WHERE MsgDate>='2009-11-15' 
ORDER BY MsgDate DESC 
LIMIT 20 
: 나는 사용자가 결과의 마지막 페이지를 찾고 검색 할 경우, 나는 결과의 마지막 7 일 반환하는 WHERE 절을 추가하도록

나는 내 PHP 코드에 특별한 경우를 만들어

이것은 훨씬 빠릅니다. 그러나 결과의 다른 페이지로 이동하자마자 이전 SQL을 사용해야하고 실행하는 데 오랜 시간이 걸립니다. 나는 모든 페이지에서 이것을 할 수있는 실용적이고 현실적인 방법을 생각할 수 없다. 또한이 특별한 경우를 수행하면 PHP 코드가 더 복잡해집니다.

이상하게도 최초 쿼리가 처음 실행될 때만 시간이 오래 걸립니다. 동일한 쿼리 또는 결과의 다른 페이지를 나타내는 쿼리 (즉, LIMIT 절만 변경)의 후속 실행은 매우 빠릅니다. 약 5 분 동안 실행하지 않으면 쿼리가 다시 느려집니다.

해결책 :

제이슨 Orendorff와 줄리엣의 아이디어를 기반으로 해낸 가장 좋은 솔루션입니다.

먼저 현재 페이지가 총 페이지 수의 시작 또는 끝에 더 가까운 지 확인합니다.마지막에 가까울 경우 ORDER BY MsgDate DESC를 사용하여 적절한 제한을 적용한 다음 반환 된 레코드의 순서를 반대로 변경합니다.

이렇게하면 결과 집합의 처음 또는 끝에 가까운 페이지를 훨씬 빠르게 검색 할 수 있습니다 (처음에는 15-30 대신 4-5 초 걸립니다). 사용자가 가운데 근처의 페이지 (현재 430 번째 페이지 부근)로 이동하려는 경우 속도가 다시 내려갈 수 있습니다. 하지만 드문 경우입니다.

완벽한 해결책이없는 것처럼 보이지만 대부분의 경우보다 훨씬 좋습니다.

감사합니다. 제이슨과 줄리엣.

+0

좋은 점, shylent. 나는 DISTINCT없이 그것을 시도 할 것이다. – elmonty

+0

DISTINCT를 제거해도 성능이 향상되지 않았습니다. – elmonty

+0

"LIMIT 17290,20"는 검색어의 865 번째 페이지에 해당하지 않습니까? 사용자가 실제로 데이터 세트를 탐색하고 있습니까? – Juliet

답변

3

ORDER BY MsgDate LIMIT 17290,20 대신 ORDER BY MsgDate DESC LIMIT 20을 사용해보십시오.

물론 결과가 역순으로 나오지만 처리가 쉬워야합니다.

편집 :MessageId 값은 항상 시간이 지남에 따라 증가합니까? 그들은 독특합니까?

그렇다면, 내가 인덱스 만들 것 : 메시지에 따라

UNIQUE KEY `ListMsgId` (`List`, `MessageId`) 

및 쿼리가 IDS보다는 날짜 가능합니다.

-- Most recent messages (in reverse order) 
SELECT * FROM messages 
WHERE List = 'general' 
ORDER BY MessageId DESC 
LIMIT 20 

-- Previous page (in reverse order) 
SELECT * FROM messages 
WHERE List = 'general' AND MessageId < '15885830' 
ORDER BY MessageId DESC 
LIMIT 20 

-- Next page 
SELECT * FROM messages 
WHERE List = 'general' AND MessageId > '15885829' 
ORDER BY MessageId 
LIMIT 20 

난 당신이 또한 int 타입이 훨씬 빨리 될 것 varchar 열을 가진 지불하고 생각합니다. 예를 들어 List은 별도의 테이블에있는 항목을 가리키는 ListId 일 수 있습니다. 그것이 사실인지 확인하기 위해 테스트 데이터베이스에서 시도해 볼 수 있습니다. 나는 MySQL 전문가가 아니다.

+0

사용자가 이전 20 개의 메시지 (또는 이전 20 개)로 다시 페이지 할 수 있기 때문에 LIMIT을 사용합니다. – elmonty

+0

나는 이것을 시도했지만 전혀 도움이되지 않습니다. LIMIT에 오프셋이 사용되는지 여부와 상관없이 동일한 문제가 발생했기 때문일 수 있습니다. – elmonty

+0

와우. 이것은 일종의 기괴합니다. 이 쿼리의 가장 간단한 가능한 형태는'SELECT * FROM messages WHERE List = '일반'ORDER BY MsgDate' 일 것입니다. (List, MsgDate, MessageId)에 대한 인덱스가 있기 때문에 초고속 일 것으로 예상됩니다. 한계가 처리되기 전에 전체 행 세트를 검색하기 때문에 그것은 빨리하지 MsgDate DESC LIMIT 20 BY 목록 = '일반' ORDER 메시지 로부터 SELECT * : –

1

사용중인 SQL 버전은 무엇입니까? 이전 버전 중 일부는 LIMIT 절을 포스트 프로세스 필터 (서버에서 요청한 모든 레코드를 가져 왔지만 요청한 20 개만 표시 함을 의미)로 사용했습니다.

18 개의 행이 표시되지만 18 개만 표시 될 수 있습니다. 18000 행을 되찾고 20 개만 보여주기보다는 반환하려는 20 행을 식별하기 위해 선택 기준을 조정할 수있는 방법이 있습니까 ???

+0

4.0.26 : 확실히 오래된 버전의 MySQL을 사용하고 싶습니다. – bobince

+0

의미가 있습니다. 첫 번째 쿼리는 18002 개의 행을 반환하지만 20 개만 표시합니다. (귀하의 설명은 이것을 보여줍니다). 다른 사람들이 같은 줄을 따라 솔루션을 제공 한 것처럼 보입니다. LIMIT 옵션을 사용하는 대신 WHERE 절을 통해 행을 줄 이도록하십시오. 가장 최근의 레코드를 찾고 있다면 max msgdate를 가져 와서 어쩌면 이전 날짜를 where 절에서 사용할 수 있습니다. 데이터가 최대 날짜와 최대 날짜를 구하는 두 가지 쿼리 일지라도 더 빠르게 실행되어야합니다. 행운을 빌어 요 – Sparky

+0

PHP 코드에서 특별한 경우를 만들었습니다. 사용자가 결과의 마지막 페이지를보고 있음을 감지하면 지난 7 일간의 결과 만 반환하는 WHERE 절이 추가됩니다. SELECT m.ID, 목록, '보낸 사람', 제목, 메시지 날짜 발신자 표시 메시지 WHERE MsgDate> = '2009-11-15' ORDER BY MsgDate DESC LIMIT 20 이것은 훨씬 빠릅니다. 그러나 결과의 다른 페이지로 이동하자마자 이전 SQL을 사용하고 반환하는 데 오래 걸립니다. 나는 모든 페이지에서 이것을 할 수있는 실용적이고 현실적인 방법을 생각할 수 없다. – elmonty

2

ListOnly 키를 누를 수 있습니다. 복합 색인 List에는 이미 모든 정보가 들어 있습니다.

List -indexed 쿼리의 EXPLAIN은 filesort가 부족해 보입니다. Jason이 제안한대로 ORDER를 교체하고 UNIX_TIMESTAMP 호출을 잃어 버리면 더 나은 성능을 얻을 수 있습니다 (응용 프로그램 계층에서이를 수행하거나 스키마에서 INTEGER로 저장된 Unix 타임 스탬프 만 사용하면됩니다).

+0

UNIX_TIMESTAMP를 잃으면 실제로 성능에 많은 영향을 줍니까? – elmonty

+0

정말 모르겠지만 테스트하고 볼 수만 있습니다. 나는 희망하지 않지만, 종종 계산 된 컬럼을 사용하면 MySQL이 임시 테이블을 사용할 수있다. 개인적으로는 고유 한 날짜 유형이 DBMS간에 액세스 레이어 비 호환성을 발생시키는 경향이 있고 일반 타임 스탬프보다 상대적으로 거의 유틸리티를 제공하지 않으므로 날짜에 유닉스 타임 스탬프 만 사용합니다. – bobince

+0

UNIX_TIMESTAMP 제거를 시도했습니다. 그것은 성능에 전혀 도움이되지 못했습니다. – elmonty

관련 문제