এসকিউএল সার্ভারে, আমি কি নিম্নলিখিত ক্ষেত্রে একটি লুপ যোগ দিতে বাধ্য করব?


15

সাধারণত, আমি সমস্ত মানক কারণে যোগদানের ইঙ্গিতগুলি ব্যবহার করার বিরুদ্ধে প্রস্তাব দিই। তবে সম্প্রতি আমি এমন একটি প্যাটার্ন পেয়েছি যেখানে আমি প্রায় সবসময় আরও ভালভাবে সম্পাদন করার জন্য একটি বাধ্যতামূলক লুপটি যোগ দিতে পাই। আসলে, আমি এটিকে এত বেশি ব্যবহার এবং সুপারিশ করতে শুরু করেছি যে আমি কিছু মিস করছি না তা নিশ্চিত করার জন্য আমি দ্বিতীয় মতামত পেতে চাইছিলাম। এখানে একটি প্রতিনিধি দৃশ্য রয়েছে (উদাহরণ উত্পন্ন করার জন্য খুব নির্দিষ্ট কোডের শেষে রয়েছে):

--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 পরিকল্পনা 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

*/

উত্তর:


13

স্যাম্পেল টেবিলটি প্রায় 36MB নিয়ে 4714 পৃষ্ঠায় রয়েছে। কেস 1 তাদের সমস্ত স্ক্যান করে যার জন্য আমরা 4714 টি পঠন করি। আরও, এটি অবশ্যই 1 মিলিয়ন হ্যাশগুলি সম্পাদন করবে, যা সিপিইউ নিবিড়, এবং যা শেষ পর্যন্ত আনুপাতিকভাবে সময়কে চালিত করে। এটি এই সমস্ত হ্যাশিং যা মনে হয় 1 এর ক্ষেত্রে সময় ব্যয় করবে।

একটি হ্যাশ জোনের একটি স্টার্ট-আপ ব্যয় রয়েছে (হ্যাশ টেবিল তৈরি করা, এটি একটি ব্লকিং অপারেশনও), তবে হ্যাশ জোনে অন্তত এসকিউএল সার্ভার দ্বারা সমর্থিত তিনটি শারীরিক যোগদানের প্রকারের সর্বনিম্ন তাত্ত্বিক প্রতি সারি ব্যয় রয়েছে, উভয় ক্ষেত্রে আইও এবং সিপিইউয়ের শর্তাদি। তুলনামূলকভাবে ছোট বিল্ড ইনপুট এবং একটি বড় প্রোব ইনপুট দিয়ে হ্যাশ জয়েনটি সত্যই তার নিজের মধ্যে আসে। এটি বলেছিল যে কোনও পরিস্থিতিতে শারীরিক যোগদানের ধরণটি 'পরিস্থিতিতে ভাল' নয়।

এখন কেস 2 বিবেচনা করুন এটি কোনও হ্যাশিং করছে না, পরিবর্তে এটি 50000 আলাদা সিক্স করছে, যা রিডগুলি চালাচ্ছে। তবে তুলনামূলকভাবে পাঠ্যগুলি কতটা ব্যয়বহুল? কেউ বলতে পারেন যে এগুলি শারীরিক পড়া থাকলে এটি বেশ ব্যয়বহুল হতে পারে। তবে মনে রাখবেন 1) প্রদত্ত পৃষ্ঠার কেবল প্রথম পঠনটি শারীরিক হতে পারে এবং 2) তবুও, কেস 1 এর একই বা আরও খারাপ সমস্যা হবে কারণ এটি প্রতিটি পৃষ্ঠা হিট করার গ্যারান্টিযুক্ত।

প্রতিটি সন্ধানের জন্য একটি বি-ট্রিকে মূলে নেভিগেট করা প্রয়োজন, যা একটি একক হ্যাশ প্রোবের সাথে তুলনামূলকভাবে ব্যয়বহুল। তদ্ব্যতীত, নেস্টেড লুপস জোনের অভ্যন্তরীণ দিকের জন্য সাধারণ আইও প্যাটার্নটি এলোমেলো, হ্যাশ যোগদানের জন্য প্রোব-সাইড স্ক্যান ইনপুটটির অনুক্রমিক অ্যাক্সেস প্যাটার্নের সাথে তুলনা করে। অন্তর্নিহিত শারীরিক আইও সাবসিস্টেমের উপর নির্ভর করে ক্রমবর্ধমান পাঠগুলি এলোমেলো পড়াগুলির চেয়ে দ্রুত হতে পারে। এছাড়াও, এসকিউএল সার্ভার রিড-ফরোয়ার্ড মেকানিজম আরও বড় রিড জারি করে সিক্যুয়াল আইওয়ের সাথে আরও ভাল কাজ করে।

সুতরাং উভয় ক্ষেত্রে কমপক্ষে একবারে প্রতিটি পৃষ্ঠায় অ্যাক্সেস করতে হবে এ জন্য অ্যাকাউন্টিং, এটি মনে হয় কোনটি দ্রুত, 1 মিলিয়ন হ্যাশ বা প্রায় 155000 মেমরির বিরুদ্ধে পড়ে? আমার পরীক্ষাগুলি পরে বলে মনে হচ্ছে তবে এসকিউএল সার্ভার ধারাবাহিকভাবে পূর্বটিকে পছন্দ করে।

এসকিউএল সার্ভার ক্যোয়ারী অপ্টিমাইজারটি অনেকগুলি অনুমান করে। একটি হ'ল ক্যোয়ারী দ্বারা তৈরি পৃষ্ঠায় প্রথম অ্যাক্সেসের ফলে কোনও শারীরিক আইও ('কোল্ড ক্যাশে অনুমান') আসবে। ইতিমধ্যে একই প্রশ্নের মাধ্যমে মেমরিতে পড়া পাতায় পরবর্তী পঠনের সুযোগটি মডেল করা হবে তবে এটি শিক্ষিত অনুমানের চেয়ে বেশি কিছু নয়।

অপ্টিমাইজারের মডেলটি এভাবে কাজ করার কারণটি হ'ল সাধারণত খারাপ অবস্থার জন্য অপ্টিমাইজ করা ভাল (শারীরিক আইও প্রয়োজন)। সমান্তরালতা এবং স্মৃতিতে চলমান জিনিসগুলি দ্বারা অনেকগুলি ত্রুটিগুলি coveredেকে রাখা যায়। এই অনুমানটি অবৈধ প্রমাণিত হলে যদি সমস্ত ডেটা মেমরির মধ্যে থাকে তবে ধারণাটি অকার্যকরভাবে সম্পাদন করতে পারে যদি ধারণাটি উত্পন্ন করে তবেই অপ্টিমাইজার উত্পাদন করবে।

শীতল ক্যাশে অনুমান ব্যবহার করে উত্পাদিত পরিকল্পনাটি তাত্পর্যপূর্ণভাবে কার্যকর করতে না পারার পাশাপাশি যদি উষ্ণ ক্যাশেটি ধরে নেওয়া হয়েছিল তবে এটির সবচেয়ে খারাপ ক্ষেত্রে পারফরম্যান্সটি সাধারণত উন্নততর হবে।

টেস্টিং যখন এই ধরণের ফলাফল দেখায় তখন আমার এই লুপ জয়েন্ট ইঙ্গিতটি জোর করে রাখা উচিত, বা আমি আমার বিশ্লেষণে কিছু মিস করছি? আমি এসকিউএল সার্ভারের অপ্টিমাইজারের বিরুদ্ধে যেতে দ্বিধা বোধ করছি, তবে এটি মনে হয় যে এটি হ্যাশ ব্যবহার করে যেমন এই ক্ষেত্রে হওয়া উচিত তার চেয়ে অনেক আগে যোগদান করে।

দুটি কারণে এটি করা সম্পর্কে আপনার খুব সতর্ক হওয়া উচিত। প্রথমত, ইঙ্গিতগুলিতে যোগদান করুন এছাড়াও স্থিরভাবে শারীরিক যোগদানের ক্রমের সাথে কোয়েরির লিখিত ক্রমের সাথে মিল রাখতে বাধ্য করুন (ঠিক যেমনটি OPTION (FORCE ORDER)আপনিও নির্দিষ্ট করে দিয়েছেন This এটি অপটিমাইজারের জন্য উপলব্ধ বিকল্পগুলি কঠোরভাবে সীমাবদ্ধ করে এবং আপনি যা চান তা সর্বদা নাও হতে পারে OPTION (LOOP JOIN)ested ক্যোয়ারির সাথে যোগ দেয়, তবে লিখিত যোগদানের আদেশ প্রয়োগ করে না।

দ্বিতীয়ত, আপনি এই ধারণাটি তৈরি করছেন যে ডেটা সেটের আকারটি ছোট থাকবে এবং লজিক্যাল রিডের বেশিরভাগই ক্যাশে থেকে আসবে। যদি এই অনুমানগুলি অবৈধ হয়ে যায় (সম্ভবত সময়ের সাথে সাথে), কর্মক্ষমতা হ্রাস পাবে। পরিবর্তিত পরিস্থিতিতে প্রতিক্রিয়া জানাতে বিল্ট-ইন ক্যোয়ারী অপ্টিমাইজারটি বেশ ভাল; এই স্বাধীনতা অপসারণ এমন একটি বিষয় যা আপনার সম্পর্কে কঠোরভাবে চিন্তা করা উচিত।

সামগ্রিকভাবে, যদি না সেখানে একটি হল বাধ্যকারী লুপ যোগদান করে বলপূর্বক কারণ, আমি এটা এড়ানো হবে। ডিফল্ট পরিকল্পনাগুলি সাধারণত সর্বোত্তমের কাছাকাছি থাকে এবং পরিবর্তিত পরিস্থিতির মধ্যে আরও বেশি স্থিতিস্থাপক হতে থাকে।


আপনাকে ধন্যবাদ পল। দুর্দান্ত বিস্তারিত বিশ্লেষণ। আমি আরও কিছু পরীক্ষার উপর ভিত্তি করে, আমার মনে হয় যা ঘটছে তা হ'ল অপটিমাইজারের শিক্ষিত অনুমানগুলি এই নির্দিষ্ট উদাহরণের জন্য ধারাবাহিকভাবে বন্ধ থাকে যখন টেম্প টেবিলের আকার 5K এবং 100K এর মধ্যে থাকে। আমাদের প্রয়োজনীয়তাগুলি গ্যারান্টিযুক্ত টেবিলটি <50K হবে বলে গ্যারান্টি দেওয়া হয়েছে, এটি আমার কাছে নিরাপদ বলে মনে হয়। আমি কৌতূহলী, আপনি কি এখনও জেনে কোনও ধরণের যোগদানের ইঙ্গিতটি এড়াতে পারবেন?
জননিএম

1
@ জননিএম ইঙ্গিতগুলি একটি কারণে উপস্থিত রয়েছে। আপনার যেখানে এটি করার যথাযথ কারণ রয়েছে সেগুলি ব্যবহার করা ঠিক। এটি বলেছিল, অন্তর্নিহিত হওয়ার কারণে আমি খুব কমই যোগদানের ইঙ্গিতগুলি ব্যবহার করি FORCE ORDER। বিজোড় উপলক্ষে আমি একটি যোগদানের ইঙ্গিত ব্যবহার করি না OPTION (FORCE ORDER)কেন , আমি প্রায়শই কেন তা ব্যাখ্যা করার জন্য একটি মন্তব্যে যুক্ত করি ।
পল হোয়াইট 9

0

50,000 সারি মিলিয়ন-সারির টেবিলের বিপরীতে যোগদান করেছে কোনও সূচি ছাড়াই যে কোনও টেবিলের জন্য এটি অনেক বেশি।

এক্ষেত্রে ঠিক কী করা উচিত তা আপনাকে বলা শক্ত, যেহেতু আপনি যে সমস্যাটি সমাধান করার চেষ্টা করছেন তা থেকে এটি এতটাই বিচ্ছিন্ন। আমি অবশ্যই আশা করি যে এটি আপনার কোডের মধ্যে একটি সাধারণ প্যাটার্ন নয় যেখানে আপনি উল্লেখযোগ্য পরিমাণে সারি সহ অনেকগুলি আনইনডেক্সড অস্থায়ী টেবিলের বিরুদ্ধে যোগ দিচ্ছেন।

এটি যা বলে তার জন্য উদাহরণ গ্রহণ করা, কেন কেবল # ড্রাইভের উপর একটি সূচি রাখেনি? ডিআইডি কি সত্যই অনন্য? যদি তা হয় তবে এটি শব্দার্থগতভাবে একটি বিদ্যমান বক্তব্যের সমতুল্য, যা কমপক্ষে এসকিউএল সার্ভারকে জানতে দেবে যে আপনি ডি এর সদৃশ মানগুলির জন্য এস অনুসন্ধান চালিয়ে যেতে চান না:

SELECT S.*
INTO #Results
FROM SampleTable S
WHERE EXISTS (SELECT * #Driver D WHERE S.ID = D.ID);

সংক্ষেপে, এই প্যাটার্নটির জন্য, আমি কোনও লুপ ইঙ্গিত ব্যবহার করব না। আমি কেবল এই প্যাটার্নটি ব্যবহার করব না। আমি নিম্নলিখিতগুলির মধ্যে একটি করব, অগ্রাধিকারের क्रमে যদি সম্ভব না হয়:

  • সম্ভব হলে # ড্রাইভের জন্য টেম্প টেবিলের পরিবর্তে সিটিই ব্যবহার করুন
  • আইডি তে # ড্রাইভের উপর একটি অনন্য অনক্লাস্টারড ইনডেক্স ব্যবহার করুন যদি এটি অনন্য হয় (ধরে নেওয়া যায় আপনি কেবলমাত্র # ড্রাইভার ব্যবহার করেন এবং আপনি টেবিল থেকে নিজেই কোনও ডেটা চান না - যদি আপনাকে সেই টেবিল থেকে আসলে ডেটা প্রয়োজন হয় তবে আপনি এটি একটি ক্লাস্টার ইনডেক্স করতে ভাল হতে পারে)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.