در اس کیو ال سرور 2016، قابلیت غیر فعال نمودن
parameter sniffing در سطح بانک اطلاعاتی مهیا شده است. اما چرا؟
قبل از پاسخگویی به سؤال بالا، به یک سری مقدمات نیاز است:
وقتی یک کوئری به اس کیو ال ارسال میشود، چه اتفاقی رخ میدهد؟
وقتی یک کوئری ارسال میشود، تعدادی از پروسسها بر روی کوئری شروع به فعالیتهایی مانند مهیا نمودن دادههای بازگشتی، یا ذخیره سازی و ... میکنند.
پروسسها به دو دسته زیر تقسیم میشوند:
- پروسسهایی که در relational engine رخ میدهند
- پروسسهایی که در storage engine رخ میدهند
در
relational engine، هر کوئری pars شده و سپس بوسیله query optimizer
پردازش و پلن اجرایی (execution plan) آن که بفرمت باینری است، ایجاد میشود و
به storage engine ارسال میگردد. در storage engine پروسسهایی مانند قفل
گذاری، نگهداری ایندکسها و تراکنشها رخ میدهد. هنگامیکه اس کیو ال
سرور کوئری را دریافت مینمایند، آن را بلافاصله به relational engine ارسال
میکند. سپس نحو (syntax) آن بررسی میشود؛ این عمل query parsing نامیده
میشود. خروجی عملیات پارسر، یک ساختار درختی (query tree) است. این ساختار
درختی مشخص کننده مراحل لازم جهت اجرای کوئری ارائه شده میباشد.
اگر یک کوئری شامل DML نباشد (مانند ساخت جدول)، علمیات بهبود برروی آن صورت
نخواهد گرفت. ولی در صورتیکه کوئری ارسالی، DML باشد، درخت اشاره شده در
بالا به algebrizer فرستاده میشود که وظیفه آن تفسیر و بررسی کلیه نام
اشیاء، جداول و ستونهای اشاره شده در متن کوئری است. فرآیند algebrizer
بسیار مهم و حیاتی است؛ بدلیل اینکه در کوئری ممکن است اشاره کنندههایی به
اشیایی باشند که در بانک اطلاعاتی موجود نیست. خروجی algebrizer یک query
processor tree باینری است که به بهبود دهنده کوئری ارسال میگردد.
معرفی Query Optimizer (بهبود دهنده پرس و جو)
بهبود
دهنده، بهترین مسیر اجرای کوئری را مشخص میکند. این بهبود دهنده است که مشخص
میکند که اطلاعات بوسیله ایندکس دریافت شوند، یا اینکه از چه اتصالی استفاده
شود و الی آخر. این تصمیمات براساس محاسبات هزینههای (میزان پردازش لازم
cpu و I/O) پلن اجرایی صورت خواهد پذیرفت. بهمین دلیل به پلن cost-based نیز شناخته میشود.
هنگامیکه کوئری سادهای مانند دریافت اطلاعات از یک جدول، که بر روی آن
ایندکس گذاری انجام نشدهاست، ارسال شود، بهبود دهنده بجای مشخص نمودن یک
پلن مناسب بهینه، از یک پلن ساده (trivial) استفاده میکند. ولی برعکس در
صورتیکه کوئری trivial نباشد (یعنی مثلا کوئری به گونهای باشد که از
ایندکسها به شکل صحیحی استفاده شده باشند)، بهبود دهنده یک پلن مناسب را
براساس اطلاعات آماری مهیا شده در اس کیو ال سرور، تولید و انتخاب مینماید.
اطلاعات
آماری از ستونها و ایندکسها جمع آوری میشود. این اطلاعات شامل نحوه
توزیع داده، یکتایی و انتخاب شوندگی است. این اطلاعات توسط یک histogram
ارائه میشود. اگر اطلاعات آماری برای یک ستون و یا ایندکس وجود داشته
باشد، بهبود دهنده از آنها برای محاسبات خود استفاده خواهد کرد. اطلاعات
آماری بصورت خودکار برای تمام ایندکسها و یا هر ستونی که بشود بر روی آنها
where یا join نوشت، فراهم خواهد شد.
بهبود دهنده با مقایسه پلنها براساس بررسی تفاوتهای انواع joinها، چیدمان
مجدد ترتیب join و بررسی ایندکسهای مختلف و سایر فعالیتهای دیگر، پلن
مناسب را انتخاب و از آن استفاده میکند. در طی هر کدام از فعالیتهای
اشاره شده، زمان اجرای آنها نیز تخمین زده (estimated cost) خواهد شد و در
پایان، زمان کل تخمینی بدست خواهد آمد و بهبود دهنده از این زمان برای انتخاب
پلن مناسب بهره خواهد برد. باید توجه داشت که این زمان تقریبی است. زمانیکه بهبود دهنده پلن اجرایی انتخاب میکند، یک actual plan را ایجاد و در حافظه ذخیره
میشود؛ بنام plan cache. البته درصورتیکه پلن مشابه و بهینهتری وجود
نداشته باشد.
استفاده مجدد از پلن ها
تولید پلن هزینه بر است. بههمین دلیل اس کیوال سرور اقدام به ذخیره سازی و
نگهداری آنها میکند تا بتواند از آنها مجددا استفاده نماید؛ البته تا جایی
که مقدور باشد. هنگامیکه آنها تولید میشوند، در قسمتی از حافظه بنام
plan cache ذخیره میشوند. به این عمل procedure cache نیز گفته میشود.
هنگامیکه کوئری به سرور ارسال میشود، بوسیله بهبود دهنده، یک estimated plan ایجاد
خواهد شد و قبل از اینکه به storage engine ارسال شود، بهبود دهنده estimated
plan را با actual execution planهای موجود در plan cache مقایسه میکند.
در صورتیکه یک actual plan را مطابق با estimated plan پیدا نماید، از آن مجدد
استفاده خواهد کرد. این استفاده مجدد به عدم تحمیل سربار اضافهای به سرور جهت
کوئریهای بزرگ و پیچیده که در زمان واحد، هزاران بار اجرا خواهند شد، منجر میشود.
هر پلن فقط یکبار در حافظه ذخیره خواهد شد. ولی در مواقعی با تشخیص
بهبود دهنده و هزینه پلن، یک کوئری میتواند پلن دیگری نیز داشته باشد.
بنابراین پلن دوم نیز با مجموعه عملیاتی متفاوت، جهت اجرای موازی (parallel
execution) برای یک کوئری ایجاد و در حافظه ذخیره میشود.
پلنهای اجرایی برای همیشه در حافظه باقی نخواهند ماند. پلنهای اجرایی دارای
طول عمری طبق فرمول حاصل ضرب هزینه، در تعداد دفعات میباشند. مثلاً پلنی با
هزینه 10 و تعداد دفعات اجرای 5، طول عمر 50 را خواهد داشت. پروسس lazywriter
که یک پروسس داخلی است وظیفه آزاد سازی تمام انواع کشها، از جمله پلن کش را دارد. این پروسس در بازههای مشخص، تمام اشیاء درون حافظه را بررسی کرده
و یک واحد از طول عمر آنها میکاهد.
در موارد زیر، یک پلن از حافظه پاک خواهد شد:
1. به حافظه بیشتری نیاز باشد
2. طول عمر پلن صفر شده باشد
حال فرض کنید شما یک پروسیجر یا یک کوئری پارامتری دارید (پارامتر ورودی: شناسه
سفارش یا نال) که کلیه محصولات سفارش داده شده یا محصولات یک سفارش خاص را
نمایش میدهد. هنگامی که SQL Server optimizer پلن این کوئری را ایجاد
میکند و یا آن را کامپایل میکند، به پارامترهای ورودی این پروسیجر گوش
میدهد (نال یا یک شناسه سفارش). optimizer بوسیله column statistics از
تعداد رکوردهایی که بازگشت داده میشود، برآوردی میکند (مثلا 40 رکورد).
سپس یک پلن مناسب را انتخاب میکند و آن را برای اجرا ارسال میکند و پلن
را ذخیره مینماید.
جمله آخر، معمولا باعث ایجاد مشکل میشوند.
اگر optimizer تکست کوئری مشابهی را مشاهده نماید، ولی با پارامترهای
متفاوت، به کش پلن مراجعه کرده و اگر در آن جا قرار داشت، از آن مجددا
استفاده مینماید. این استفاده مجدد خوب است؛ اما درصورتیکه پارامتر
ارسالی نال باشد چه اتفاقی رخ میدهد؟ جدول سفارشات محصول بسیار حجیم است و
متاسفانه از پلنی که برای بازگشت 40 رکورد قبلا ایجاد شده، برای بازگشت این
حجم بالای از رکوردها استفاده میشود که این کشنده است.
هیچ تضمینی وجود ندارد که از وقوع این اتفاق جلوگیری نمایید؛ اما میتوانید
در هنگام توسعه، پروسیجر را شناسایی و نسبت به رفع آنها اقدام نمایید.
ابتدا کش پلن را خالی نمایید و سپس پروسیجر را با مقادیر متفاوت، اجرا
نمایید. در صورتیکه پلنهای متفاوتی مشاهده نمودید، این یک علامت هشدار است و
میبایست نسبت به رفع آنها اقدام فوری نمایید.