Pada salah satu pelanggan kami, kami mengalami beberapa masalah kinerja pada aplikasi kami. Ini adalah aplikasi web .NET 3.5 yang mengkonsumsi dan memperbarui data pada database SQL Server. Saat ini lingkungan produksi kami terdiri dari mesin Windows 2008 R2 sebagai ujung depan, dan kluster SQL Server 2008 R2 di ujung belakang. Aplikasi kami menggunakan COM + dan MSDTC untuk terhubung ke database.
Inilah yang terjadi: pengguna akhir kami kadang mengeluh kelambatan dalam aplikasi. Beberapa halaman membutuhkan waktu lebih lama untuk memuat daripada yang diharapkan. Ketika mencoba mencari tahu apa yang terjadi, saya berhasil menemukan beberapa perilaku aneh di sisi database yang mungkin menjadi penyebab penurunan kinerja. Saya perhatikan bahwa kadang-kadang ada beberapa pernyataan SQL yang membutuhkan lebih banyak waktu untuk menjalankan apa yang diharapkan. Saya berhasil mengidentifikasi beberapa pernyataan ini (terutama pemanggilan beberapa prosedur tersimpan aplikasi kami) menggunakan jejak profiler (dengan templat TSQL_Duration) untuk mengidentifikasi kueri yang sudah berjalan lama.
Masalahnya adalah bahwa ketika saya menjalankan prosedur tersimpan ini secara langsung pada database di SQL Management Studio kadang-kadang mereka butuh waktu lama (sekitar 7/8 detik), di lain waktu mereka cepat (di bawah 1 detik). Saya tidak tahu mengapa ini terjadi dan itu membuat saya gila, karena mesin SQL (4 core, 32 GB) tidak digunakan oleh aplikasi lain, dan pertanyaan ini seharusnya tidak membutuhkan waktu lama untuk berjalan.
Tidak menjadi DBA atau guru SQL Server, saya sudah mencoba melihat beberapa hal yang dapat membantu saya memahami masalahnya. Inilah langkah-langkah yang telah saya ambil untuk mencoba menyelesaikan masalah dan apa yang saya temukan sejauh ini:
- Semua kode TSQL yang dipanggil oleh aplikasi ditulis dalam prosedur tersimpan.
- Saya mengidentifikasi beberapa permintaan yang berjalan lama pada profiler SQL Server, namun ketika saya menjalankan ini di Management Studio mereka butuh waktu lama untuk menjalankan (dari 4 hingga 10 detik), atau berjalan cepat (di bawah 1 detik). Saya menjalankan kueri yang sama persis dengan data yang sama yang dikirimkan dalam parameter. Pertanyaan ini terutama disimpan prosedur dengan pernyataan pilih di dalamnya
- Saya mencoba melihat statistik menunggu dan antrian untuk mencoba dan mencari tahu apakah ada proses yang menunggu pada beberapa sumber daya. Saya menjalankan kueri berikut:
WITH Waits AS
(SELECT
wait_type,
wait_time_ms / 1000.0 AS WaitS,
(wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
signal_wait_time_ms / 1000.0 AS SignalS,
waiting_tasks_count AS WaitCount,
100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BROKER_TO_FLUSH',
'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT', 'DISPATCHER_QUEUE_SEMAPHORE',
'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
)
SELECT
W1.wait_type AS WaitType,
CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
W1.WaitCount AS WaitCount,
CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount, W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO
Inilah yang saya temukan:
- Setelah saya mereset statistik menggunakan DBCC SQLPERF (sekitar 1 atau 2 jam setelah), jenis menunggu yang paling saya miliki adalah SOS_SCHEDULER_YIELD dan WRITELOG
- Seiring waktu (setelah eksekusi sekitar 1 hari), jenis menunggu yang paling banyak terjadi pada basis data adalah CXPACKET (67%) dan OLEDB (17%), meskipun waktu tunggu rata-rata untuk masing-masing tidak lama. Saya juga memperhatikan bahwa pernyataan yang berjalan lebih lama yang diidentifikasi pada SQL Profiler adalah panggilan ke prosedur tersimpan yang mengembalikan lebih dari satu resultset (seringkali 3). Bisakah ada masalah paralellisme di sini? Apakah ada cara saya bisa mencoba mengidentifikasi apakah ini penyebab masalahnya?
- Saya pernah membaca bahwa OLEDB menunggu dapat disebabkan oleh panggilan ke sumber daya OLEDB seperti server yang ditautkan. Kami memang memiliki server yang terhubung untuk terhubung dengan mesin Layanan Pengindeksan (MSIDXS), namun tidak satu pun dari pernyataan yang diidentifikasi sebagai berjalan lama menggunakan server tertaut itu.
- Waktu tunggu rata-rata yang lebih tinggi yang saya miliki adalah untuk menunggu tipe LCK_M_X (rata-rata sekitar 1,5 detik), tetapi tipe menunggu ini tidak terlalu sering terjadi dibandingkan dengan tipe lain (misalnya, 64 LCK_M_X menunggu vs 10.823 menunggu CXPACKET pada periode waktu yang sama ).
- Satu hal yang saya perhatikan adalah bahwa layanan MSDTC tidak berkerumun. Layanan SQL Server berkerumun tetapi tidak MSDTC. Bisakah ada hit kinerja karena ini? Kami menggunakan MSDTC karena aplikasi kami menggunakan Enterprise Services (DCOM) untuk mengakses database, tetapi server tidak diinstal dan dikonfigurasi oleh kami, tetapi oleh klien kami.
Adakah yang bisa membantu saya lebih memahami data ini? Adakah yang bisa membantu saya memahami apa yang mungkin terjadi? Apakah ada sesuatu yang bisa saya lakukan di server untuk mencoba dan mencari tahu? Haruskah saya berbicara dengan tim pengembangan aplikasi?
exec()
fungsi akan menjelaskan perilaku yang diamati. Dalam hal ini menggunakansp_executesql
biasanya menyelesaikan masalah dengan pernyataan SQL dinamis.Jika kueri berjalan sesekali cepat dan lambat dalam SSMS dan aplikasi, Anda mungkin memiliki masalah statistik atau parameter sniffing.
Saya akan menjalankan prosedur yang tersimpan ini, kemudian meninjau rencana eksekusi untuk menarik properti dari operator root (simpul hijau di paling kiri dari setiap pernyataan).
Berapa taksiran jumlah baris dalam rencana eksekusi, dibandingkan berapa banyak baris aktual yang dikembalikan?
Apakah parameter yang dikompilasi cocok dengan parameter kueri yang sebenarnya?
Jika rencana eksekusi dibuat untuk parameter yang hanya mengembalikan beberapa baris, dan Anda menjalankan prosedur yang sama dengan parameter yang mengembalikan sejumlah besar baris, SQL dapat menggunakan rencana eksekusi yang salah untuk kueri.
Pilihan paket eksekusi terkait erat dengan statistik SQL, jadi sebaiknya Anda membangun kembali statistik Anda secara berkala.
Jika Anda memiliki prosedur tersimpan yang terkadang mengembalikan sejumlah kecil data atau data dalam jumlah besar tergantung pada parameter yang disediakan, Anda mungkin memiliki masalah mengendus parameter.
Jika membangun kembali statistik Anda tidak menyelesaikan masalah, Anda dapat menjalankan pernyataan paling mahal di dalam prosedur tersimpan
OPTION (RECOMPILE)
sumber
Karena Anda telah mengidentifikasi kueri yang sudah berjalan lama, Anda dapat mengambil rencana eksekusi untuk prosedur ini dari cache Anda dan melihat apakah Anda dapat menentukan masalah di sana. Seringkali ada konversi tipe data implisit atau run-time. Juga, jika Anda membersihkan atau memasukkan banyak data, disarankan untuk memperbarui statistik juga.
sumber