2012-10-23 2 views
8

이 질문은 "저의 숙제를하는 것처럼"느껴지 긴하지만,이 쿼리가 많은 행이있는 테이블에 대해 신속하게 실행되도록 노력하고 있습니다. Here's a SQLFiddle (다소 차이가 있음) 스키마를 보여줍니다.수억 개의 행을 가진 테이블에 대한 쿼리를 최적화하십시오.

나는 모든 필수 열을 보여줄 것이지만별로 성공하지 못했던 것을 얻기 위해 색인을 가지고 놀았습니다.

CREATE TABLE `AuditEvent` (
    `auditEventId` bigint(20) NOT NULL AUTO_INCREMENT, 
    `eventTime` datetime NOT NULL, 
    `target1Id` int(11) DEFAULT NULL, 
    `target1Name` varchar(100) DEFAULT NULL, 
    `target2Id` int(11) DEFAULT NULL, 
    `target2Name` varchar(100) DEFAULT NULL, 
    `clientId` int(11) NOT NULL DEFAULT '1', 
    `type` int(11) not null, 
    PRIMARY KEY (`auditEventId`), 
    KEY `Transactions` (`clientId`,`eventTime`,`target1Id`,`type`), 
    KEY `TransactionsJoin` (`auditEventId`, `clientId`,`eventTime`,`target1Id`,`type`) 
) 

을 그리고 (버전)을 select : 여기에 create

select ae.target1Id, ae.type, count(*) 
from AuditEvent ae 
where ae.clientId=4 
    and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
group by ae.target1Id, ae.type; 

나는뿐만 아니라 '사용 일시적인'과 '사용 filesort'와 끝까지. count(*)을 삭제하고 대신 select distinct을 사용하여 '파일 사용'을 발생시키지 않았습니다. 카운트를 얻기 위해 join으로 돌아 가면 가능합니다.

원본으로 감사 레코드가 생성되었을 때와 같이 대상의 target1Name 및 target2Name을 추적하도록 결정되었습니다. 그 이름도 필요합니다 (가장 최근에 할 것입니다).

현재 위의 쿼리는 target1Name 및 target2Name 열이 누락되어 약 2400 만 개의 레코드에서 약 5 초 만에 실행됩니다. 우리의 목표는 수억 수천만에 달합니다. 쿼리를 계속 실행하고 싶습니다. (1-2 분 내에 유지하기를 원하지만, 우리는 그것을 훨씬 좋아야합니다.)하지만 두려움은 한 번입니다. 우리는 더 많은 양의 데이터를 쳤지 만 추가 행을 시뮬레이션하기 위해 노력하고 있습니다.

추가 입력란을 가져 오는 가장 좋은 전략은 확실하지 않습니다. select에 열을 직접 추가하면 쿼리에서 '인덱스 사용'이 손실됩니다. 나는 테이블에 join을 시도했는데, 이것은 'Using index'를 유지하지만 20 초 정도 걸린다.

eventTime 열을 datetime이 아닌 int로 변경하려고했으나 인덱스 사용이나 시간에 영향을 미치지 않았습니다.

당신이 아마 알고있는 것처럼
+0

무엇 현재 쿼리 타이밍이고 당신이 아래 이해한다 : 당신이 질문을하면

는 바로이 다음 시도 (당신은 충분히 빨리 생각하는) 쿼리에 두 필드를 추가하는 방법을 그냥 "빨리"? – feeela

+0

죄송합니다. 해당 세부 정보를 추가했습니다. –

+0

clientId 및 eventTime에 인덱스가 있습니까? 또한 eventTime 인덱스를 사용 중이며 전체 테이블 스캔을 수행하지 않는 오브젝트가 있는지 확인하십시오. –

답변

3

, 여기에 문제는 (항상처럼) Transactions 인덱스의 효율적인 사용을 (즉, 인덱스가 실제로 clientId 방정식과 범위 조건의 첫 번째 부분과 만 사용됩니다 나누기 범위 조건 ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00'입니다 색인은 그룹화에 사용되지 않음).

는 대부분의 경우,이 솔루션은 (귀하의 경우, 마침표로 period 열, 그룹 eventTime을 소개하고 period IN (1,2,3,4,5)으로 BETWEEN 절을 대체) 동등 검사와 범위 조건을 교체하는 것입니다. 그러나 이것은 귀하의 테이블에 대한 오버 헤드가 될 수 있습니다.

당신이 시도 할 수있는 또 다른 해결책은 (그것이 더 이상 사용하지 않을 경우 아마 Transactions 교체) 다른 인덱스를 추가하는 것입니다

: (clientId, target1Id, type, eventTime)를, 다음과 같은 쿼리를 사용

SELECT 
    ae.target1Id, 
    ae.type, 
    COUNT(
    NULLIF(ae.eventTime BETWEEN '2011-09-01 03:00:00' 
          AND '2012-09-30 23:57:00', 0) 
) as cnt, 
FROM AuditEvent ae 
WHERE ae.clientId=4 
GROUP BY ae.target1Id, ae.type; 

그런 식으로, 당신은 것) 이동를 다음 단의 범위 조건, b), c) 상기 인덱스 그게)

UPD1를 디스크 IO 동작을 필요로하지 않는 질의이다 (쿼리 인덱스을 피복 할 그룹핑에 대한 인덱스를 사용 허용 죄송합니다. yesteday 귀하의 게시물을주의 깊게 읽지 않았으며 target1Nametarget2Name을 검색하는 것이 확인되지 않았습니다. 우선, Using index의 의미를 올바르게 이해하는지 확신 할 수 없습니다. Using index이 없어도 쿼리에 인덱스가 사용되지 않는다는 의미는 아니며 Using index은 인덱스 자체에 하위 쿼리를 실행할 수있는 충분한 데이터가 포함되어 있음을 의미합니다. target1Nametarget2Name은 색인에 포함되어 있지 않으므로 가져 오는 하위 쿼리는 Using index이 아닙니다.

SELECT a1.target1Id, a1.type, cnt, target1Name, target2Name 
FROM (
    select ae.target1Id, ae.type, count(*) as cnt, MAX(auditEventId) as max_id 
    from AuditEvent ae 
    where ae.clientId=4 
     and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
    group by ae.target1Id, ae.type) as a1 
JOIN AuditEvent a2 ON a1.max_id = a2.auditEventId 
; 
+0

둘 다 실제로 유효한 답변입니다. 나는 쿼리의 성능을 높이기 위해/구조화하는 여러 가지 방법에 대한 조언을 구하는 것이 었습니다. 게다가 인덱스가 아닌 열을 검색하는 가장 좋은 방법은 궁금합니다. 두 가지 제안 모두 내가 시도한 쿼리와 비교하여 성능이 향상되었습니다! –

+0

@nickSpacek, 오케이, 기쁘다. =) – newtover

관련 문제