এসকিউএল সার্ভারে বহু-থেকে-বহু যোগদানের ইঙ্গিত কীভাবে দেওয়া যায়?


9

আমার কাছে 3 টি "বৃহত্তর" সারণী রয়েছে যা এক জোড়া কলামে যুক্ত হয় (উভয় intগুলি)।

  • টেবিল 1 এ 200 মিলিয়ন ডলার সারি রয়েছে
  • টেবিল 2 এ 1.5 মিলিয়ন ডলার সারি রয়েছে
  • টেবিল 3 এ ~ 6 মিলিয়ন সারি রয়েছে

প্রতিটি টেবিলের উপর একটি ক্লাস্টার সূচক আছে Key1, Key2এবং তারপর আরো একটি কলাম। Key1কার্ডিনালিটি কম এবং খুব স্কিউড। এটি সর্বদা ধারাটিতে রেফারেন্স করা হয় WHEREKey2ধারাটিতে কখনই উল্লেখ করা হয় না WHERE। প্রতিটি যোগদান বহু থেকে বহু।

কার্ডিনালিটি অনুমানের সাথে সমস্যা। প্রতিটি যোগদানের আউটপুট অনুমান বৃহত্তর পরিবর্তে আরও ছোট হয় । আসল ফলাফলটি কয়েক মিলিয়ন হয়ে গেলে কম শতকের চূড়ান্ত প্রাক্কলনে এই ফলাফল হয়।

আরও ভাল অনুমান করার জন্য আমার কি সিই ক্লু করার কোনও উপায় আছে?

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

সমাধানগুলি আমি চেষ্টা করেছি:

  • এতে বহু-কলামের পরিসংখ্যান তৈরি করা হচ্ছে Key1,Key2
  • প্রচুর পরিমাণে ফিল্টার পরিসংখ্যান তৈরি করা Key1(এটি বেশ কিছুটা সহায়তা করে তবে আমি ডাটাবেসে হাজার হাজার ব্যবহারকারীর দ্বারা নির্মিত পরিসংখ্যান শেষ করি))

মাস্কড এক্সিকিউশন প্ল্যান (খারাপ মুখোশ দেওয়ার জন্য দুঃখিত)

যে ক্ষেত্রে আমি দেখছি, ফলাফলটিতে 9 মিলিয়ন সারি রয়েছে। নতুন সিই 180 সারি অনুমান করে; লেগ্যাসি সিই 6100 সারি অনুমান করে।

এখানে একটি পুনরুত্পাদনযোগ্য উদাহরণ:

DROP TABLE IF EXISTS #Table1, #Table2, #Table3;
CREATE TABLE #Table1 (Key1 INT NOT NULL, Key2 INT NOT NULL, T1Key3 INT NOT NULL, CONSTRAINT pk_t1 PRIMARY KEY CLUSTERED (Key1, Key2, T1Key3));
CREATE TABLE #Table2 (Key1 INT NOT NULL, Key2 INT NOT NULL, T2Key3 INT NOT NULL, CONSTRAINT pk_t2 PRIMARY KEY CLUSTERED (Key1, Key2, T2Key3));
CREATE TABLE #Table3 (Key1 INT NOT NULL, Key2 INT NOT NULL, T3Key3 INT NOT NULL, CONSTRAINT pk_t3 PRIMARY KEY CLUSTERED (Key1, Key2, T3Key3));

-- Table1 
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2),
     DataSize (Key1, NumberOfRows)
     AS (SELECT 1, 2000 UNION
         SELECT 2, 10000 UNION
         SELECT 3, 25000 UNION
         SELECT 4, 50000 UNION
         SELECT 5, 200000)
INSERT INTO #Table1
SELECT Key1
     , Key2 = ROW_NUMBER() OVER (PARTITION BY Key1, T1Key3 ORDER BY Number)
     , T1Key3
FROM DataSize
     CROSS APPLY (SELECT TOP(NumberOfRows) 
                         Number
                       , T1Key3 = Number%(Key1*Key1) + 1 
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smaller number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table2
SELECT DISTINCT 
       Key1
     , Key2
     , T2Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1*10) 
                         T2Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smallest number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table3
SELECT DISTINCT 
       Key1
     , Key2
     , T3Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1) 
                         T3Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;


DROP TABLE IF EXISTS #a;
SELECT col = 1 
INTO #a
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
WHERE t1.Key1 = 1;

DROP TABLE IF EXISTS #b;
SELECT col = 1 
INTO #b
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN #Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

উত্তর:


5

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

প্রথম যে বিষয়টি আমি চেষ্টা করব তা হ'ল যোগদানের ফলাফলগুলি একটি টেম্প টেবিলের মধ্য থেকে Object3and Object5আপনি যে পরিকল্পনার পোস্ট করেছেন তার জন্য এটি 51393 সারিগুলিতে কেবল একটি একক কলাম, সুতরাং এটি খুব কমই টেম্পডবিতে কোনও স্থান গ্রহণ করা উচিত। আপনি টেম্প টেবিলে সম্পূর্ণ পরিসংখ্যান সংগ্রহ করতে পারেন এবং যথেষ্ট পরিমাণে চূড়ান্ত কার্ডিনালিটির প্রাক্কলন পাওয়ার জন্য এটি একাই যথেষ্ট। সম্পূর্ণ পরিসংখ্যান সংগ্রহ Object1করা পাশাপাশি সহায়তা করতে পারে। কার্ডিনালিটির অনুমানগুলি প্রায়শই খারাপ হয়ে যায় কারণ আপনি ডান থেকে বামে কোনও পরিকল্পনাটি অতিক্রম করেন।

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

শেষ অবলম্বন হিসাবে আপনি অ্যাডাম মাচানিকের MANY()ফাংশনটি ব্যবহার করতে পছন্দ করেন এমন কোনও উপাদান দ্বারা ম্যানুয়ালি কার্ডিনালিটির অনুমান বাড়িয়ে নিতে পারেন । আমি অন্য উত্তরে এটি সম্পর্কে কথা বলি তবে মনে হচ্ছে লিঙ্কটি মারা গেছে। আপনার আগ্রহ থাকলে আমি কিছু খনন করার চেষ্টা করতে পারি।


অ্যাডামের make_parallelক্রিয়াটি সমস্যাটি প্রশমিত করতে সহায়তা করে। আমি একবার তাকান many। দেখতে বেশ গ্রস ব্যান্ড-সহায়তার মতো মনে হচ্ছে।
স্টিভেন হাইবল

2

এসকিউএল সার্ভারের পরিসংখ্যানগুলিতে কেবলমাত্র পরিসংখ্যান অবজেক্টের শীর্ষস্থানীয় কলামের জন্য একটি হিস্টোগ্রাম থাকে। অতএব, আপনি ফিল্টার পরিসংখ্যান তৈরি করতে পারেন যা মানগুলির একটি হিস্টগ্রাম সরবরাহ করে Key2তবে কেবল সারিগুলির মধ্যে Key1 = 1। প্রতিটি টেবিলে এই ফিল্টার করা পরিসংখ্যানগুলি অনুমানগুলি স্থির করে এবং পরীক্ষার প্রশ্নের জন্য আপনার যে আচরণের প্রত্যাশা রয়েছে তার দিকে নিয়ে যায়: প্রতিটি নতুন যোগদানের ফলে চূড়ান্ত কার্ডিনালিটির অনুমানের উপর প্রভাব পড়বে না (এসকিউএল 2016 এসপি 1 এবং এসকিউএল 2017 উভয় ক্ষেত্রেই নিশ্চিত)।

-- Note: Add "WITH FULLSCAN" to each if you want a perfect 20,000 row estimate
CREATE STATISTICS st_#Table1 ON #Table1 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table2 ON #Table2 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table3 ON #Table3 (Key2) WHERE Key1 = 1

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

উদাহরণস্বরূপ, যোগ USE HINT('ASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERS')তোমার প্রশ্নের যেই সব ইঙ্গিতটি সংবরণ অনুসন্ধানমূলক যোগদানের মধ্যে কিছু পারস্পরিক সম্পর্ক (বরং স্বাধীনতা বেশি) অনুমান করা পরিবর্তন করতে হবে Key1সম্পৃক্ত এবং Key2বিধেয়, যা আপনার প্রশ্নের উপকারী হতে পারে যোগদান করুন। চূড়ান্ত পরীক্ষা ক্যোয়ারী জন্য, এই ইঙ্গিতটি থেকে cardinality অনুমান বৃদ্ধি 1,175করতে 7,551, কিন্তু এখনও বেশ একটু সঠিক এর লাজুক 20,000সারি অনুমান ফিল্টার পরিসংখ্যান উত্পাদিত।

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

DROP TABLE IF EXISTS #Table1_extract, #Table2_extract, #Table3_extract, #c
-- Extract only the subset of rows that match the filter predicate
-- (Or better yet, extract only the subset of columns you need!)
SELECT * INTO #Table1_extract FROM #Table1 WHERE Key1 = 1
SELECT * INTO #Table2_extract FROM #Table2 WHERE Key1 = 1
SELECT * INTO #Table3_extract FROM #Table3 WHERE Key1 = 1
-- Now perform the join on those extracts, removing the filter predicate
SELECT col = 1
INTO #c 
FROM #Table1_extract t1
JOIN #Table2_extract t2
    ON t1.Key2 = t2.Key2
JOIN #Table3_extract t3
    ON t1.Key2 = t3.Key2

আমরা ফিল্টার করা পরিসংখ্যানগুলি ব্যাপকভাবে ব্যবহার করি তবে আমরা Key1প্রতিটি টেবিলে মান অনুযায়ী এটি তৈরি করি । আমাদের এখন হাজার হাজার আছে।
স্টিভেন হিবল

2
@ স্টিভেন হিবিলে গুড পয়েন্ট যে হাজার হাজার ফিল্টার পরিসংখ্যান পরিচালনা করা কঠিন করে তুলতে পারে। (আমরা এটিও দেখেছি যে এটি সংকলনের সময়টিকে নেতিবাচকভাবে প্রভাবিত করে)) এটি আপনার ব্যবহারের ক্ষেত্রে ফিট নাও হতে পারে, তবে আমি আরও একটি # টেম্প টেবিল পদ্ধতির যোগ করেছি যা আমরা বেশ কয়েকবার সাফল্যের সাথে ব্যবহার করেছি।
জেফ প্যাটারসন

-1

একটি নাগাল। চেষ্টা ছাড়া অন্য কোন আসল ভিত্তি নেই।

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key2 = t2.Key2
      AND t1.Key1 = 1
      AND t2.Key1 = 1
     JOIN Table3 t3
       ON t2.Key2 = t3.Key2
      AND t3.Key1 = 1;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.