اخیرا لازم بود تا میلیون ها داده ( بخشی از آمار فروش بلیت های تک سفره و دو سفره متروی تهران) را از پایگاه داده ی سروری به پایگاه داده سروری دیگر، جهت پاره ای از پردازش ها انتقال دهیم.
برای اینکه داده ها را از سرور مبدا انتخاب کنیم، لازم بود تا سه جدول با داده های به نسبت بزرگی را به هم ملحق (JOIN) کنیم و برای اینکار از اپراتور INNER JOIN استفاده شده بود. متاسفانه کوئریِ ما مدت نامحدودی ادامه پیدا می کرد، بدون آنکه جوابی از سمت Database Engine به ما برگشت داده شود. برای بررسی این موضوع:
1. پلن اجرایی (Execution Plan) را بررسی کردم. در برخی جاها نیاز به تعریف Index های جدید، Composite Index ها و Covered Index بود. این ها را تعریف کردیم ولی همچنان هیچ پاسخی از SQL Server بازگشت داده نمیشد.
2. در Activity Monitor، مشاهده شد که Thread های متعددی برای کوئری ما ساخته شده و همه دارای wait type ایی برابر با CXPACKET بودند. در نتیجه سعی کردم با تغییر پارامتر MAXDOP و Cost Threshhold for Parralelism اوضاع را بهتر کنم، ولی هیچ افاقه ای نکرد.
راه حل نهایی: در نهایت به جای استفاده از INNER JOIN، از اپراتوری به نام INNER HASH JOIN استفاده کردم. روش HASH JOIN دقیقا مناسب وقتی است که قرار است داده های بسیار بسیار زیادی را به هم ملحق کنیم و روش INNER JOIN تنها مناسب داده هایی با حجم کمتر می باشد.
در روش HASH JOIN، به طور خلاصه موتور SQL Server دو کار را انجام می دهد:
1. ابتدا جدول کوچک تر را در حافظه بارگذاری کرده و در یک ساختار داده ی Hash Table قرار می دهد.
2. جدول بزرگ تر از دیسک خوانده می شود، و سطر به سطر با Hash Table مقایسه شده و در صورت لازم به هم ملحق می شوند. این علمیات با O(1)1 صورت می گیرد.
در روش جدید، در 16 ثانیه پاسخ کوئری برگشت داده شد.
1. در مورد Hash Join:
http://blog.sqlauthority.com/2007/06/14/sql-server-explanation-sql-server-hash-join/
2. روش های دیگری نیز برای عملیات JOIN وجود دارند، که با توجه به ماهیت داده ها می توانیم از پایگاه داده بخواهیم، با روش مناسب عملیات الحاق سازی را انجام دهند. به این روش ها JOIN Hint گفته می شود. اگر با داده های زیادی کار می کنید، یادگیری این ها خالی از لطف نیست.
https://technet.microsoft.com/en-us/library/ms191426(v=sql.105).aspx