2017-11-15 1 views
1

우리는 VoIP 공급자로부터 "트렁크"를 구입할 수 있으며 트렁크를 통과 할 때마다 분당 요금이 부과됩니다 (예쁜 페니). 공급자는보고 기능을 제공하지 않으므로 어떤 트렁크를 사용해야하는지 추측하고 있으며 빈번하게 추측하고 있습니다. 그래서, 우리는 그것의 모든 통화 기록을 가지고있는 DB를 셋업했습니다. 그런 다음 "무료"전화 (행)를 완료하는 데 필요한 많은 트렁크를 알려주는 SQL 쿼리를 만들었습니다. 내가 사용하고있는 쿼리는 다음과 같습니다.중첩 된 mySQL 쿼리 최적화 ... 또는 무기한 실행

USE cdrs; 
CREATE TEMPORARY TABLE IF NOT EXISTS cdr_temp 
AS (
    SELECT callrecords.Timestamp, callrecords.CallEnd, callrecords.CallDirection, callrecords.Rate 
    FROM cdrs.callrecords 
); 

UPDATE cdrs.callrecords AS a 
SET TrunksNeeded = (
    select count(CallID) 
    FROM cdr_temp AS b 
    WHERE b.Timestamp <= a.Timestamp 
    AND b.CallEnd >= a.Timestamp 
    AND b.CallDirection = a.CallDirection 
    AND b.Rate > 0 
) 
WHERE TrunksNeeded IS NULL AND Rate > 0 
LIMIT 50; 

DROP TEMPORARY TABLE IF EXISTS cdr_temp; 

알림 50 레코드는 50-80 초입니다. 색인을 사용하여 최적화를 시도했습니다. 그러나 내가하는 일은 도움이되지 않는다. 다음은 쇼 테이블 덤프입니다.

CREATE TABLE 'callrecords' (
    'Timestamp' datetime DEFAULT NULL, 
    'AccountID' varchar(45) DEFAULT NULL, 
    'CNAME' varchar(45) DEFAULT NULL, 
    'To' varchar(255) DEFAULT NULL, 
    'From' varchar(255) DEFAULT NULL, 
    'CallDirection' varchar(45) DEFAULT NULL, 
    'hangup_cause' varchar(45) DEFAULT NULL, 
    'BillingSeconds' int(11) DEFAULT NULL, 
    'DurationSeconds' int(11) DEFAULT NULL, 
    'Rate' float DEFAULT NULL, 
    'RateName' varchar(45) DEFAULT NULL, 
    'Cost' float DEFAULT NULL, 
    'CallID' varchar(255) DEFAULT NULL, 
    'CallEnd' datetime DEFAULT NULL, 
    'TrunksNeeded' int(11) DEFAULT NULL, 
    KEY 'idx_calldata' ('Timestamp','CallEnd','CallDirection','Rate') 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

90 일간의 통화 로그를 나타내는 DB에는 약 150 만 개의 레코드가 있습니다. 그리고 약 400k는 0을 초과합니다. 의미는 청구 가능하고 비 내부 통화입니다.

두 가지 질문이 있습니다.

1) 쿼리를 빠르게 실행하기 위해 사용하고있는 테이블이나 쿼리를 변경하는 쉬운 방법이 있습니까?

2) 계산하지 못하면 30 일 분량의 레코드에 대해 쿼리를 실행하는 데 5 일이 걸립니다. 나는 그것이 미친 소리가 난다는 것을 알고있다. 그러나 내년 또는 그렇게 최소한, 나는 그것으로 OK이다. 이 명령을 발행하여 백그라운드에서 완료하고 제한 시간을 무시하는 방법이 있습니까?

EDIT : 임시 테이블에 @Sentinel 권장 색인을 추가하면 많은 도움이됩니다. 또한, 내 하드 디스크가 최대 밖으로 나타났습니다. 그래서 임시 DB를 메모리에 두었습니다. 쿼리가 실행되는 데 하루가 조금 걸릴 것 같습니다. 하지만 난 여전히 ... 나는 그렇게 오래에 대한 쿼리를 실행하도록 할 수 있습니다 방법에 대한 질문을

업데이트 SQL 쿼리를 왼쪽 해요 :

당신이 잘 인스턴스화에서 소비 될 수 있습니다보고 시간의
USE cdrs; 

CREATE TEMPORARY TABLE IF NOT EXISTS cdr_temp ENGINE=MEMORY 
AS (
    SELECT callrecords.Timestamp, callrecords.CallEnd, callrecords.CallDirection, callrecords.Rate 
    FROM cdrs.callrecords 
); 
alter table cdr_temp add index idx1 (CallDirection, rate, timestamp, callend); 

UPDATE cdrs.callrecords AS a 
SET TrunksNeeded = (
    select count(CallID) 
    FROM cdr_temp AS b 
    WHERE b.Timestamp <= a.Timestamp 
    AND b.CallEnd >= a.Timestamp 
    AND b.CallDirection = a.CallDirection 
    AND b.Rate <> 0 
) 
WHERE TrunksNeeded IS NULL AND Rate <> 0 
ORDER BY Timestamp 
LIMIT 5000; 

DROP TEMPORARY TABLE IF EXISTS cdr_temp; 
+0

느린'UPDATE' 교화하는 더 좋은 방법 : http://mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks 당신이 전화를 사전 필터링에 의해 몇 가지 추가 성능 향상을 짜낼 수 있습니다 –

+0

을 cdr_temp 테이블을 인스턴스화 할 때 기록합니다. 구체적으로'cdrs.callrecords에서 ... rate <> 0'을 선택하여'SET TrunksNeeded = (... ... ')에서 작업하고있는 레코드 수를 줄일 수 있습니다.)'subquery – Sentinel

답변

1

많은 성능 향상을 위해 인덱스가없는 임시 테이블 cdr_temp. 당신이에 인덱스가 있다면 당신은 또한 쿼리 최적화 이후 b.Rate <> 0 테스트 대신 b.Rate > 0 특히 (CallDirection, 속도) 더 나은 성능을 얻을 수 있습니다

UPDATE cdrs.callrecords AS a 
SET TrunksNeeded = (
    select count(CallID) 
    FROM cdrs.callrecords AS b 
    WHERE b.Timestamp <= a.Timestamp 
    AND b.CallEnd >= a.Timestamp 
    AND b.CallDirection = a.CallDirection 
    AND b.Rate <> 0 
) 
WHERE TrunksNeeded IS NULL AND Rate > 0 
LIMIT 50; 

:

는 임시 테이블을 사용하지 않는 시도 되세요/planner는 겹치는 호출을 검사하는 데 필요한 범위 검사를 수행하기 전에 더 많은 레코드를 제거 할 수 있습니다.

은 임시 테이블을 유지하고 여기에 인덱스를 추가 (개정 열 순서에주의)하려면

alter table cdr_temp add index idx1 (CallDirection, rate, timestamp, callend); 

이 새로운 인덱스, 임시 테이블을 사용하고 내가 추천 코드 변경이 있습니다.

UPDATE callrecords AS a 
SET TrunksNeeded = (
    select count(CallID) 
    FROM cdr_temp AS b 
    WHERE b.CallDirection = a.CallDirection 
    AND b.Rate <> 0 
    AND a.Timestamp BETWEEN b.Timestamp AND b.CallEnd 
) 
WHERE TrunksNeeded IS NULL AND Rate > 0 
LIMIT 50; 
+0

네, 저의 원래 생각이었습니다. 하지만 ... 실행하려고하면이 오류가 발생합니다. 오류 코드 : 1093. FROM 절의 업데이트 대상 테이블 'a'를 지정할 수 없습니다. \t 0.000 초 – Rick

+0

생성시 인덱스를 복사 할 수 있습니까? 임시 테이블 어쩌면 ?? – Rick

+0

임시 테이블에 인덱스를 추가하려고 시도했지만 성능이 저하 될 수 있습니다. 존재하지 않는 임시 테이블 만들기 cdr_temp LIKE cdrs.callrecords; INSERT cdr_temp SELECT * FRr cdrs.callrecords; 기존 인덱스를 복사하는 대신 백그라운드에서 인덱스를 다시 만들고 있기 때문에 가능합니까? – Rick