Mengoptimalkan kueri untuk 25+ juta baris

11

Saya menggunakan MS SQL, dan saya harus menjalankan beberapa pertanyaan pada tabel yang sama dengan kriteria yang berbeda. Pada awalnya saya menjalankan setiap permintaan pada tabel asli meskipun mereka semua berbagi beberapa pemfilteran (yaitu Tanggal, status). Ini memakan banyak waktu (sekitar 2 menit).

Ada duplikat dalam baris data, dan semua indeks TIDAK BERKELAS. Saya hanya tertarik pada 4 kolom untuk kriteria saya dan hasilnya akan menampilkan hitungan saja, untuk semua pertanyaan.

kolom yang diperlukan: TABLE, FIELD, AFTER, DATE, dan ada indeks pada masing-masing DATEdan TABLE.

Setelah membuat tabel temp dengan hanya bidang yang saya butuhkan, turun menjadi 1:40 menit, yang masih sangat buruk.

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'

Jalankan ini -> (216598 baris terpengaruh)

Karena tidak semua kueri mengandalkan rentang tanggal, saya tidak memasukkannya dalam kueri. Masalahnya adalah hanya butuh di atas 1 menit untuk memasukkan saja . Sisipan di atas memakan waktu 1:19 menit

Saya ingin menjalankan sesuatu seperti ini untuk beberapa pertanyaan:

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

Ini masalah dengan penyisipan lebih dari seleksi, tetapi temp memiliki baris jauh lebih sedikit daripada tabel asli yang bisa lebih baik daripada melalui tabel beberapa kali.

Bagaimana saya bisa mengoptimalkan ini?

EDIT

Saya telah menghapus ID pengurutan, saya pikir masalahnya terutama dengan memilih dan tidak memasukkan. Itu hanya dugaan.

Saya tidak dapat membuat unik pada indeks apa pun karena tidak ada bidang atau baris yang unik.

Saya menggunakan SQL Server 2012.

Info Tabel : Ini tumpukan dan memiliki penggunaan ruang berikut:

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB
Atieh
sumber
@MikaelEriksson Saya tidak bisa memodifikasi tabel produksi ..
Atieh
Jika kueri yang Anda coba optimalkan berbentuk SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01', mengapa Anda tidak mencoba mengoptimalkan masing-masing (kueri) secara terpisah? Apakah Anda tidak diizinkan menambahkan indeks ke tabel?
ypercubeᵀᴹ
2
Anda perlu menentukan mengapa itu lambat. Apakah itu diblokir? Apakah menunggu tempdb untuk tumbuh? Apakah rencana pelaksanaannya terlalu buruk? Tidak ada yang dapat memperbaiki "permintaan saya lambat" tanpa rincian lebih lanjut ...
Aaron Bertrand
3
Yah, sepertinya itu penyebab yang hilang bagi saya ( "Saya tidak diizinkan mengoptimalkan apa pun, jadi mari kita cukup mendorong 200 ribu baris dalam tabel temp setiap kali kita perlu menjalankan beberapa pertanyaan" ). Tapi Anda bisa menghapus kolom TABLEdan FIELDdari #temptabel (semua baris memiliki TABLE = 'OTB' AND FIELD = 'STATUS'untuk tabel temp spesifik setelah semua.)
ypercubeᵀᴹ
2
Saya memang meminta edit dan perbaikan dengan menambahkan komentar rinci (dan sopan). Itulah gunanya komentar. Anda juga harus menandai pertanyaan Anda dengan versi SQL Server yang Anda gunakan (mis. SQL Server 2014). DDL untuk tabel mungkin juga membantu ( CREATE TABLEpernyataan). Pemungutan suara turun karena pertanyaannya tidak jelas.
Paul White 9

Jawaban:

12

Pertanyaannya terutama tentang bagaimana mengoptimalkan pernyataan pilih:

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

Menghapus proyeksi yang berlebihan dan menambahkan dboskema yang diduga :

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

Tanpa indeks seperti ([TABLE],[FIELD]) INCLUDE ([AFTER],[DATE])SQL Server memiliki dua opsi utama:

  1. Pindai tumpukan seluruhnya (3GB +); atau
  2. Temukan baris yang cocok [TABLE] = 'OTB'dan [FIELD] = 'STATUS'(menggunakan IDX6), kemudian lakukan pencarian tumpukan (RID) per baris untuk mengambil [AFTER]dan [DATE]kolom.

Apakah optimizer memilih heap scan atau pencarian indeks dengan pencarian RID tergantung pada selektivitas perkiraan [TABLE] = 'OTB'dan [FIELD] = 'STATUS'predikat. Periksa untuk melihat apakah perkiraan jumlah baris dari pencarian cocok dengan kenyataan. Jika tidak, perbarui statistik Anda. Uji kueri dengan petunjuk tabel yang memaksa penggunaan indeks, jika kondisi itu cukup selektif . Jika pengoptimal saat ini memilih pencarian indeks, uji kinerja dengan INDEX(0)atau FORCESCANpetunjuk untuk memindai tumpukan.

Selain itu, Anda dapat sedikit meningkatkan pemindaian tumpukan dengan menghapus beberapa ruang yang tidak digunakan (370MB). Dalam SQL Server 2008 ini dapat dilakukan dengan membangun kembali heap. Ruang yang tidak terpakai dalam tumpukan sering hasil dari penghapusan yang dilakukan tanpa kunci meja diambil (tanpa kunci meja, halaman kosong tidak dialokasikan dari tumpukan). Tabel yang sering mengalami penghapusan seringkali lebih baik disimpan sebagai tabel berkerumun karena alasan ini.

Kinerja pemindaian tumpukan tergantung pada seberapa banyak tabel disimpan dalam memori, berapa banyak harus dibaca dari disk, seberapa penuh halaman, kecepatan penyimpanan persisten, apakah pemindaian I / O atau terikat CPU ( paralelisme dapat membantu).

Jika kinerja masih tidak dapat diterima setelah Anda menyelidiki semua hal di atas, cobalah membuat case untuk indeks baru. Jika tersedia di versi SQL Server Anda, kemungkinan indeks yang difilter untuk kueri yang diberikan adalah:

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

Juga pertimbangkan kompresi indeks, jika tersedia dan bermanfaat. Tanpa semacam indeks baru, relatif sedikit yang dapat Anda lakukan untuk meningkatkan kinerja kueri yang diberikan.

Paul White 9
sumber
Maaf Paul, ada: IDX6 nonclustered located on PRIMARY TABLE, FIELD. Mungkin ini akan mengubah hal-hal yang Anda sebutkan?
Atieh
6

Saya pikir ada alasan untuk mengubah indeks di sini karena:

  • Anda memiliki tugas yang harus dilakukan (beberapa kueri ini)
  • volume data gudang (25+ juta baris) dan
  • masalah kinerja.

Ini juga akan menjadi kasus penggunaan yang baik untuk indeks columnstore non-clustered diperkenalkan di SQL Server 2012, yaitu meringkas / mengagregasi beberapa kolom pada tabel besar dengan banyak kolom.

Meskipun indeks ini memiliki efek samping dari membuat tabel hanya-baca (dengan pengecualian beralih partisi), mereka dapat mengubah kinerja kueri agregat di bawah kondisi yang tepat. Aspek read-only dapat dikelola, baik dengan menjatuhkan dan membuat ulang indeks atau data partisi sederhana beralih ke tabel.

Saya menyiapkan rig uji sederhana untuk meniru pengaturan Anda, dan melihat peningkatan kinerja yang baik:

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

Hasil saya, 6 detik v 0,08 detik:

masukkan deskripsi gambar di sini

Ringkasnya, coba dan bangun kasing dengan atasan Anda agar indeksnya diubah atau setidaknya buat semacam proses semalaman di mana catatan-catatan ini diukir menjadi tabel / basis data pelaporan hanya-baca tempat Anda dapat melakukan pekerjaan Anda, dan menambahkan pengindeksan sesuai untuk beban kerja itu.

wBob
sumber