TSQL 성능-최소 및 최대 값에 합류


10

저장하는 두 개의 테이블이 있습니다.

  • IP 범위-국가 조회 테이블
  • 다른 IP에서 온 요청 목록

bigint조회 성능을 향상시키기 위해 IP를 s로 저장했습니다 .

이것은 테이블 구조입니다.

create table [dbo].[ip2country](
    [begin_ip] [varchar](15) NOT NULL,
    [end_ip] [varchar](15) NOT NULL,
    [begin_num] [bigint] NOT NULL,
    [end_num] [bigint] NOT NULL,
    [IDCountry] [int] NULL,
    constraint [PK_ip2country] PRIMARY KEY CLUSTERED 
    (
        [begin_num] ASC,
        [end_num] ASC
    )
)

create table Request(
    Id int identity primary key, 
    [Date] datetime, 
    IP bigint, 
    CategoryId int
)

국가별로 요청 내역을 얻고 싶습니다. 따라서 다음 쿼리를 수행합니다.

select 
    ic.IDCountry,
    count(r.Id) as CountryCount
from Request r
left join ip2country ic 
  on r.IP between ic.begin_num and ic.end_num
where r.CategoryId = 1
group by ic.IDCountry

테이블에 많은 레코드가 있습니다 : 약 200,000 인치 IP2Country및 수백만 인치 Request이므로 쿼리에 시간이 걸립니다.

실행 계획을 살펴보면 가장 비싼 부분은 인덱스 PK_IP2Country에서 Clustered Index Seek로 여러 번 실행됩니다 (요청의 행 수).

또한, 내가 조금 이상하게 생각하는 left join ip2country ic on r.IP between ic.begin_num and ic.end_num부분 이 부분입니다 (검색을 수행하는 더 좋은 방법이 있는지 모르겠습니다).

테이블 구조, 일부 샘플 데이터 및 쿼리는 SQLFiddle에서 사용할 수 있습니다. http://www.sqlfiddle.com/#!3/a463e/3 (불행히도 문제를 재현하기 위해 많은 레코드를 삽입 할 수는 없다고 생각하지만, 이것은 희망적으로 아이디어를 제공합니다).

나는 SQL 성능 / 최적화 전문가가 아니기 때문에 내 질문은 :이 구조 / 쿼리가 누락 된 성능 측면에서 개선 될 수있는 확실한 방법이 있습니까?


2
IP 주소를 여러 국가에 매핑 할 수 있습니까? 그렇지 않은 경우 PK를로 좁힐 수 있습니다 begin_num. 또한 A BETWEEN B AND C자주 참여해야하며 지루한 RBAR 조인없이이를 달성 할 수있는 방법이 있는지 궁금합니다.
모든 거래의 존

1
그것은 당신의 질문에 약간 주제에서 벗어난,하지만 내가 만드는 생각 하는데요 begin_ipend_ip텍스트와 어떻게 든 동기화 밖으로 점점 숫자의 가능성을 방지하기 위해 열을 계산 지속되었다.
모든 거래의 존

@ w0lf : 겹치는 범위가 ip2country (begin_num, end_num)있습니까?
ypercubeᵀᴹ

@JonofAllTrades 일반적으로 하나의 IP가 단일 국가에 속해야하므로 give me the first record that has a begin_num < ip in asc order of begin_num(잘못된 경우 올바른 수정) 과 같은 쿼리 아이디어 가 유효하고 성능을 향상시킬 수 있다고 생각합니다.
Cristian Lupascu 21

1
@ w0lf : 내 인상은 기본적으로 서버가 먼저 이런 식으로 수행하는 것입니다. 먼저 스캔하여 그 세트 내에서 begin_num스캔하고 end_num하나의 레코드 만 찾습니다.
모든 거래의 존

답변:


3

추가 색인이 필요합니다. 당신의 바이올린 예제에서 나는 다음을 추가했습니다 :

CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)

요청 테이블을 다루며 클러스터형 인덱스 스캔 대신 인덱스 검색을 얻습니다.

그것이 어떻게 향상되는지보고 알려주십시오. 나는 그 색인에 대한 스캔이 저렴하지 않다고 확신하기 때문에 꽤 도움이 될 것이라고 생각합니다.


이유를 모르겠지만 결과는 달라 보입니다 (SQLFiddle에서)
Cristian Lupascu

@ w0lf : 무작위 데이터를 테이블에 삽입하기 때문에 서로 다릅니다.
ypercubeᵀᴹ

@ypercube는 그게 원인입니다. 나는 최근에 너무 많은 일을 해왔 기 때문에 데이터가 무작위라는 것을 잊었다. 죄송합니다.
Cristian Lupascu

2

항상 무차별 접근 방식이 있습니다. IP 맵을 폭발시킬 수 있습니다. IP 주소 당 하나의 레코드를 작성하려면 기존 맵과 숫자 테이블을 결합하십시오. 그것은 당신의 바이올린 데이터를 기반으로 한 267K 레코드에 불과하며 전혀 문제가 없습니다.

CREATE TABLE IPLookup
  (
  IP  BIGINT PRIMARY KEY,
  CountryID  INT
  )
INSERT INTO IPLookup (IP, CountryID)
  SELECT
    N.Number, Existing.IDCountry
  FROM
    ip2country AS Existing
    INNER JOIN Numbers AS N ON N.Number BETWEEN Existing.begin_num AND Existing.end_num

이것은 탐색을 더 단순하고 희망적으로 더 빨리 만들 것입니다. ip2country물론 에 대한 업데이트가 비교적 적은 경우에만 의미가 있습니다 .

다른 사람이 더 나은 해결책을 갖기를 바랍니다.


전체 데이터 세트는 50 억 개가 넘는 레코드를 생성하므로 그렇게하지는 않을 것입니다. 그럼에도 불구하고 이것은 좋은 생각입니다. 나는 많은 유사한 경우에 실현 가능하다고 확신합니다. +1
Cristian Lupascu

0

이 시도:

SELECT ic.IDCountry,
        COUNT(r.Id) AS CountryCount
FROM Request r
INNER JOIN (SELECT begin_num+NUMS.N [IP], IDCountry 
            FROM ip2country
            CROSS JOIN (SELECT TOP(SELECT ABS(MAX(end_num-begin_num)) FROM ip2country) ROW_NUMBER() OVER(ORDER BY sc.name)-1 [N]
                        FROM sys.columns sc) NUMS
            WHERE begin_num+NUMS.N <= end_num) ic
ON r.IP = ic.IP
WHERE r.CategoryId = 1
GROUP BY ic.IDCountry

감사합니다, 귀하의 접근 방식을 시도했지만 초기 쿼리보다 비싸 것 같습니다
Cristian Lupascu

각 테이블에 몇 개의 행이 있습니까? DB에서 문제의 규모를 재현하고 색인을 추가하지 않고 해결하려고합니다. :
Vince Pergolizzi

IP2Country에서 약 200,000 개 및 Request에서 수백만 개 (아마도 수천만 건). 난 당신이 색인없이 그것을 해결하면 당신은 "올해의 DBA"제목을받을 자격이 생각합니다 :)
크리스티안 루파 스쿠
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.