2,500 만 개 이상의 행에 대한 쿼리 최적화


11

MS SQL을 사용하고 있으며 동일한 기준으로 다른 기준으로 여러 쿼리를 실행해야합니다. 처음에는 원래 테이블에서 각 쿼리를 실행했지만 모두 일부 필터링 (예 : 날짜, 상태)을 공유합니다. 시간이 많이 걸렸습니다 (약 2 분).

데이터 행에 중복이 있으며 모든 인덱스가 비 클러스터형입니다. 내 기준으로 4 열에 만 관심이 있으며 결과는 모든 쿼리에 대해서만 카운트를 출력해야합니다.

열이 필요 : TABLE, FIELD, AFTER, DATE, 그리고 각각에 인덱스가 DATE와가 TABLE.

필요한 필드만으로 임시 테이블을 만든 후에는 1:40 분으로 줄어 들었지만 여전히 매우 나쁩니다.

CREATE TABLE #TEMP
(
    TABLE VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    AFTER VARCHAR(1000) NULL,
    DATE DATETIME,
    SORT_ID INT IDENTITY(1,1)
)
CREATE CLUSTERED INDEX IX_ADT ON #TEMP(SORT_ID)

INSERT INTO #TEMP (TABLE, FIELD, AFTER, DATE)
    SELECT TABLE, FIELD, AFTER, DATE 
    FROM mytbl WITH (NOLOCK)
    WHERE TABLE = 'OTB' AND
    FIELD = 'STATUS'

Runnig this-> (216598 행 영향을 받음)

모든 검색어가 날짜 범위에 의존하는 것은 아니기 때문에 검색어에 포함시키지 않았습니다. 문제는 삽입하는 데 1 분 이상 걸리는 것 입니다. 위의 인서트는 1:19 분이 걸렸습니다.

여러 쿼리에 대해 이와 같은 것을 실행하고 싶습니다.

SELECT COUNT(*) AS COUNT
FROM #TEMP
WHERE AFTER = 'R' AND
DATE >= '2014-01-01' AND
DATE <= '2015-01-01'

선택 항목보다 삽입에 문제가 있지만 임시 테이블은 원래 테이블보다 행 수가 적으므로 테이블을 여러 번 통과하는 것보다 낫습니다.

이것을 어떻게 최적화 할 수 있습니까?

편집하다

정렬 ID를 제거했지만 문제는 주로 선택이 아니라 삽입이라고 생각했습니다. 추측이었다.

고유 필드 또는 행이 없기 때문에 모든 인덱스에서 고유를 만들 수 없습니다.

SQL Server 2012를 사용하고 있습니다.

테이블 정보 : 힙이며 다음과 같은 공간 사용량이 있습니다.

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB

@MikaelEriksson 생산 테이블을 수정할 수 없습니다.
Atieh

최적화하려는 쿼리가 형식 인 SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01'경우 각 쿼리를 개별적으로 최적화하려고하지 않습니까? 테이블에 인덱스를 추가 할 수 없습니까?
ypercubeᵀᴹ

2
왜 느린 지 결정해야합니다. 차단되고 있습니까? tempdb가 커지기를 기다리고 있습니까? 실행 계획이 심하지 않습니까? 더 자세한 정보없이 "내 쿼리 속도가 느림"을 해결할 수있는 사람은 없습니다.
Aaron Bertrand

3
글쎄, 나에게 잃어버린 원인 인 것 같습니다 ( "아무것도 최적화 할 수 없으므로 쿼리를 실행해야 할 때마다 임시 테이블에서 200K 행을 푸시하십시오" ). 그러나 테이블 에서 TABLEFIELD열을 제거 할 수 있습니다 #temp(결국 모든 TABLE = 'OTB' AND FIELD = 'STATUS'임시 테이블에 대해 모든 행이 있음 )
ypercubeᵀᴹ

2
자세한 (그리고 공손한) 의견을 추가하여 편집 및 개선을 요청했습니다. 이것이 바로 의견입니다. 또한 사용중인 SQL Server 버전 (예 : SQL Server 2014)으로 질문에 태그를 지정해야합니다. 테이블에 대한 DDL도 도움이 될 수 있습니다 ( CREATE TABLE문). 반대 투표는 그 질문이 명확하지 않았기 때문입니다.
Paul White 9

답변:


12

질문은 주로 select 문을 최적화하는 방법에 관한 것입니다.

SELECT [TABLE], [FIELD], [AFTER], [DATE]
FROM mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB' AND
[FIELD] = 'STATUS'

중복 투영 제거 및 추정 된 dbo스키마 추가 :

SELECT [AFTER], [DATE] 
FROM dbo.mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB'
AND FIELD = 'STATUS';

([TABLE],[FIELD]) INCLUDE ([AFTER],[DATE])SQL Server 와 같은 인덱스가 없으면 두 가지 주요 옵션이 있습니다.

  1. 힙을 완전히 스캔하십시오 (3GB +). 또는
  2. 을 사용 [TABLE] = 'OTB'하고 일치하는 행을 찾은 다음 행당 힙 (RID) 조회를 수행 하여 및 열 을 검색하십시오 .[FIELD] = 'STATUS'IDX6[AFTER][DATE]

옵티마이 저가 RID 조회를 사용하여 힙 스캔 또는 인덱스 검색을 선택하는지 여부는 [TABLE] = 'OTB'[FIELD] = 'STATUS'술어 의 예상 선택도에 따라 다릅니다 . 검색에서 예상 행 수가 실제와 일치하는지 확인하십시오. 그렇지 않은 경우 통계를 업데이트하십시오. 해당 조건이 선택적인 경우 인덱스 사용을 강요하는 테이블 힌트로 쿼리를 테스트하십시오 . 옵티마이 저가 현재 인덱스 탐색을 선택하는 경우 INDEX(0)또는 FORCESCAN힌트로 성능을 테스트 하여 힙을 스캔하십시오.

그 외에도 사용되지 않은 공간 (370MB)을 제거하여 힙 스캔을 약간 개선 할 수 있습니다. SQL Server 2008에서는 힙을 다시 작성하여이 작업을 수행 할 수 있습니다. 힙에서 사용되지 않은 공간은 종종 테이블 잠금을 수행하지 않고 수행 된 삭제로 인해 발생합니다 (테이블 잠금없이 빈 페이지는 힙에서 할당 해제되지 않음). 이러한 이유로 자주 삭제되는 테이블은 클러스터 된 테이블로 저장하는 것이 좋습니다.

힙 스캔의 성능은 메모리에 저장되어있는 테이블의 양, 디스크에서 읽어야하는 양, 페이지가 가득 참, 영구 스토리지의 속도, 스캔이 I / O 또는 CPU 바운드인지에 따라 다릅니다 ( 병렬 처리가 도움이 될 수 있습니다).

위의 모든 사항을 조사한 후에도 여전히 성능이 만족스럽지 않으면 새 색인을 작성하십시오. SQL Server 버전에서 사용 가능한 경우 지정된 쿼리에 대해 필터링 가능한 인덱스는 다음과 같습니다.

CREATE INDEX index_name
ON dbo.mytbl ([DATE],[AFTER])
WHERE [TABLE] = 'OTB'
AND [FIELD] = 'STATUS';

가능하고 유익한 경우 인덱스 압축도 고려하십시오. 새로운 종류의 인덱스가 없으면 주어진 쿼리의 성능을 향상시키기 위해 할 수있는 일이 상대적으로 적습니다.


죄송합니다. Paul이 IDX6 nonclustered located on PRIMARY TABLE, FIELD있습니다. 아마도 이것은 당신이 언급 한 것을 바꿀 것입니까?
Atieh

6

다음과 같은 이유로 색인을 변경하는 경우가 있다고 생각합니다.

  • 할 일이 있습니다 (이러한 여러 쿼리)
  • 데이터웨어 하우스 볼륨 (2,500 만 행)
  • 성능 문제.

이는 SQL Server 2012에 도입 된 비 클러스터형 열 저장소 인덱스의 경우에도 유용합니다. 즉, 많은 열이있는 큰 테이블에서 몇 개의 열을 요약 / 집계합니다.

이러한 인덱스는 테이블을 읽기 전용 (파티션 전환 제외)으로 만드는 부작용이 있지만 올바른 조건에서 집계 쿼리 성능을 변환 할 수 있습니다. 인덱스 또는 단순 파티션 스위치 데이터를 테이블에 삭제하고 다시 작성하여 읽기 전용 측면을 관리 할 수 ​​있습니다.

설정을 모방하기 위해 간단한 테스트 장비를 설정했으며 성능이 크게 향상되었습니다.

USE tempdb
GO

SET NOCOUNT ON
GO

-- Create a large table
IF OBJECT_ID('dbo.largeTable') IS NOT NULL
DROP TABLE dbo.largeTable
GO
CREATE TABLE dbo.largeTable ( 

    [TABLE] VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    [AFTER] VARCHAR(1000) NULL,
    [DATE] DATETIME,
    SORT_ID INT IDENTITY(1,1),

    pad VARCHAR(100) DEFAULT REPLICATE( '$', 100 )
)
GO

-- Populate table
;WITH cte AS (
SELECT TOP 100000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
SELECT 
    x.tableName, 
    y.field,
    z.[after],
    DATEADD( day, rn % 1111, '1 Jan 2012' )
FROM cte c
    CROSS JOIN ( VALUES ( 'OTB' ), ( 'AAA' ), ( 'BBB' ), ( 'CCCC' ) ) x ( tableName )
    CROSS JOIN ( VALUES ( 'STATUS' ), ( 'TIME' ), ( 'POWER' ) ) y ( field )
    CROSS JOIN ( VALUES ( 'R' ), ( 'X' ), ( 'Z' ), ( 'A' ) ) z ( [after] )

CHECKPOINT

GO 5

EXEC sp_spaceused 'dbo.largeTable'
GO

SELECT MIN([DATE]) xmin, MAX([DATE]) xmax, FORMAT( COUNT(*), '#,#' ) records
FROM dbo.largeTable
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff1
GO

-- Add the non-clustered columnstore
CREATE NONCLUSTERED COLUMNSTORE INDEX _cs ON dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

-- Check query again
DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff2
GO

내 결과, 6 초 v 0.08 초 :

여기에 이미지 설명을 입력하십시오

요약하자면, 상사와 함께 사건을 수정하여 색인을 변경하거나 최소한 이러한 종류의 야간 프로세스를 작성하여 작업을 수행 할 수있는 읽기 전용보고 테이블 / 데이터베이스에 기록하고 색인을 추가하십시오. 해당 워크로드에 적합합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.