Tối ưu hóa truy vấn cho hơn 25 triệu hàng


11

Tôi đang sử dụng MS SQL và tôi phải chạy một số truy vấn trên cùng một bảng theo các tiêu chí khác nhau. Lúc đầu, tôi chạy từng truy vấn trên bảng gốc mặc dù tất cả chúng đều chia sẻ một số bộ lọc (ví dụ: Ngày, trạng thái). Điều này mất rất nhiều thời gian (khoảng 2 phút).

Có các bản sao trong các hàng dữ liệu và tất cả các chỉ mục đều KHÔNG ĐƯỢC CLUSTERED. Tôi chỉ quan tâm đến 4 cột cho tiêu chí của mình và kết quả sẽ chỉ xuất số đếm cho tất cả các truy vấn.

cột cần thiết: TABLE, FIELD, AFTER, DATE, và có một chỉ mục trên mỗi DATETABLE.

Sau khi tạo một bảng tạm thời chỉ với các trường tôi cần, nó đã giảm xuống còn 1:40 phút, điều này vẫn rất tệ.

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'

Chạy này -> (216598 hàng bị ảnh hưởng)

Vì không phải tất cả các truy vấn đều dựa vào phạm vi ngày, nên tôi đã không đưa nó vào truy vấn. Vấn đề là chỉ mất hơn 1 phút để chèn . Việc chèn ở trên mất 1:19 phút

Tôi muốn chạy một cái gì đó như thế này cho một số truy vấn:

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

Đó là một vấn đề với phần chèn nhiều hơn phần chọn, nhưng temp có ít hàng hơn bảng gốc có thể tốt hơn so với việc đi qua bảng nhiều lần.

Làm thế nào tôi có thể tối ưu hóa điều này?

BIÊN TẬP

Tôi đã xóa ID sắp xếp, tôi nghĩ vấn đề chủ yếu là chọn và không chèn. Đó là một phỏng đoán.

Tôi không thể tạo duy nhất trên bất kỳ chỉ mục nào vì không có trường hoặc hàng duy nhất.

Tôi đang sử dụng SQL Server 2012.

Thông tin bảng : Đó là một đống và có cách sử dụng không gian sau:

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

@MikaelEriksson Tôi không thể sửa đổi các bảng sản xuất ..
Atieh 27/2/2015

Nếu các truy vấn bạn đang cố gắng tối ưu hóa có dạng SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01', tại sao bạn không thử tối ưu hóa từng (truy vấn) riêng biệt? Bạn không được phép thêm chỉ mục vào bảng?
ypercubeᵀᴹ 27/2/2015

2
Bạn cần xác định tại sao nó chậm. Có phải nó đang bị chặn? Có phải nó đang chờ tempdb phát triển? Là kế hoạch thực hiện vực thẳm? Không ai có thể sửa "truy vấn của tôi chậm" mà không có thêm chi tiết ...
Aaron Bertrand

3
Chà, có vẻ như là một nguyên nhân đã mất đối với tôi ( "Tôi không được phép tối ưu hóa bất cứ điều gì, vì vậy, chỉ cần đẩy 200K hàng trong bảng tạm thời mỗi khi chúng tôi cần chạy một số truy vấn" ). Nhưng bạn có thể xóa các cột TABLEFIELDcột khỏi #tempbảng (tất cả các hàng đều có TABLE = 'OTB' AND FIELD = 'STATUS'bảng tạm thời cụ thể.)
ypercubeᵀᴹ 27/2/2015

2
Tôi đã yêu cầu chỉnh sửa và cải tiến bằng cách thêm một nhận xét chi tiết (và lịch sự). Đó là những gì bình luận dành cho. Bạn cũng nên gắn thẻ câu hỏi của mình với phiên bản SQL Server bạn đang sử dụng (ví dụ: SQL Server 2014). DDL cho bảng cũng có thể hữu ích ( CREATE TABLEtuyên bố). Việc bỏ phiếu là vì câu hỏi không rõ ràng.
Paul White 9

Câu trả lời:


12

Câu hỏi chủ yếu là về cách tối ưu hóa câu lệnh chọn:

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

Loại bỏ các phép chiếu dự phòng và thêm dbolược đồ giả định :

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

Không có chỉ mục như ([TABLE],[FIELD]) INCLUDE ([AFTER],[DATE])SQL Server có hai tùy chọn chính:

  1. Quét toàn bộ đống (3GB +); hoặc là
  2. Xác định vị trí khớp hàng [TABLE] = 'OTB'[FIELD] = 'STATUS'(sử dụng IDX6), sau đó thực hiện tra cứu heap (RID) trên mỗi hàng để lấy các cột [AFTER][DATE]cột.

Việc trình tối ưu hóa chọn quét heap hay tìm kiếm chỉ mục với tra cứu RID tùy thuộc vào độ chọn lọc ước tính của [TABLE] = 'OTB'và các [FIELD] = 'STATUS'vị từ. Kiểm tra xem số lượng hàng ước tính từ tìm kiếm có khớp với thực tế không. Nếu không, hãy cập nhật số liệu thống kê của bạn. Kiểm tra truy vấn với một gợi ý bảng buộc sử dụng chỉ mục, nếu điều kiện đó là chọn lọc hợp lý . Nếu trình tối ưu hóa hiện đang chọn tìm kiếm chỉ mục, hãy kiểm tra hiệu năng bằng một INDEX(0)hoặc FORCESCANgợi ý để quét heap.

Ngoài ra, bạn có thể tìm cách cải thiện việc quét heap một chút bằng cách loại bỏ một số dung lượng không sử dụng (370MB). Trong SQL Server 2008, điều này có thể được thực hiện bằng cách xây dựng lại heap. Không gian không được sử dụng trong heap thường là kết quả của việc xóa được thực hiện mà không có khóa bảng được thực hiện (không có khóa bảng, các trang trống không được giải phóng khỏi một đống). Các bảng có trải nghiệm xóa thường xuyên thường được lưu trữ tốt hơn dưới dạng bảng cụm vì lý do này.

Hiệu năng của quét heap phụ thuộc vào số lượng bảng được lưu trữ trong bộ nhớ, phải đọc bao nhiêu từ đĩa, mức độ đầy đủ của các trang, tốc độ lưu trữ liên tục, cho dù quét là I / O hay CPU bị ràng buộc ( song song có thể giúp đỡ).

Nếu hiệu suất vẫn không được chấp nhận sau khi bạn đã điều tra tất cả các điều trên, hãy thử tạo trường hợp cho một chỉ mục mới. Nếu có sẵn trên phiên bản SQL Server của bạn, một chỉ mục được lọc có thể cho truy vấn đã cho sẽ là:

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

Cũng xem xét nén chỉ số, nếu điều đó là có sẵn và có lợi. Nếu không có một chỉ mục mới nào đó, bạn có thể làm tương đối ít để cải thiện hiệu năng của truy vấn đã cho.


Xin lỗi Paul, có : IDX6 nonclustered located on PRIMARY TABLE, FIELD. Có lẽ điều này sẽ thay đổi những điều bạn đề cập?
Atieh

6

Tôi nghĩ rằng có một trường hợp để thay đổi các chỉ mục ở đây vì:

  • bạn có một nhiệm vụ phải làm (nhiều truy vấn này)
  • khối lượng kho dữ liệu (hơn 25 triệu hàng) và
  • một vấn đề hiệu suất.

Đây cũng sẽ là trường hợp sử dụng tốt cho các chỉ mục của cửa hàng cột không được phân cụm được giới thiệu trong SQL Server 2012, tức là tóm tắt / tổng hợp một vài cột trên một bảng lớn có nhiều cột.

Mặc dù các chỉ mục này có tác dụng phụ là làm cho bảng chỉ đọc (ngoại trừ chuyển đổi phân vùng), chúng có thể chuyển đổi hiệu suất của các truy vấn tổng hợp trong các điều kiện phù hợp. Khía cạnh chỉ đọc có thể được quản lý, bằng cách thả và tạo lại chỉ mục hoặc dữ liệu chuyển đổi phân vùng đơn giản vào bảng.

Tôi đã thiết lập một thử nghiệm đơn giản để bắt chước thiết lập của bạn và thấy hiệu suất cải thiện tốt:

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

Kết quả của tôi, 6 giây v 0,08 giây:

nhập mô tả hình ảnh ở đây

Tóm lại, hãy thử và xây dựng trường hợp với sếp của bạn để thay đổi chỉ mục hoặc ít nhất là tạo ra một loại quy trình qua đêm trong đó các bản ghi này được khắc vào bảng báo cáo / cơ sở dữ liệu chỉ đọc nơi bạn có thể thực hiện công việc của mình và thêm lập chỉ mục thích hợp cho khối lượng công việc đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.