sp_cursorprepexec menyebabkan 53 juta membaca?

9

Kami menjalankan instalasi Dynamics AX 2012 dengan SQL Server 2012. Saya tahu bahwa kursor tidak boleh digunakan lagi tetapi AX menggunakannya dan kami tidak dapat mengubah perilaku ini sehingga kami harus bekerja dengannya.

Hari ini saya menangkap permintaan yang sangat buruk dengan lebih dari 53 juta bacaan dan waktu eksekusi lebih dari 20 menit.

Saya menangkap kueri ini melalui alat pemantauan kami SentryOne.

declare @p1 int
set @p1=1073773227
declare @p2 int
set @p2=180158805
declare @p5 int
set @p5=16
declare @p6 int
set @p6=1
declare @p7 int
set @p7=2
exec sp_cursorprepexec @p1 output,@p2 output,N'@P1 bigint,@P2 nvarchar(5),@P3 bigint,@P4 nvarchar(8),@P5 bigint,@P6 bigint,@P7 bigint,@P8 bigint,@P9 bigint,@P10 bigint,@P11 bigint,@P12 bigint,@P13 bigint,@P14 bigint,@P15 bigint,@P16 bigint,@P17 bigint,@P18 bigint,@P19 nvarchar(5),@P20 bigint,@P21 bigint,@P22 bigint,@P23 bigint,@P24 bigint',N'SELECT T1.PRODUCT,T1.EXTERNALVENDPARTY,T1.LIFECYCLESTATUS,T1.RECID,T2.ECORESPRODUCT,T2.ECORESDISTINCTPRODUCTVARIANT,T2.SGE,T2.ECORESREFORDERNUM,T2.ORDERNUM,T2.RECID,T3.ECORESREFORDERNUM,T3.NAME1,T3.NAME2,T3.NAME3,T3.RECID,T4.ECORESPRODUCT,T4.EXTERNALITEMID,T4.ECORESDISTINCTPRODUCTVARIANT,T4.RECID,T5.RECID,T5.PERSON,T6.RECID,T6.NAME,T6.INSTANCERELATIONTYPE,T7.RECID,T7.NAME,T7.INSTANCERELATIONTYPE,T8.PARTY,T8.ACCOUNTNUM,T8.RECID,T9.RECID,T9.DISPLAYPRODUCTNUMBER,T9.INSTANCERELATIONTYPE,T10.PRODUCT,T10.CATEGORY,T10.RECID,T11.RECID,T11.CODE,T11.NAME,T11.INSTANCERELATIONTYPE FROM INVENTTABLE T1 CROSS JOIN ECORESPRODUCTORDERNUM T2 CROSS JOIN ECORESPRODUCTORDERNUMTRANSLATION T3 LEFT OUTER JOIN VENDEXTERNALITEM T4 ON ((T4.PARTITION=5637144576) AND ((T2.ECORESPRODUCT=T4.ECORESPRODUCT) AND (T4.ECORESDISTINCTPRODUCTVARIANT=@P1))) CROSS JOIN HCMWORKER T5 CROSS JOIN DIRPARTYTABLE T6 CROSS JOIN DIRPARTYTABLE T7 CROSS JOIN VENDTABLE T8 CROSS JOIN ECORESPRODUCT T9 CROSS JOIN ECORESPRODUCTCATEGORY T10 CROSS JOIN ECORESCATEGORY T11 WHERE (((T1.PARTITION=5637144576) AND (T1.DATAAREAID=N''087'')) AND (T1.DATAAREAID=@P2)) AND ((T2.PARTITION=5637144576) AND ((T2.ECORESPRODUCT=T1.PRODUCT) AND (T2.SGE=@P3))) AND ((T3.PARTITION=5637144576) AND ((T3.ECORESREFORDERNUM=T2.ECORESREFORDERNUM) AND (T3.LANGUAGEID=@P4))) AND ((T5.PARTITION=5637144576) AND (T5.RECID=T2.PRODUCTMANAGER)) AND (((T6.PARTITION=5637144576) AND (T6.INSTANCERELATIONTYPE IN (@P5,@P6,@P7,@P8,@P9,@P10,@P11) )) AND (T6.RECID=T5.PERSON)) AND (((T7.PARTITION=5637144576) AND (T7.INSTANCERELATIONTYPE IN (@P12,@P13,@P14,@P15,@P16,@P17,@P18) )) AND (T1.EXTERNALVENDPARTY=T7.RECID)) AND (((T8.PARTITION=5637144576) AND (T8.DATAAREAID=N''087'')) AND ((T7.RECID=T8.PARTY) AND (T8.DATAAREAID=@P19))) AND (((T9.PARTITION=5637144576) AND (T9.INSTANCERELATIONTYPE IN (@P20,@P21,@P22) )) AND (T9.RECID=T1.PRODUCT)) AND ((T10.PARTITION=5637144576) AND (T10.PRODUCT=T9.RECID)) AND (((T11.PARTITION=5637144576) AND (T11.INSTANCERELATIONTYPE IN (@P23,@P24) )) AND (T11.RECID=T10.CATEGORY))',@p5 output,@p6 output,@p7 output,0,N'087',5637146082,N'de',41,2303,2377,2975,2978,5329,6886,41,2303,2377,2975,2978,5329,6886,N'087',3265,3266,3267,2665,4423
select @p1, @p2, @p5, @p6, @p7

Hal pertama yang saya perhatikan adalah bahwa kueri ini menggunakan kursor. Karena penasaran, saya menyalin pernyataan dan menjalankannya di Management Studio tanpa hal-hal kursor (saya harus mengakui bahwa saya mengganti parameter untuk kueri sehingga saya bisa menjalankannya). Dalam SSMS kueri selesai dalam 30 detik. Tidak terlalu cepat, tetapi masih lebih cepat daripada alternatif kursor.

Di sini saya memberikan Anda berdua paket:

Rencana tanpa kursor masih merupakan rencana yang sangat buruk tetapi jauh lebih baik. Pertanyaan saya di sini adalah: Dapatkah seseorang tolong jelaskan kepada saya mengapa versi kursor membutuhkan 53 juta bacaan?

Statistik untuk kueri dengan kursor:

Duration    CPU         Reads       Writes  Est Rows    Actual Rows
1.396.212   1.379.157   53.270.895  3.878   30          2

Statistik untuk kueri tanpa kursor:

Duration    CPU         Reads       Writes  Est Rows    Actual Rows
23.337      1.703       665.113     13      4.287       34.813

Tampaknya aneh untuk mendapatkan 34.813 baris, bukan 2; tapi saya cukup yakin bahwa saya mengisi parameter yang tepat. Saya pikir ini mungkin kekhasan lucu dari SQL Sentry karena saya baru saja menyalin statistik dari sana.

Saya harap saya bisa memberikan Anda semua informasi yang diperlukan untuk Anda. Juga jika ada yang memiliki bacaan yang baik, lebih baik memahami kursor yang akan lebih bagus.

Hans Vader
sumber

Jawaban:

10

Pertama-tama, ini mengejutkan saya bahwa jumlah baris sebenarnya untuk kedua pertanyaan dari SQL Sentry tidak kurang lebih sama.

Kedua. Sulit untuk mengatakan seberapa benar perkiraan Anda dalam rencana dengan kursor tanpa rencana aktual tetapi beberapa hal menonjol bagi saya. (PS: lihat jawaban saya di sini untuk mendapatkan rencana aktual).

Yang sedang berkata, ada beberapa hal yang dapat dicatat dari perkiraan rencana Anda.

Ada peringatan tentang indeks yang tidak cocok karena parametrization. Menghapus parameterisasi sehingga SQL Server dapat menggunakan yang tidak cocok dapat secara dramatis meningkatkan I / O.

Perkiraan jumlah baris di antara 2 paket juga turun secara dramatis. Dalam paket Anda dengan kursor, Anda memiliki taksiran jumlah baris dari vendexternalitem dari 11. Dalam paket Anda tanpa kursor, Anda memiliki taksiran dan jumlah sebenarnya dari hampir 200K. Jika catatan 200K Anda benar-benar masuk ke operator spool yang mungkin menyakitkan.

Semua operator memiliki taksiran yang sangat berbeda (jauh lebih kecil dalam paket dengan kursor), jadi mungkin paket Anda dikompilasi dan di-cache dengan nilai parameter yang berbeda dari yang Anda gunakan dalam kueri tanpa kursor. (dikenal sebagai sniffing parameter )

Ada juga pilihan yang sangat aneh dalam pencarian indeks + kunci pada tabel invent. Rencananya menggunakan typeIdx dan kemudian melakukan pencarian kunci untuk indeks berkerumun (itemidx). Jika perkiraan Anda di luar sana dan SQL Server harus melakukan banyak pencarian kunci yang bisa menjelaskan banyak IO juga. Saya tidak terbiasa dengan stopidx yang Anda miliki di paket tanpa kursor, tetapi sepertinya menutup sehingga mungkin pilihan yang lebih baik untuk parameter yang Anda berikan. Saya kira itu memilih typeidx karena jauh lebih sempit tapi itu bisa jadi karena nilai waktu kompilasi yang berbeda dari yang Anda berikan dengan eksekusi bermasalah.

Singkatnya, saya akan menghapus parametrization untuk kueri ini di AX sehingga menghasilkan rencana dengan nilai aktual sehingga mengambil indeks "lebih baik" yang dibuktikan dengan rencana (dan waktu eksekusi) di SSMS. Ini juga akan memungkinkan SQL Server untuk menggunakan indeks yang difilter. Untuk melakukannya, minta pengembang menambahkan forceliteralskata kunci dalam kode aplikasi tempat kueri ini dijalankan dan lihat apa yang terjadi.

Itu mungkin masih akan meninggalkan Anda dengan permintaan yang membutuhkan waktu 30 detik (mirip dengan apa yang Anda miliki di SSMS) tapi itu hanya masalah penyetelan. Ada peringatan indeks yang hilang dalam rencana Anda dan saya pikir indeks pada ecoresproductordernum.sge bisa membantu misalnya tetapi saya tidak tahu tabel itu dan berpikir mereka ditambahkan oleh penyesuaian. Prinsip-prinsip penyetelan umum akan membantu di sini tetapi itu mungkin terlalu luas untuk jawaban ini (mencakup indeks, ...)

Tom V - coba topanswers.xyz
sumber
Ini menyelesaikan masalah saya. Kami sebenarnya memiliki dua masalah: Parameter sniffing dan filterd menunjukkan. Kami melewatkan melewatkan beberapa hubungan di AX sehingga aplikasi menghasilkan klausa "dalam" yang aneh untuk DirPartyTable. Akhir cerita: tidak pernah melewatkan hubungan meja :)
Hans Vader