সাধারণত, আমি সমস্ত মানক কারণে যোগদানের ইঙ্গিতগুলি ব্যবহার করার বিরুদ্ধে প্রস্তাব দিই। তবে সম্প্রতি আমি এমন একটি প্যাটার্ন পেয়েছি যেখানে আমি প্রায় সবসময় আরও ভালভাবে সম্পাদন করার জন্য একটি বাধ্যতামূলক লুপটি যোগ দিতে পাই। আসলে, আমি এটিকে এত বেশি ব্যবহার এবং সুপারিশ করতে শুরু করেছি যে আমি কিছু মিস করছি না তা নিশ্চিত করার জন্য আমি দ্বিতীয় মতামত পেতে চাইছিলাম। এখানে একটি প্রতিনিধি দৃশ্য রয়েছে (উদাহরণ উত্পন্ন করার জন্য খুব নির্দিষ্ট কোডের শেষে রয়েছে):
--Case 1: NO HINT
SELECT S.*
INTO #Results
FROM #Driver AS D
JOIN SampleTable AS S ON S.ID = D.ID
--Case 2: LOOP JOIN HINT
SELECT S.*
INTO #Results
FROM #Driver AS D
INNER LOOP JOIN SampleTable AS S ON S.ID = D.ID
স্যাম্পেল টেবিলটিতে 1 মিলিয়ন সারি রয়েছে এবং এর পিকে আইডি।
টেম্প টেবিল # ড্রাইভের কেবল একটি কলাম, আইডি, কোনও সূচী এবং 50K সারি নেই।
যা আমি ধারাবাহিকভাবে পাই তা নিম্নলিখিত:
কেস 1:
নমুনা টেবিল
হ্যাশ সম্পর্কিত কোনও এইচআইএনটি সূচক স্ক্যান
উচ্চতর সময়কালে (গড় 333 মিমি)
উচ্চ সিপিইউ (গড় 331 মিমি)
লোয়ার লজিকাল রিডস (4714)
কেস 2: লুপ
যোগদানের ইঙ্গিত সূচক নমুনা টেবিল
লুপের সন্ধান করুন
লোয়ার সময়কালে যোগদান করুন (গড় 204ms, 39% কম)
লোয়ার সিপিইউ (গড় 206, 38% কম)
অনেক উচ্চতর লজিকাল রিডস (160015, 34X আরও)
প্রথমদিকে, দ্বিতীয় মামলার উচ্চতর পড়া আমাকে কিছুটা ভয় পেয়েছিল কারণ কম পড়াগুলি প্রায়শই পারফরম্যান্সের একটি শালীন পরিমাপ হিসাবে বিবেচিত হয়। তবে আসলে কী ঘটছে সে সম্পর্কে আমি যত বেশি চিন্তা করি, তা আমার উদ্বেগের বিষয় নয়। এখানে আমার চিন্তাভাবনা:
স্যাম্পেল টেবিলটি প্রায় 36MB নিয়ে 4714 পৃষ্ঠায় রয়েছে। কেস 1 তাদের সমস্ত স্ক্যান করে যার জন্য আমরা 4714 টি পঠন করি। আরও, এটি অবশ্যই 1 মিলিয়ন হ্যাশগুলি সম্পাদন করবে, যা সিপিইউ নিবিড়, এবং যা শেষ পর্যন্ত আনুপাতিকভাবে সময়কে চালিত করে। এটি এই সমস্ত হ্যাশিং যা মনে হয় 1 এর ক্ষেত্রে সময় ব্যয় করবে।
এখন কেস 2 বিবেচনা করুন এটি কোনও হ্যাশিং করছে না, পরিবর্তে এটি 50000 আলাদা সিক্স করছে, যা রিডগুলি চালাচ্ছে। তবে তুলনামূলকভাবে পাঠ্যগুলি কতটা ব্যয়বহুল? কেউ বলতে পারেন যে এগুলি যদি শারীরিক পাঠ হয় তবে এটি ব্যয়বহুল হতে পারে। তবে মনে রাখবেন 1) প্রদত্ত পৃষ্ঠার কেবল প্রথম পঠনটি শারীরিক হতে পারে এবং 2) তবুও, কেস 1 এর একই বা আরও খারাপ সমস্যা হবে কারণ এটি প্রতিটি পৃষ্ঠা হিট করার গ্যারান্টিযুক্ত।
সুতরাং উভয় ক্ষেত্রে কমপক্ষে একবারে প্রতিটি পৃষ্ঠায় অ্যাক্সেস করতে হবে এ জন্য অ্যাকাউন্টিং, এটি মনে হয় কোনটি দ্রুত, 1 মিলিয়ন হ্যাশ বা প্রায় 155000 মেমরির বিরুদ্ধে পড়ে? আমার পরীক্ষাগুলি পরে বলে মনে হচ্ছে তবে এসকিউএল সার্ভার ধারাবাহিকভাবে পূর্বটিকে পছন্দ করে।
প্রশ্ন
সুতরাং আমার প্রশ্নটিতে ফিরে: পরীক্ষার মাধ্যমে এই ধরণের ফলাফল দেখানোর সময় কি আমি এই লুপ জিন ইঙ্গিতটি জোর করেই রাখা উচিত, বা আমি আমার বিশ্লেষণে কিছু হারিয়ে ফেলছি? আমি এসকিউএল সার্ভারের অপ্টিমাইজারের বিরুদ্ধে যেতে দ্বিধা বোধ করছি, তবে এটি মনে হয় যে এটি হ্যাশ ব্যবহার করে যেমন এই ক্ষেত্রে হওয়া উচিত তার চেয়ে অনেক আগে যোগদান করে।
আপডেট 2014-04-28
আমি আরও কিছু টেস্টিং করেছি এবং আবিষ্কার করেছি যে ফলাফলগুলি আমি উপরে পাচ্ছি (একটি ভিএম ডাব্লু / 2 সিপিইউতে) আমি অন্যান্য পরিবেশে প্রতিলিপি করতে পারি না (আমি 8 এবং 12 সিপিইউ সহ 2 টি ভিন্ন শারীরিক মেশিনে চেষ্টা করেছি)। অপ্টিমাইজারটি পরবর্তীকালের ক্ষেত্রে আরও ভালভাবে কাজ করেছিল যেখানে এমন কোনও উচ্চারিত সমস্যা নেই। আমি অনুমান করি যে পাঠটি শিখেছে, যা পূর্ববর্তী ক্ষেত্রে স্পষ্ট বলে মনে হচ্ছে এটি হল পরিবেশটি অপ্টিমাইজারটি কতটা ভাল কাজ করে তা উল্লেখযোগ্যভাবে প্রভাবিত করতে পারে।
কার্যকর করার পরিকল্পনা
এক্সিকিউশন প্ল্যান কেস 1 এক্সিকিউশন প্ল্যান কেস 2
নমুনা কেস উত্পন্ন কোড
------------------------------------------------------------
-- 1. Create SampleTable with 1,000,000 rows
------------------------------------------------------------
CREATE TABLE SampleTable
(
ID INT NOT NULL PRIMARY KEY CLUSTERED
, Number1 INT NOT NULL
, Number2 INT NOT NULL
, Number3 INT NOT NULL
, Number4 INT NOT NULL
, Number5 INT NOT NULL
)
--Add 1 million rows
;WITH
Cte0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows
Cte1 AS (SELECT 1 AS C FROM Cte0 AS A, Cte0 AS B),--4 rows
Cte2 AS (SELECT 1 AS C FROM Cte1 AS A ,Cte1 AS B),--16 rows
Cte3 AS (SELECT 1 AS C FROM Cte2 AS A ,Cte2 AS B),--256 rows
Cte4 AS (SELECT 1 AS C FROM Cte3 AS A ,Cte3 AS B),--65536 rows
Cte5 AS (SELECT 1 AS C FROM Cte4 AS A ,Cte2 AS B),--1048576 rows
FinalCte AS (SELECT ROW_NUMBER() OVER (ORDER BY C) AS Number FROM Cte5)
INSERT INTO SampleTable
SELECT Number, Number, Number, Number, Number, Number
FROM FinalCte
WHERE Number <= 1000000
------------------------------------------------------------
-- Create 2 SPs that join from #Driver to SampleTable.
------------------------------------------------------------
GO
IF OBJECT_ID('JoinTest_NoHint') IS NOT NULL DROP PROCEDURE JoinTest_NoHint
GO
CREATE PROC JoinTest_NoHint
AS
SELECT S.*
INTO #Results
FROM #Driver AS D
JOIN SampleTable AS S ON S.ID = D.ID
GO
IF OBJECT_ID('JoinTest_LoopHint') IS NOT NULL DROP PROCEDURE JoinTest_LoopHint
GO
CREATE PROC JoinTest_LoopHint
AS
SELECT S.*
INTO #Results
FROM #Driver AS D
INNER LOOP JOIN SampleTable AS S ON S.ID = D.ID
GO
------------------------------------------------------------
-- Create driver table with 50K rows
------------------------------------------------------------
GO
IF OBJECT_ID('tempdb..#Driver') IS NOT NULL DROP TABLE #Driver
SELECT ID
INTO #Driver
FROM SampleTable
WHERE ID % 20 = 0
------------------------------------------------------------
-- Run each test and run Profiler
------------------------------------------------------------
GO
/*Reg*/ EXEC JoinTest_NoHint
GO
/*Loop*/ EXEC JoinTest_LoopHint
------------------------------------------------------------
-- Results
------------------------------------------------------------
/*
Duration CPU Reads TextData
315 313 4714 /*Reg*/ EXEC JoinTest_NoHint
309 296 4713 /*Reg*/ EXEC JoinTest_NoHint
327 329 4713 /*Reg*/ EXEC JoinTest_NoHint
398 406 4715 /*Reg*/ EXEC JoinTest_NoHint
316 312 4714 /*Reg*/ EXEC JoinTest_NoHint
217 219 160017 /*Loop*/ EXEC JoinTest_LoopHint
211 219 160014 /*Loop*/ EXEC JoinTest_LoopHint
217 219 160013 /*Loop*/ EXEC JoinTest_LoopHint
190 188 160013 /*Loop*/ EXEC JoinTest_LoopHint
187 187 160015 /*Loop*/ EXEC JoinTest_LoopHint
*/