SQL: Apa yang memperlambat INSERT jika bukan CPU atau IO?

19

Kami memiliki database untuk produk yang berat-menulis. Kami baru saja membeli mesin server baru dengan SSD untuk membantu. Yang mengejutkan kami, pemasangannya tidak lebih cepat dari pada mesin lama kami dengan penyimpanan yang jauh lebih lambat. Selama benchmarking kami perhatikan bahwa tingkat IO yang ditunjukkan oleh proses SQL Server sangat rendah.

Misalnya, saya menjalankan skrip yang ditemukan di halaman ini , kecuali bahwa saya menambahkan BEGIN TRAN dan COMMIT di sekitar loop. Paling-paling saya bisa melihat penggunaan disk mencapai 7Mb / s, sementara CPU hampir tidak menyentuh 5%. Server telah menginstal 64Gb dan menggunakan 10. Waktu menjalankan total adalah 2 menit 15 detik untuk panggilan pertama turun menjadi sekitar 1 menit untuk panggilan berikutnya. Basis data sedang dalam pemulihan sederhana dan tidak digunakan selama pengujian. Saya menjatuhkan meja di antara setiap panggilan.

Mengapa skrip sederhana ini begitu lambat? Perangkat kerasnya hampir tidak digunakan sama sekali. Kedua alat pembandingan disk khusus dan SQLIO menunjukkan bahwa SSD berkinerja dengan benar dengan kecepatan hingga 500Mb / dtk untuk membaca dan menulis. Saya mengerti bahwa menulis acak lebih lambat dari menulis berurutan, tapi saya berharap insert sederhana seperti ini, ke tabel tanpa pengindeksan berkelompok, menjadi jauh lebih cepat.

Pada akhirnya skenario kami jauh lebih kompleks, tetapi saya merasa perlu memahami kasus sederhana terlebih dahulu. Singkatnya aplikasi kita menghapus data lama, kemudian menggunakan SqlBulkCopy untuk menyalin data baru ke staging tables, melakukan beberapa penyaringan, dan akhirnya menggunakan MERGE dan / atau INSERT INTO tergantung pada kasus untuk menyalin data ke tabel akhir.

-> EDIT 1: Saya mengikuti prosedur yang ditautkan oleh Martin Smith, dan saya mendapatkan hasil sebagai berikut:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

Saya merasa aneh NETWORK_IO mengambil sebagian besar waktu, mengingat tidak ada hasil untuk ditampilkan dan tidak ada data untuk ditransfer di mana pun selain ke file SQL. Apakah tipe NETWORK_IO mencakup semua IO?

-> EDIT 2: Saya membuat disk RAM 20Gb dan memasang basis data dari sana. Waktu terbaik yang saya miliki di SSD adalah 48 detik, dengan disk RAM turun menjadi 37 detik. NETWORK_IO masih menunggu terbesar. Kecepatan tulis maksimum ke disk RAM adalah sekitar 250Mb / s sementara itu mampu melakukan multi gigabytes per detik. Masih tidak menggunakan banyak CPU, jadi apa yang menahan SQL?

Djof
sumber
1
Anda dapat menggunakan skrip di sini untuk menangkap waitstats untuk spid tertentu . Saya akan menambahkannya SET NOCOUNT ONjuga.
Martin Smith
3
yang NETWORK_IOmungkin dari 3 juta "1 baris (s) terpengaruh" pesan-pesan yang dikirim kembali. Apakah Anda mencoba menambahkan SET NOCOUNT ONke skrip?
Martin Smith
Ya, saya menambahkan NOCOUNT.
Djof
2
Aneh. Saya tidak akan berharap banyak dalam hal aktivitas jaringan sama sekali. Apakah Anda menghapus file acara lama yang diperluas antara berjalan? Script yang membacanya menggunakan wild card EE_WaitStats*.xelsehingga yang lama akan mencemari hasil Anda.
Martin Smith
Panggilan bagus, saya akan memperbarui hasilnya besok.
Djof

Jawaban:

9

Saya tahu ini adalah pertanyaan lama tetapi ini mungkin masih membantu para pencari dan ini adalah masalah yang muncul setiap saat.

Alasan utama mengapa Anda menekan langit-langit kinerja tanpa Anda melihat hambatan sumber daya adalah karena Anda telah mencapai batas apa yang mungkin untuk diproses dalam satu thread tunggal sesi. Loop tidak diproses secara paralel, tetapi semua insert dilakukan secara serial.

Dalam kasus saya, dibutuhkan 36 detik untuk memasukkan 3 juta baris. Itu berarti 36/30000000 = 0,000012 detik per baris. Itu cukup cepat. Di sistem saya, hanya dibutuhkan 0,000012 untuk melalui semua langkah yang diperlukan.

Satu-satunya cara untuk menyelesaikannya lebih cepat adalah memulai sesi kedua secara paralel.

Jika saya memulai 2 sesi secara paralel, keduanya melakukan 15 juta sisipan. Keduanya selesai dalam 18 detik. Saya dapat meningkatkan skala, tetapi pengaturan pengujian saya saat ini mencapai 95% cpu dengan dua sesi paralel, jadi melakukan 3 akan memusingkan hasilnya karena saya akan mengalami hambatan CPU.

Jika saya memulai 2 sesi paralel keduanya menyisipkan 3 juta baris, keduanya selesai dalam 39 detik. jadi sekarang 6 juta baris dalam 39 detik.

Oke, itu masih meninggalkan kita dengan menunggu NETWORK_IO muncul.

Tunggu NETWORK_IO ditambahkan oleh fakta bahwa Anda menggunakan peristiwa yang diperluas untuk melacaknya. Dalam kasus saya, insert membutuhkan waktu 36 detik (rata-rata). Saat menggunakan cara acara yang diperluas (dari tautan di atas di komentar pertama) inilah yang terdaftar:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

Anda dapat melihat bahwa 68 detik NETWORK_IO terdaftar. Tetapi karena loop insert adalah tindakan berulir tunggal yang memerlukan waktu 36 detik, ini tidak mungkin. (Ya, banyak utas digunakan, tetapi operasinya serial, tidak pernah paralel, sehingga Anda tidak dapat mengakumulasi lebih banyak waktu tunggu daripada total durasi kueri)

Jika saya tidak menggunakan acara yang diperpanjang tetapi hanya menunggu DMVs statistik pada contoh yang tenang (hanya dengan saya menjalankan sisipan) Saya mendapatkan ini:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

Jadi NETWORK_IO yang Anda lihat di log peristiwa yang diperluas, tidak terkait dengan loop sisipan Anda. (Jika Anda tidak mengaktifkan nocount, Anda akan memiliki IO jaringan async besar, +1 Martin)

Namun saya tidak tahu mengapa NETWORK_IO muncul di jejak acara yang diperluas. Tentu saja penulisan target file async dari peristiwa tersebut mengakumulasi ASYNC_NETWORK_IO, tetapi tentunya ini semua dilakukan pada SPID yang berbeda dari yang kami filter. Saya mungkin mengajukan ini sebagai pertanyaan baru sendiri)

Edward Dortland
sumber
1
"Anda memukul langit-langit kinerja tanpa Anda melihat hambatan sumber daya adalah karena Anda telah mencapai batas apa yang mungkin untuk diproses dalam satu thread tunggal sesi": Anda menggambarkan hambatan CPU 100% (pada satu inti). Jika tidak ada hambatan, maka sistem akan berjalan lebih cepat, jadi sesuatu yang lain harus dimainkan.
Remus Rusanu
Jawaban Anda sangat informatif bagi Edward. Kelihatannya paralelisme adalah solusi untuk masalah kita, kita sudah mengatasinya, meskipun itu membutuhkan perubahan pada tata letak basis data kita. Seperti halnya Remus, saya masih penasaran mengapa mesin itu tampaknya tidak menggunakan semua (satu) sumber daya CPU atau disk.
Djof
9

Biasanya Anda mulai dengan melihat sys.dm_exec_requests, khususnya pada wait_time, wait_typedan wait_resourceuntuk permintaan INSERT Anda. Ini akan memberikan indikasi yang jelas apa yang menghalangi INSERT Anda. Hasil akan menunjukkan apakah pertikaian kunci, peristiwa pertumbuhan file, menunggu log flush, pertikaian alokasi (dimanifestasikan sebagai pertengkaran kait halaman PFS) dll. Dll. Setelah Anda mengukur, perbarui pertanyaan Anda sesuai. Saya sangat menyarankan Anda untuk berhenti sekarang dan membaca metodologi pemecahan masalah Tunggu dan Antrian sebelum Anda melanjutkan.

Remus Rusanu
sumber
3

Saya menjalankan skrip uji pada halaman yang terhubung dalam OP dengan BEGIN TRAN / COMMIT di sekitar loop. Di komputer saya, dibutuhkan 1:28 untuk menyelesaikan pertama kalinya.

Lalu saya memindahkan dua perintah ini di luar loop:

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

Itu selesai dalam 28 detik setelah itu.

Saya tidak tahu pasti apa yang terjadi, tapi saya kira mungkin ada semacam tidur dalam RAND()kode, mungkin sebagai bagian dari algoritma yang mereka gunakan untuk menghasilkan entropi (angka acak yang lebih baik).

FWIW, SSD tidak selalu merupakan teknologi terbaik untuk aplikasi yang berat. Untuk kinerja terbaik, pastikan log DB Anda menggunakan huruf drive berbeda dari data DB, file log sudah dipra-tumbuh hingga ukuran maksimumnya, dan jangan pernah memotong log.

RickNZ
sumber
Terima kasih atas masukan Anda, RickNZ. Saya tidak mendapatkan hasil yang lebih cepat dengan memindahkan kode dari loop. Tunggu, yang saya amati adalah bahwa jika Anda menjalankannya beberapa kali, ia menjadi lebih cepat, itu mungkin yang Anda alami. Saya tahu SSD bukan peluru perak, tapi saya masih merasa kinerjanya bukan seperti itu.
Djof
1

DMV lain yang saya gunakan untuk mengidentifikasi kelambatan adalah sys.dm_os_waiting_tasks . Jika permintaan Anda bukan CPU intensif, maka Anda dapat menemukan informasi lebih lanjut tentang menunggu dari DMV ini.

StanleyJohns
sumber
0

Saya memeriksa daftar acara tunggu untuk sql 2008 dan saya tidak melihat NETWORK_IO terdaftar: http://technet.microsoft.com/en-us/library/ms179984(v=sql.100).aspx

Saya pikir NETWORK_IO sekarang baru saja terdaftar sebagai ASYNC_NETWORK_IO, jadi saya ingin bertanya apakah Anda dapat memeriksa versi SQL Anda lagi, karena saya hanya ingin tahu bagaimana / mengapa acara tunggu itu muncul untuk versi itu.

Adapun menunggu jaringan muncul sama sekali, ya itu bisa terjadi bahkan jika Anda bekerja pada server mandiri. Sudahkah Anda memeriksa pengaturan untuk kartu jaringan Anda? Saya bertanya-tanya apakah itu masalah.

Pada akhirnya, hanya ada beberapa hambatan sumber daya yang mungkin: memori, CPU, disk I / O, jaringan, dan penguncian. Anda telah mengindikasikan bahwa CPU dan I / O bukan masalahnya, dan Anda memiliki acara tunggu NETWORK_IO, jadi saya sarankan Anda melihat kartu NIC tersebut terlebih dahulu.

SQLRockstar
sumber
1
Ini NETWORK_IOditampilkan karena OP menggunakan acara yang diperluas. Itu tidak pernah diperbarui disys.dm_xe_map_values
Martin Smith
Saya sedang memikirkan SQLRockstar yang sama, hanya apa yang bisa terjadi. Saya memang mencoba menonaktifkan kartu jaringan sepenuhnya. Martin menunjukkan bahwa beberapa file lama mungkin masih ada di sana. Saya akan memperbarui hasilnya besok untuk melihat apakah ada perubahan.
Djof
juga, mungkin membantu jika kita dapat melihat rencana eksekusi untuk pernyataan.
SQLRockstar