RESOURCE_SEMAPHORE_QUERY_COMPILE Intermittent Tunggu Statistik

8

Saya mencoba memecahkan masalah beberapa lonjakan CPU terputus-putus yang kami saksikan di salah satu Server SQL produksi kami. Kami menjalankan SQL Server 2008 R2 Standard Edition dengan 28 GB RAM dan 4 core CPU. Ketika ini terjadi, kami memperhatikan sejumlah besar RESOURCE_SEMAPHORE_QUERY_COMPILER menunggu, yang berlangsung sekitar satu atau dua menit dan kemudian berhenti, yang kemudian penggunaan CPU kembali normal.

Setelah meneliti ini, saya mengerti bahwa ini biasanya disebabkan oleh kompilasi banyak rencana eksekusi yang tidak dapat digunakan kembali, yang saat ini kami sedang mengerjakan perubahan pada aplikasi kami untuk alamat.

Bisakah perilaku ini juga dipicu oleh rencana penggusuran cache karena tekanan memori? Jika demikian, bagaimana saya memeriksa ini? Saya mencoba melihat apakah ada solusi jangka pendek yang dapat kami lakukan, seperti memutakhirkan RAM server, hingga kami menerapkan perbaikan aplikasi kami. Satu-satunya pilihan jangka pendek lain yang dapat saya pikirkan adalah memindahkan beberapa basis data tersibuk kami ke server yang berbeda.

DanM
sumber

Jawaban:

6

Saya yakin Anda akan melihat gejala ini jika Anda memiliki BANYAK rencana kueri besar yang berjuang untuk memori agar dapat dikompilasi (ini sangat sedikit hubungannya dengan menjalankan kueri itu sendiri). Untuk mencapai ini, saya curiga Anda menggunakan ORM atau semacam aplikasi yang menghasilkan banyak pertanyaan unik namun relatif kompleks. SQL Server dapat berada di bawah tekanan memori karena hal-hal seperti operasi permintaan besar, tetapi pada pemikiran lebih lanjut lebih mungkin hanya bahwa sistem Anda dikonfigurasi dengan memori jauh lebih sedikit daripada yang dibutuhkan (baik tidak pernah ada cukup memori untuk memenuhi semua permintaan Anda Sedang mencoba untuk mengkompilasi, atau ada proses lain pada kotak yang mencuri memori dari SQL Server).

Anda dapat melihat apa yang dikonfigurasi dengan SQL Server menggunakan:

EXEC sp_configure 'max server memory';    -- max configured in MB

SELECT counter_name, cntr_value
  FROM sys.dm_os_performance_counters
  WHERE counter_name IN
  (
    'Total Server Memory (KB)',    -- max currently granted
    'Target Server Memory (KB)'    -- how much SQL Server wished it had
  );

Anda dapat mengidentifikasi paket cache yang membutuhkan memori paling kompilasi dengan permintaan Jonathan Kehayias berikut , yang sedikit diadaptasi:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

;WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT TOP (10) CompileTime_ms, CompileCPU_ms, CompileMemory_KB,
  qs.execution_count,
  qs.total_elapsed_time/1000.0 AS duration_ms,
  qs.total_worker_time/1000.0 as cputime_ms,
  (qs.total_elapsed_time/qs.execution_count)/1000.0 AS avg_duration_ms,
  (qs.total_worker_time/qs.execution_count)/1000.0 AS avg_cputime_ms,
  qs.max_elapsed_time/1000.0 AS max_duration_ms,
  qs.max_worker_time/1000.0 AS max_cputime_ms,
  SUBSTRING(st.text, (qs.statement_start_offset / 2) + 1,
    (CASE qs.statement_end_offset
      WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset
     END - qs.statement_start_offset) / 2 + 1) AS StmtText,
  query_hash, query_plan_hash
FROM
(
  SELECT 
    c.value('xs:hexBinary(substring((@QueryHash)[1],3))', 'varbinary(max)') AS QueryHash,
    c.value('xs:hexBinary(substring((@QueryPlanHash)[1],3))', 'varbinary(max)') AS QueryPlanHash,
    c.value('(QueryPlan/@CompileTime)[1]', 'int') AS CompileTime_ms,
    c.value('(QueryPlan/@CompileCPU)[1]', 'int') AS CompileCPU_ms,
    c.value('(QueryPlan/@CompileMemory)[1]', 'int') AS CompileMemory_KB,
    qp.query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
CROSS APPLY qp.query_plan.nodes('ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS n(c)
) AS tab
JOIN sys.dm_exec_query_stats AS qs ON tab.QueryHash = qs.query_hash
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
ORDER BY CompileMemory_KB DESC
OPTION (RECOMPILE, MAXDOP 1);

Anda dapat melihat bagaimana cache rencana digunakan dengan yang berikut ini:

SELECT objtype, cacheobjtype,
    AVG(size_in_bytes*1.0)/1024.0/1024.0,
    MAX(size_in_bytes)/1024.0/1024.0,
    SUM(size_in_bytes)/1024.0/1024.0,
    COUNT(*)
FROM sys.dm_exec_cached_plans
GROUP BY GROUPING SETS ((),(objtype, cacheobjtype))
ORDER BY objtype, cacheobjtype;

Saat Anda mengalami semafor tinggi menunggu, periksa untuk melihat apakah hasil kueri ini berbeda secara signifikan dari selama aktivitas "normal":

SELECT resource_semaphore_id, -- 0 = regular, 1 = "small query"
  pool_id,
  available_memory_kb,
  total_memory_kb,
  target_memory_kb
FROM sys.dm_exec_query_resource_semaphores;

SELECT StmtText = SUBSTRING(st.[text], (qs.statement_start_offset / 2) + 1,
        (CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset
         END - qs.statement_start_offset) / 2 + 1),
  r.start_time, r.[status], DB_NAME(r.database_id), r.wait_type, 
  r.last_wait_type, r.total_elapsed_time, r.granted_query_memory,
  m.requested_memory_kb, m.granted_memory_kb, m.required_memory_kb,
  m.used_memory_kb
FROM sys.dm_exec_requests AS r
INNER JOIN sys.dm_exec_query_stats AS qs
ON r.plan_handle = qs.plan_handle
INNER JOIN sys.dm_exec_query_memory_grants AS m
ON r.request_id = m.request_id
AND r.plan_handle = m.plan_handle
CROSS APPLY sys.dm_exec_sql_text(r.plan_handle) AS st;

Dan Anda mungkin juga ingin melihat dan melihat bagaimana memori didistribusikan:

DBCC MEMORYSTATUS;

Dan ada beberapa informasi bagus di sini tentang mengapa Anda mungkin melihat banyak kompilasi / kompilasi (yang akan berkontribusi pada penantian itu):

http://technet.microsoft.com/en-us/library/ee343986(v=sql.100).aspx

http://technet.microsoft.com/en-us/library/cc293620.aspx

Anda dapat memeriksa jumlah kompilasi / kompilasi ulang yang tinggi menggunakan penghitung berikut:

SELECT counter_name, cntr_value
  FROM sys.dm_os_performance_counters
  WHERE counter_name IN 
  (
    'SQL Compilations/sec',
    'SQL Re-Compilations/sec'
  );

Dan Anda dapat memeriksa tekanan memori internal yang mengarah ke penggusuran - penghitung non-nol di sini akan menunjukkan bahwa sesuatu yang tidak baik sedang terjadi dengan cache rencana:

SELECT * FROM sys.dm_os_memory_cache_clock_hands 
  WHERE [type] IN (N'CACHESTORE_SQLCP', N'CACHESTORE_OBJCP');

CATATAN Sebagian besar metrik ini tidak memiliki keajaiban "ya ampun, saya perlu panik atau melakukan sesuatu!" ambang. Yang perlu Anda lakukan adalah melakukan pengukuran selama aktivitas sistem normal , dan menentukan di mana ambang batas ini untuk perangkat keras, konfigurasi, dan beban kerja Anda. Ketika Anda panik melakukan sesuatu adalah ketika dua kondisi itu benar:

  1. metrik bervariasi secara signifikan dari nilai normal; dan,
  2. sebenarnya ada masalah kinerja yang terjadi (seperti paku CPU Anda) - tetapi hanya jika mereka benar-benar mengganggu apa pun. Selain melihat lonjakan CPU, apakah Anda melihat gejala lain? Dengan kata lain, apakah lonjakan gejala, atau apakah lonjakan menyebabkan gejala lain? Apakah pengguna sistem akan memperhatikan? Banyak orang selalu mengejar konsumen yang menunggu tertinggi mereka, hanya karena itu yang tertinggi. Sesuatu akan selalu menjadi konsumen tunggu tertinggi - Anda harus tahu bahwa itu cukup bervariasi dari aktivitas normal yang mengindikasikan masalah atau perubahan signifikan.

Optimize for ad hoc workloadsadalah pengaturan yang tepat untuk 99% dari beban kerja di luar sana, tetapi itu tidak akan sangat membantu dalam mengurangi biaya kompilasi - ini bertujuan untuk mengurangi rembesan cache rencana dengan mencegah rencana penggunaan tunggal menyimpan seluruh paket sampai dijalankan dua kali . Bahkan ketika Anda hanya menyimpan rintisan di cache paket, Anda masih harus mengkompilasi paket lengkap untuk pelaksanaan kueri. Mungkin yang ingin direkomendasikan oleh @ Kahn adalah mengatur parameterisasi tingkat basis data untuk dipaksakan , yang berpotensi memberikan rencana penggunaan kembali yang lebih baik (tapi itu benar-benar tergantung pada seberapa unik semua permintaan biaya tinggi ini).

Juga beberapa informasi yang baik dalam buku putih ini tentang caching dan kompilasi rencana.

Aaron Bertrand
sumber
Kami saat ini memiliki Optimize for ad hoc workloadsset, seperti yang Anda sebutkan, itu tidak benar-benar relevan dengan masalah khusus ini. Kami memiliki kode yang menghasilkan banyak pertanyaan unik, beberapa dari alat ORM, beberapa kode tangan. Sejauh yang saya tahu, lonjakan CPU tidak terjadi cukup lama bagi pengguna kami untuk melihat. Mengatur basis data ke parameterisasi paksa terdengar berbahaya bagi saya.
DanM
Satu pertanyaan - ketika Anda memeriksa kompilasi tinggi, apa yang sebenarnya merupakan angka tinggi? Saya pikir kompilasi / detik hanya bermakna jika dibandingkan dengan jumlah permintaan batch / detik.
DanM
@DanM seperti yang saya katakan di atas, tidak ada cara bagi saya untuk mengetahui apa yang mungkin tinggi untuk lingkungan Anda, karena saya tidak tahu apa yang normal untuk lingkungan Anda. Jika jumlahnya mendekati atau lebih tinggi dari jumlah permintaan batch / detik, itu mungkin merupakan indikator, tetapi sekali lagi itu tergantung. Misalnya, jika kumpulan Anda terdiri dari 5000 pernyataan dan 10 di antaranya memerlukan kompilasi ulang (karena ini dapat terjadi di tingkat pernyataan), comp / sec akan menjadi 10x dibandingkan dengan batch / detik. Apakah itu masalah?
Aaron Bertrand
@DanM Juga Anda harus mengambil rekomendasi apa pun untuk mengubah basis data atau pengaturan global dengan penafian tersirat bahwa itu adalah sesuatu yang harus Anda uji dan bukan hanya diaktifkan karena seseorang di Internet mengatakan untuk melakukannya. :-) Saya mencoba menjelaskan bagaimana dan mengapa perubahan bisa membantu, tapi saya tidak selalu ingat untuk menyatakan yang jelas: uji dulu .
Aaron Bertrand
Saya mengerti maksud Anda - apa yang merupakan kompilasi "tinggi" sepenuhnya bergantung pada lingkungan.
DanM
-1

Alasan yang paling khas sejauh ini yang telah saya lihat menunggu itu muncul, adalah sebagai akibat dari indeks terfragmentasi atau tidak cukup, dan statistik yang memiliki ukuran sampel tidak cukup, atau usang. Ini menghasilkan pemindaian tabel lengkap yang besar yang memonopoli semua memori, yang pada gilirannya menghasilkan gejala yang sering kita lihat sebagai RESOURCE_SEMAPHORE_QUERY_COMPILE.

Cara termudah untuk memverifikasi ini adalah dengan memeriksa apakah kueri menjalankan pemindaian tabel penuh / pemindaian indeks, ketika mereka harus melakukan pencarian indeks. Jika Anda memiliki kueri masalah yang dengannya Anda dapat mereproduksi masalah - menjadi sangat mudah untuk mendiagnosis dan memperbaikinya.

Saya akan memeriksa indeks pada tabel yang dipengaruhi oleh pertanyaan masalah - yaitu. periksa fragmentasi indeks, indeks potensial yang difilter yang tidak digunakan, hilang indeks yang mungkin ingin Anda buat, dll. Juga, perbarui statistik mereka dengan FULLSCAN sesegera mungkin.

Poin yang baik untuk diingat adalah bahwa tabel masalah Anda mungkin bukan satu-satunya yang membutuhkan ini. Misalnya, jika Anda memiliki kueri yang mengambil data dari 10 tabel, perencana eksekusi kadang-kadang dapat menunjukkan bahwa itu tidak menggunakan indeks pada tabel 1, tetapi ketika Anda kemudian memeriksa indeks pada tabel 1, itu sebenarnya ok. Perencana kueri dapat memutuskan untuk mengambil data pada tabel 1 dengan pemindaian tabel lengkap dengan benar, karena indeks yang salah / tidak cukup pada tabel 7 misalnya, mengembalikan begitu banyak data sehingga ini akan menjadi pilihan yang lebih cepat. Jadi mendiagnosis ini terkadang sulit.

Juga, jika Anda memiliki banyak kueri kode-kode dengan hanya beberapa perubahan dalam nilai variabel misalnya, Anda mungkin ingin melihat pengaktifan pengoptimalan untuk beban kerja ad hoc . Pada dasarnya apa yang dilakukannya adalah menyimpan potongan rintisan dari rencana yang disusun alih-alih keseluruhan rencana, menghemat sumber daya ketika Anda tidak pernah mendapatkan rencana yang persis sama setiap saat.

Kahn
sumber
Sebagian besar hal yang Anda sorot akan menyebabkan rencana kueri yang tidak efisien, bukan waktu kompilasi yang tinggi. MENURUT OPINI SAYA.
Aaron Bertrand
Namun demikian, saya telah melihat ini terjadi beberapa kali dan penantian itu sering kali persis seperti apa yang terjadi selanjutnya. Jelas saya tidak tahu seberapa umum itu atau apakah itu berlaku di sini, tetapi metode yang disebutkan di atas memperbaikinya.
Kahn
Sebagai after-edit, dalam kasus kami itu muncul dalam DB yang agak besar setelah indeks / statistik penting terpengaruh yang digunakan oleh sejumlah besar pertanyaan yang dijalankan sepanjang waktu.
Kahn
1
Benar, dan menunggu kompilasi datang karena indeks / statistik diubah, yang menyebabkan semua rencana yang relevan dikompilasi ulang, bukan karena mereka terfragmentasi atau ketinggalan zaman (seperti negara jawaban Anda).
Aaron Bertrand