Trik penyetelan kinerja favorit [ditutup]

126

Saat Anda memiliki kueri atau prosedur tersimpan yang membutuhkan penyetelan kinerja, apa saja hal pertama yang Anda coba?

Terrapin
sumber
Berikut adalah beberapa trik Optimasi Kueri SQL Server
SQLMenace
Saya setuju bahwa ini tidak konstruktif dan dapat dicari di Google, tetapi mengapa ia memiliki 118 uv ?! :)
FLICKER

Jawaban:

114

Berikut adalah daftar berguna yang selalu saya berikan kepada seseorang yang bertanya kepada saya tentang pengoptimalan.
Kami terutama menggunakan Sybase, tetapi sebagian besar saran akan berlaku di seluruh papan.

SQL Server, misalnya, dilengkapi dengan sejumlah bit pemantauan / penyetelan kinerja, tetapi jika Anda tidak memiliki yang seperti itu (dan mungkin bahkan jika Anda melakukannya) maka saya akan mempertimbangkan yang berikut ...

99% masalah yang saya lihat disebabkan oleh menempatkan terlalu banyak tabel dalam suatu gabungan . Perbaikan untuk ini adalah dengan melakukan setengah dari gabungan (dengan beberapa tabel) dan menyimpan hasilnya dalam tabel sementara. Kemudian lakukan sisa kueri yang bergabung di tabel sementara itu.

Daftar Periksa Pengoptimalan Permintaan

  • Jalankan STATISTIK PEMBARUAN pada tabel di bawahnya
    • Banyak sistem menjalankan ini sebagai pekerjaan mingguan yang dijadwalkan
  • Hapus catatan dari tabel yang mendasarinya (mungkin arsip catatan yang dihapus)
    • Pertimbangkan untuk melakukan ini secara otomatis sekali sehari atau seminggu sekali.
  • Buat ulang Indeks
  • Rebuild Tables (data bcp keluar / masuk)
  • Buang / muat ulang basis data (drastis, tetapi mungkin memperbaiki korupsi)
  • Buat indeks baru yang lebih tepat
  • Jalankan DBCC untuk melihat apakah ada kemungkinan korupsi dalam database
  • Kunci / Deadlock
    • Pastikan tidak ada proses lain yang berjalan di database
      • Terutama DBCC
    • Apakah Anda menggunakan penguncian tingkat baris atau halaman?
    • Kunci tabel secara eksklusif sebelum memulai kueri
    • Periksa bahwa semua proses mengakses tabel dalam urutan yang sama
  • Apakah indeks digunakan dengan tepat?
    • Bergabung hanya akan menggunakan indeks jika kedua ekspresi sama persis dengan tipe data
    • Indeks hanya akan digunakan jika bidang pertama pada indeks dicocokkan dalam kueri
    • Apakah indeks berkerumun digunakan jika sesuai?
      • rentang data
      • WHERE isian antara value1 dan value2
  • Gabungan Kecil adalah Gabungan Bagus
    • Secara default, pengoptimal hanya akan mempertimbangkan tabel 4 pada satu waktu.
    • Ini berarti bahwa dalam bergabung dengan lebih dari 4 tabel, ia memiliki peluang bagus untuk memilih rencana kueri yang tidak optimal
  • Putuskan Gabung
    • Bisakah kamu memecah gabung?
    • Pra-pilih kunci asing ke dalam tabel sementara
    • Lakukan setengah dari join dan letakkan hasilnya di tabel sementara
  • Apakah Anda menggunakan jenis tabel sementara yang tepat?
    • #temptabel mungkin berkinerja lebih baik daripada @tablevariabel dengan volume besar (ribuan baris).
  • Pertahankan Tabel Ringkasan
    • Bangun dengan pemicu di tabel yang mendasarinya
    • Bangun setiap hari / setiap jam / dll.
    • Bangun ad-hoc
    • Bangun secara bertahap atau teardown / rekondisi
  • Lihat apa rencana kueri dengan SET SHOWPLAN ON
  • Lihat apa yang sebenarnya terjadi dengan SET STATS IO ON
  • Paksa indeks menggunakan pragma: (indeks: myindex)
  • Paksa pesanan tabel menggunakan SET FORCEPLAN ON
  • Parameter Sniffing:
    • Prosedur Break Stored menjadi 2
    • panggil proc2 dari proc1
    • memungkinkan pengoptimal untuk memilih indeks di proc2 jika @parameter telah diubah oleh proc1
  • Bisakah Anda meningkatkan perangkat keras Anda?
  • Jam berapa kamu berlari? Apakah ada waktu yang lebih tenang?
  • Apakah Replication Server (atau proses non-stop lainnya) berjalan? Bisakah Anda menangguhkannya? Jalankan misalnya. per jam?
AJ.
sumber
2
ke bit mana yang Anda maksud?
AJ.
2
Ini adalah beberapa hal yang keren, tetapi saya berharap Anda memiliki beberapa referensi untuk beberapa klaim. Sebagai contoh: Saya belum pernah mendengar optimisasi menganggap hanya 4 tabel dalam satu waktu bergabung. Saya tidak mengerti bagaimana ini bisa benar. Bisakah Anda memberikan beberapa referensi untuk itu? Saya ingin melihat dari mana Anda mendapatkan ini.
SheldonH
19
  1. Punya ide bagus tentang jalur optimal menjalankan kueri di kepala Anda.
  2. Periksa paket kueri - selalu.
  3. Nyalakan STATS, sehingga Anda dapat memeriksa kinerja IO dan CPU. Berfokuslah untuk menurunkan angka-angka itu, tidak harus waktu kueri (seperti yang dapat dipengaruhi oleh aktivitas lain, cache, dll.)
  4. Carilah jumlah besar baris yang masuk ke operator, tetapi jumlah kecil keluar. Biasanya, indeks akan membantu dengan membatasi jumlah baris yang masuk (yang menyimpan disk dibaca).
  5. Fokus pada subtree biaya terbesar pertama. Mengubah subtree itu seringkali dapat mengubah seluruh rencana kueri.
  6. Masalah umum yang saya lihat adalah:
    • Jika ada banyak gabungan, terkadang Sql Server akan memilih untuk memperluas gabungan, dan kemudian menerapkan klausa WHERE. Anda biasanya dapat memperbaikinya dengan memindahkan kondisi WHERE ke klausa JOIN, atau tabel turunan dengan ketentuan yang diuraikan. Tampilan dapat menyebabkan masalah yang sama.
    • Gabungan suboptimal (LOOP vs HASH vs MERGE). Aturan praktis saya adalah menggunakan LOOP bergabung ketika baris atas memiliki sangat sedikit baris dibandingkan dengan bagian bawah, sebuah PENGGABUNGAN ketika set kira-kira sama dan dipesan, dan HASH untuk yang lainnya. Menambahkan petunjuk bergabung akan memungkinkan Anda menguji teori Anda.
    • Parameter mengendus. Jika Anda menjalankan proc yang disimpan dengan nilai-nilai yang tidak realistis pada awalnya (katakanlah, untuk pengujian), maka rencana permintaan dalam cache mungkin suboptimal untuk nilai-nilai produksi Anda. Berjalan kembali DENGAN RECOMPILE harus memverifikasi ini. Untuk beberapa procs yang disimpan, terutama yang berhubungan dengan rentang ukuran yang bervariasi (katakanlah, semua tanggal antara hari ini dan kemarin - yang akan memerlukan INDEKS PENCARIAN - atau, semua tanggal antara tahun lalu dan tahun ini - yang akan lebih baik dengan INDEX SCAN ) Anda mungkin harus menjalankannya DENGAN RECOMPILE setiap saat.
    • Lekukan buruk ... Oke, jadi Sql Server tidak memiliki masalah dengan ini - tapi saya yakin menemukan itu mustahil untuk memahami permintaan sampai saya sudah memperbaiki format.
Mark Brackett
sumber
1
+1 untuk dimasukkannya indentasi buruk. Memformat adalah kunci! :)
mwigdahl
18

Agak topik, tetapi jika Anda memiliki kontrol atas masalah ini ...
Tingkat tinggi dan Dampak Tinggi

  • Untuk lingkungan IO tinggi, pastikan disk Anda untuk RAID 10 atau RAID 0 +1 atau beberapa implementasi raid 1 dan raid 0 yang bersarang.
  • Jangan gunakan drive yang kurang dari 1500K.
  • Pastikan disk Anda hanya digunakan untuk Database Anda. IE no logging no OS.
  • Matikan tumbuh otomatis atau fitur serupa. Biarkan database menggunakan semua penyimpanan yang diantisipasi. Belum tentu apa yang sedang digunakan.
  • desain skema Anda dan indeks untuk tipe permintaan.
  • jika ini adalah tabel jenis log (masukkan saja) dan harus dalam DB jangan indeks itu.
  • jika Anda melakukan banyak pelaporan (kompleks memilih dengan banyak gabungan) maka Anda harus melihat membuat gudang data dengan skema bintang atau kepingan salju.
  • Jangan takut mereplikasi data dengan imbalan kinerja!
jason saldo
sumber
8

CREATE INDEX

Yakinkan ada indeks yang tersedia untuk Anda WHEREdan JOINklausa. Ini akan sangat mempercepat akses data.

Jika lingkungan Anda adalah data mart atau gudang, indeks harus berlimpah untuk hampir semua permintaan yang mungkin.

Dalam lingkungan transaksional , jumlah indeks harus lebih rendah dan definisinya lebih strategis sehingga pemeliharaan indeks tidak menurunkan sumber daya. (Pemeliharaan indeks adalah ketika daun indeks harus diubah untuk mencerminkan perubahan dalam tabel yang mendasarinya, seperti dengan INSERT, UPDATE,dan DELETEoperasi.)

Selain itu, perhatikan urutan bidang dalam indeks - bidang yang lebih selektif (kardinalitas lebih tinggi), yang lebih awal dalam indeks akan muncul. Misalnya, Anda meminta mobil bekas:

SELECT   i.make, i.model, i.price
FROM     dbo.inventory i
WHERE    i.color = 'red'
  AND    i.price BETWEEN 15000 AND 18000

Harga umumnya memiliki kardinalitas yang lebih tinggi. Mungkin hanya ada beberapa lusin warna yang tersedia, tetapi sangat mungkin ribuan harga yang diminta berbeda.

Dari pilihan indeks ini, idx01berikan jalur yang lebih cepat untuk memenuhi kueri:

CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)

Ini karena lebih sedikit mobil yang akan memenuhi titik harga daripada pilihan warna, memberikan mesin kueri data yang jauh lebih sedikit untuk dianalisis.

Saya dikenal memiliki dua indeks yang sangat mirip, hanya berbeda di bidang untuk mempercepat kueri (nama depan, nama belakang) dalam satu dan (nama belakang, nama depan) di yang lain.

Akan SQL untuk Makanan
sumber
6

Trik yang baru-baru ini saya pelajari adalah bahwa SQL Server dapat memperbarui variabel lokal serta bidang, dalam pernyataan pembaruan.

UPDATE table
SET @variable = column = @variable + otherColumn

Atau versi yang lebih mudah dibaca:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

Saya telah menggunakan ini untuk mengganti kursor / gabungan yang rumit ketika menerapkan perhitungan rekursif, dan juga mendapatkan banyak kinerja.

Berikut detail dan contoh kode yang membuat peningkatan fantastis dalam kinerja: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. aspx

jandersson
sumber
5

Dengan asumsi MySQL di sini, gunakan EXPLAIN untuk mencari tahu apa yang terjadi dengan kueri, pastikan bahwa indeks digunakan seefisien mungkin dan mencoba untuk menghilangkan jenis file. MySQL Berkinerja Tinggi: Optimasi, Cadangan, Replikasi, dan Lainnya adalah buku yang hebat tentang topik ini seperti halnya Blog Kinerja MySQL .

davidmytton
sumber
3
Itu bagus untuk MySQL, tetapi pertanyaannya ditandai "sqlserver". Tetap saja, itu hal yang baik untuk dilakukan. Hal analog yang harus dilakukan dalam SSMS adalah menggunakan "Tampilkan Rencana Eksekusi Eksekusi" dan "Sertakan Rencana Eksekusi Aktual". Jika Anda dapat menghilangkan pemindaian tabel besar dan menggunakan pencarian indeks berkerumun, maka Anda berada di jalan menuju kinerja yang optimal.
eksortso
5

@Terrapin ada beberapa perbedaan lain antara isnull dan penggabungan yang layak disebutkan (selain kepatuhan ANSI, yang merupakan hal besar bagi saya).

Coalesce vs IsNull

AlexCuse
sumber
3

Terkadang dalam SQL Server jika Anda menggunakan ATAU di mana klausa itu benar-benar akan mendongkrak kinerja. Alih-alih menggunakan ATAU, lakukan saja dua pemilihan dan satukan keduanya. Anda mendapatkan hasil yang sama dengan kecepatan 1000x.

Ryan
sumber
Saya telah melihat perilaku yang tidak dapat dijelaskan ini.
Esen
2

Lihatlah klausa mana - verifikasi penggunaan indeks / verifikasi tidak ada yang konyol sedang dilakukan

where SomeComplicatedFunctionOf(table.Column) = @param --silly
Mike
sumber
2

Saya biasanya akan mulai dengan gabungan - saya akan mengetuk masing-masing dari kueri satu per satu dan menjalankan kembali kueri untuk mendapatkan ide jika ada gabung tertentu yang bermasalah dengan saya.

John Christensen
sumber
2

Pada semua tabel temp saya, saya ingin menambahkan batasan unik (jika perlu) untuk membuat indeks, dan kunci utama (hampir selalu).

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)
Seibar
sumber
2

Saya sudah terbiasa untuk selalu menggunakan variabel bind. Mungkin variabel terikat tidak akan membantu jika RDBMS tidak men-cache pernyataan SQL. Tetapi jika Anda tidak menggunakan variabel bind, RDBMS tidak memiliki kesempatan untuk menggunakan kembali rencana eksekusi permintaan dan menguraikan pernyataan SQL. Penghematan bisa sangat besar: http://www.akadia.com/services/ora_bind_variables.html . Saya bekerja sebagian besar dengan Oracle, tetapi Microsoft SQL Server bekerja dengan cara yang hampir sama.

Dalam pengalaman saya, jika Anda tidak tahu apakah Anda menggunakan variabel mengikat atau tidak, Anda mungkin tidak. Jika bahasa aplikasi Anda tidak mendukungnya, temukan yang mendukung. Terkadang Anda dapat memperbaiki kueri A dengan menggunakan variabel bind untuk kueri B.

Setelah itu, saya berbicara dengan DBA kami untuk mencari tahu apa yang paling menyebabkan rasa sakit pada RDBMS. Perhatikan bahwa Anda seharusnya tidak bertanya "Mengapa permintaan ini lambat?" Itu seperti meminta dokter Anda untuk mengambil Anda lampiran. Tentu kueri Anda mungkin masalahnya, tetapi kemungkinan besar ada sesuatu yang salah. Sebagai pengembang, kami cenderung berpikir dalam hal baris kode. Jika saluran lambat, perbaiki jalur itu. Tetapi RDBMS adalah sistem yang sangat rumit dan permintaan Anda yang lambat mungkin merupakan gejala dari masalah yang jauh lebih besar.

Terlalu banyak tips tuning SQL adalah berhala pemujaan kargo. Sebagian besar masalah tidak terkait atau minimal terkait dengan sintaks yang Anda gunakan, jadi biasanya yang terbaik adalah menggunakan sintaks terbersih yang Anda bisa. Kemudian Anda bisa mulai mencari cara untuk menyempurnakan basis data (bukan kueri). Hanya men-tweak sintaks ketika itu gagal.

Seperti penyetelan kinerja, selalu kumpulkan statistik yang berarti. Jangan gunakan waktu jam dinding kecuali itu adalah pengalaman pengguna yang sedang Anda setel. Alih-alih melihat hal-hal seperti waktu CPU, baris diambil dan blok pembacaan disk. Terlalu sering orang mengoptimalkan untuk hal yang salah.

Jon Ericson
sumber
2

Langkah pertama: Lihatlah Rencana Eksekusi Kueri!
TableScan ->
NestedLoop buruk -> peringatan meh
TableScan di belakang NestedLoop -> DOOM!

SET STATISTIK IO ON
SET STATISTICS TIME ON

Amy B
sumber
2

Menjalankan kueri menggunakan WITH (NoLock) cukup banyak operasi standar di tempat saya. Siapa pun yang ketahuan menjalankan kueri pada tabel puluhan gigabytes tanpa dikeluarkan dan ditembak.

Valerion
sumber
2
Ini harus digunakan secara bijaksana, bukan kebiasaan. Mengunci bukanlah kejahatan, hanya disalahpahami.
2

Konversikan BUKAN kueri menjadi LEFT OUTER GABUNG jika memungkinkan. Misalnya jika Anda ingin menemukan semua baris di Table1 yang tidak digunakan oleh kunci asing di Table2 Anda bisa melakukan ini:

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

Tetapi Anda mendapatkan kinerja yang jauh lebih baik dengan ini:

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
Martin Brown
sumber
1

@ DavidM

Dengan asumsi MySQL di sini, gunakan EXPLAIN untuk mencari tahu apa yang terjadi dengan kueri, pastikan bahwa indeks digunakan seefisien mungkin ...

Dalam SQL Server, rencana eksekusi memberi Anda hal yang sama - ia memberi tahu Anda indeks apa yang dipukul, dll.

Seibar
sumber
1

Buat indeks tabel berdasarkan clm yang Anda filter

csmba
sumber
1

Tidak harus trik kinerja SQL per se tetapi pasti terkait:

Sebaiknya gunakan memcached jika mungkin karena akan jauh lebih cepat hanya dengan mengambil data yang sudah dikompilasi langsung dari memori daripada mendapatkannya dari database. Ada juga rasa MySQL yang memcached built in (pihak ketiga).

Andy
sumber
1

Pastikan panjang indeks Anda sekecil mungkin. Ini memungkinkan DB untuk membaca lebih banyak kunci sekaligus dari sistem file, sehingga mempercepat penggabungan Anda. Saya menganggap ini bekerja dengan semua DB, tapi saya tahu ini adalah rekomendasi khusus untuk MySQL.

Barrett Conrad
sumber
1

Saya mencari:

  • Buka gulungan loop CURSOR dan konversikan ke dalam pernyataan UPDATE / INSERT berbasis set.
  • Carilah kode aplikasi apa pun yang:
    • Memanggil SP yang mengembalikan set rekaman besar,
    • Kemudian dalam aplikasi, telusuri setiap record dan panggil SP dengan parameter untuk memperbarui record.
    • Ubah ini menjadi SP yang melakukan semua pekerjaan dalam satu transaksi.
  • Setiap SP yang melakukan banyak manipulasi string. Ini bukti bahwa data tidak terstruktur dengan benar / dinormalisasi.
  • Setiap SP yang menciptakan kembali roda.
  • Setiap SP yang saya tidak mengerti apa yang coba dilakukan dalam satu menit!
Orang
sumber
1
SET NOCOUNT ON

Biasanya baris pertama di dalam prosedur tersimpan saya, kecuali saya benar-benar perlu menggunakan @@ROWCOUNT.

travis
sumber
2
@@ ROWCOUNT diatur. NOCOUNT menonaktifkan pernyataan "xx baris yang terpengaruh".
Sklivvz
Apakah ini benar-benar membuat perbedaan kinerja yang cukup besar?
JohnFx
Ya, maka penghitungan tidak dihitung secara otomatis setiap kali pernyataan SQL dijalankan. Cukup mudah untuk membuat kueri dengan dan tanpa melihat apakah itu membuat perbedaan.
travis
Hitungan dilacak di SQL Server. Setiap perbedaan kinerja yang Anda lihat adalah karena jumlah harus melewati jaringan ke ujung depan Anda. Jika Anda melakukan SELECT tunggal, itu tidak akan membuat perbedaan yang berarti. Jika Anda memiliki loop dengan 100000 sisipan, itu jauh lebih banyak melalui jaringan.
Tom H
1

Dalam SQL Server, gunakan direktif nolock. Ini memungkinkan perintah pilih untuk selesai tanpa harus menunggu - biasanya transaksi lainnya selesai.

SELECT * FROM Orders (nolock) where UserName = 'momma'
jinsungy
sumber
3
NOLOCK hanya untuk permintaan yang Anda tidak peduli dengan hasil yang benar
Mark Sowul
1

Hapus kursor di mana pun tidak diperlukan.

Terrapin
sumber
Ya, kursor adalah kutukan! ;)
Sklivvz
8
Ugh. Jangan buang itu tanpa pengecualian seperti itu. Kursor seperti senjata. Mereka tidak buruk dengan diri mereka sendiri, hanya saja orang melakukan hal-hal buruk dengan mereka.
JohnFx
1

Hapus panggilan fungsi di Sprocs di mana banyak baris akan memanggil fungsi.

Kolega saya menggunakan panggilan fungsi (dapatkan lastlogindate dari userid sebagai contoh) untuk mengembalikan recordset yang sangat luas.

Ditugasi dengan pengoptimalan, saya mengganti pemanggilan fungsi dalam sproc dengan kode fungsi: Saya mendapatkan banyak waktu sprocs 'berjalan dari> 20 detik menjadi <1.

Callisto
sumber
0
  • Awali semua tabel dengan dbo. untuk mencegah kompilasi ulang.
  • Lihat paket permintaan dan cari scan tabel / indeks.
  • Pada tahun 2005, menjelajahi pandangan manajemen untuk indeks yang hilang.
Stu
sumber
0

Jangan awali nama Prosedur Tersimpan dengan "sp_" karena semua prosedur sistem dimulai dengan "sp_", dan SQL Server harus mencari lebih keras untuk menemukan prosedur Anda ketika dipanggil.

Terrapin
sumber
1
Apakah Anda benar-benar membandingkan ini? Jika SQL Server melakukan apa yang masuk akal (menggunakan algoritma hash untuk menemukan Stored Proc), maka ini tidak ada bedanya. Bahkan jika SQL Server tidak melakukan itu, sepertinya kinerja sistem akan bau (karena itu mungkin menyebut itu procs sendiri).
John Stauffer
1
Saya pikir ini termasuk dalam optimasi prematur. Ini mungkin merupakan praktik yang baik untuk menghindari kebingungan bagi orang, tetapi sebagai ujung optimasi ... D-
JohnFx
0

Kotor berbunyi -

set transaction isolation level read uncommitted

Mencegah kunci mati di mana integritas transaksional tidak mutlak diperlukan (yang biasanya benar)

Terrapin
sumber
1
Ya, tetapi ini dapat menyebabkan bug aneh yang SANGAT sulit ditemukan.
Grant Johnson
0

Saya selalu pergi ke SQL Profiler (jika ini adalah prosedur tersimpan dengan banyak tingkat bersarang) atau perencana pelaksanaan kueri (jika itu beberapa pernyataan SQL tanpa bersarang) terlebih dahulu. 90% dari waktu Anda dapat segera menemukan masalah dengan salah satu dari dua alat ini.

mwigdahl
sumber