কোয়েরিতে অর্ডার অন্তর্ভুক্তি যা কোনও সারি দেয় না তা কর্মক্ষমতাকে মারাত্মকভাবে প্রভাবিত করে


15

একটি সাধারণ তিনটি টেবিল জয়েন দেওয়া হয়েছে, অর্ডার বাই দ্বারা অন্তর্ভুক্ত করা সত্ত্বেও কোনও সারি ফিরে না পেয়ে ক্যোরির পারফরম্যান্স মারাত্মকভাবে পরিবর্তিত হয়। প্রকৃত সমস্যা পরিস্থিতি শূন্য সারি ফিরে আসতে 30 সেকেন্ড সময় নেয় তবে তাত্ক্ষণিকভাবে যখন অর্ডার অন্তর্ভুক্ত না হয়। কেন?

SELECT * 
FROM tinytable t                          /* one narrow row */
JOIN smalltable s on t.id=s.tinyId        /* one narrow row */
JOIN bigtable b on b.smallGuidId=s.GuidId /* a million narrow rows */
WHERE t.foreignId=3                       /* doesn't match */
ORDER BY b.CreatedUtc          /* try with and without this ORDER BY */

আমি বুঝতে পারি যে বিগ টেবিল.স্মলগুইডআইডিতে আমার একটি সূচক থাকতে পারে তবে আমি বিশ্বাস করি যে এই ক্ষেত্রে এটি আরও খারাপ করে দেবে।

পরীক্ষার জন্য টেবিলগুলি তৈরি / পপুলেট করার জন্য এখানে স্ক্রিপ্ট। কৌতূহলজনকভাবে, এটি মনে হচ্ছে যে ছোট্ট টেবিলের একটি এনভারচার (সর্বোচ্চ) ক্ষেত্র রয়েছে। এটির বিষয়টিও মনে হচ্ছে যে আমি একটি গাইড (যা আমার অনুমান করে যে এটি হ্যাশ মেলানো ব্যবহার করতে চায়) দিয়ে বিগ টেবলে যোগ দিচ্ছি।

CREATE TABLE tinytable
  (
     id        INT PRIMARY KEY IDENTITY(1, 1),
     foreignId INT NOT NULL
  )

CREATE TABLE smalltable
  (
     id     INT PRIMARY KEY IDENTITY(1, 1),
     GuidId UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID(),
     tinyId INT NOT NULL,
     Magic  NVARCHAR(max) NOT NULL DEFAULT ''
  )

CREATE TABLE bigtable
  (
     id          INT PRIMARY KEY IDENTITY(1, 1),
     CreatedUtc  DATETIME NOT NULL DEFAULT GETUTCDATE(),
     smallGuidId UNIQUEIDENTIFIER NOT NULL
  )

INSERT tinytable
       (foreignId)
VALUES(7)

INSERT smalltable
       (tinyId)
VALUES(1)

-- make a million rows 
DECLARE @i INT;

SET @i=20;

INSERT bigtable
       (smallGuidId)
SELECT GuidId
FROM   smalltable;

WHILE @i > 0
  BEGIN
      INSERT bigtable
             (smallGuidId)
      SELECT smallGuidId
      FROM   bigtable;

      SET @i=@i - 1;
  END 

আমি এসকিউএল 2005, 2008 এবং 2008R2 এ একই ফলাফল সহ পরীক্ষা করেছি।

উত্তর:


32

আমি মার্টিন স্মিথের উত্তরের সাথে একমত, তবে সমস্যাটি হুবহু এক পরিসংখ্যানের নয়। বিদেশী আইডি কলামের পরিসংখ্যানগুলি (স্বয়ংক্রিয় পরিসংখ্যান সক্ষম করে ধরে নিচ্ছেন) সঠিকভাবে দেখায় যে 3 টির মান (7 টির মান সহ একটি মাত্র আছে) উপস্থিত রয়েছে:

DBCC SHOW_STATISTICS (tinytable, foreignId) WITH HISTOGRAM

পরিসংখ্যান আউটপুট

এসকিউএল সার্ভার জানে যে পরিসংখ্যানগুলি ধরা পড়ার পর থেকে জিনিসগুলি পরিবর্তিত হতে পারে , সুতরাং পরিকল্পনাটি কার্যকর করা হলে 3 মানের জন্য একটি সারি থাকতে পারে । এছাড়াও, পরিকল্পনা সংকলন এবং সম্পাদনের মধ্যে যে কোনও সময় ব্যয় হতে পারে (পরিকল্পনাগুলি পুনরায় ব্যবহারের জন্য ক্যাশে করা হয়)। মার্টিন যেমন বলেছে, এসকিউএল সার্ভারে উপযুক্ত কারণে যে কোনও ক্যাশেড পরিকল্পনা পুনরায় সংশোধন করার ন্যায্যতা অর্জনের জন্য পর্যাপ্ত পরিবর্তন করা হয়েছে তা সনাক্ত করার জন্য যুক্তি রয়েছে।

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

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

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

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


21

এখানে মূল সমস্যাটি একটি পরিসংখ্যান।

উভয় প্রশ্নের জন্য আনুমানিক সারির গণনাটি দেখায় যে এটি বিশ্বাস করে যে চূড়ান্তভাবে 0 টির পরিবর্তে SELECT1,048,580 টি সারি (যে পরিমাণ সারি বিদ্যমান bigtableবলে মনে হচ্ছে) ফিরে আসবে।

আপনার উভয় JOINশর্ত মেলে এবং সমস্ত সারি সংরক্ষণ করবে। এগুলি শেষ হয়ে যায় কারণ একক সারি ইন প্রিডিটকের সাথে tinytableমেলে না t.foreignId=3

আপনি যদি চালান

SELECT * 
FROM tinytable t  
WHERE t.foreignId=3  AND id=1 

এবং এটির 1চেয়ে সারিগুলির আনুমানিক সংখ্যাটি দেখুন 0এবং এই ত্রুটিটি পুরো পরিকল্পনা জুড়েই প্রচার করে। tinytableবর্তমানে 1 সারি রয়েছে। 500 টি সারি পরিবর্তন না হওয়া পর্যন্ত পরিসংখ্যানগুলি এই টেবিলটির জন্য পুনরায় সংমিশ্রিত হবে না যাতে কোনও মিলের সারি যুক্ত করা যেতে পারে এবং এটি পুনরায় সংযোগের সূত্রপাত করে না।

আপনি ক্লজটি যুক্ত করার পরে যোগদানের আদেশের পরিবর্তনের কারণ ORDER BYএবং সেখানে একটি varchar(max)কলাম smalltableরয়েছে কারণ এটি অনুমান করে যে varchar(max)কলামগুলি রোজকে গড়ে গড়ে 4,000 বাইট বৃদ্ধি করবে। এটি 1048580 সারি দ্বারা গুণ করে এবং এর অর্থ এই যে সাজানোর ক্রিয়াকলাপের জন্য আনুমানিক 4 জিবি লাগবে তাই এটি সংবেদনশীলতার সাথে SORTঅপারেশনটি করার আগে সিদ্ধান্ত নেয় JOIN

আপনি নীচের হিসাবে ইঙ্গিতগুলি ব্যবহারের সাথে যোগ ORDER BYনা ORDER BYদেওয়ার কৌশল অবলম্বন করতে কোয়েরিকে বাধ্য করতে পারেন ।

SELECT *
FROM   tinytable t /* one narrow row */
       INNER MERGE JOIN smalltable s /* one narrow row */
                        INNER LOOP JOIN bigtable b
                          ON b.smallGuidId = s.GuidId /* a million narrow rows */
         ON t.id = s.tinyId
WHERE  t.foreignId = 3 /* doesn't match */
ORDER  BY b.CreatedUtc
OPTION (MAXDOP 1) 

পরিকল্পনাটি প্রায় 12,000এবং ভ্রান্ত अंदाजित সারি গণনা এবং আনুমানিক ডেটা আকারের একটি আনুমানিক উপ-ট্রি ব্যয় সহ একটি সাজানোর অপারেটর দেখায় ।

পরিকল্পনা

বিটিডব্লিউ আমি UNIQUEIDENTIFIERআমার পরীক্ষায় জিনিসগুলি পরিবর্তিত পূর্ণসংখ্যার সাথে কলামগুলি প্রতিস্থাপন করতে পেলাম না ।


2

আপনার শো এক্সিকিউশন প্ল্যান বোতামটি চালু করুন এবং আপনি যা দেখতে পাচ্ছেন তা দেখতে পাবেন। "ধীর" ক্যোয়ারির জন্য এখানে পরিকল্পনা রয়েছে: এখানে চিত্র বর্ণনা লিখুন

এবং এখানে "দ্রুত" ক্যোয়ারী: এখানে চিত্র বর্ণনা লিখুন

এটি দেখুন - একসাথে চালান, প্রথম ক্যোয়ারী ~ 33x আরও "ব্যয়বহুল" (97: 3 অনুপাত)। এসকিউএল তারিখের সময় অনুসারে বিগ টেবিলকে অর্ডার করার জন্য প্রথম ক্যোয়ারীটি অনুকূল করে তুলছে, তারপরে ছোট ছোট "সন্ধান" লুপটি চালাচ্ছে ছোট ছোট টেবিল এবং টিনিটেবলকে প্রতি 1 মিলিয়ন বার প্রয়োগ করে (আপনি আরও ক্লাস্টারড ইনডেক্স সিক "আইকন ধরে আরও স্ট্যাটাস পেতে পারেন)। সুতরাং, বাছাই (27%), এবং 2 x 1 মিলিয়ন ছোট টেবিলগুলিতে "অনুসন্ধান" (23% এবং 46%) ব্যয়বহুল ক্যোয়ারির বিশাল অংশ। তুলনায়, অ- ORDER BYজিজ্ঞাস্যটি মোট 3 টি স্ক্যান করে।

মূলত, আপনার নির্দিষ্ট দৃশ্যের জন্য আপনি এসকিউএল অপ্টিমাইজার যুক্তিতে একটি গর্ত পেয়েছেন। তবে টিশএইচটিটিপি অনুসারে, আপনি যদি একটি সূচি যোগ করেন (যা আপনার সন্নিবেশকে / কিছু আপডেট করে) তবে আপনার স্ক্যানিং দ্রুত পাগল হয়ে যায়।


2

কী হচ্ছে এসকিউএল নিষেধাজ্ঞার আগেই আদেশটি চালানোর সিদ্ধান্ত নিচ্ছে।

এটা চেষ্টা কর:

SELECT *
(
SELECT * 
FROM tinytable t
    INNER JOIN smalltable s on t.id=s.tinyId
    INNER JOIN bigtable b on b.smallGuidId=s.GuidId
WHERE t.foreignId=3
) X
ORDER BY b.CreatedUtc

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

শেষ অবধি, নিম্নলিখিত স্ক্রিপ্টটি চালানোর চেষ্টা করুন এবং তারপরে দেখুন আপডেট হওয়া পরিসংখ্যান এবং সূচীগুলি আপনার যে সমস্যাটি রয়েছে তা ঠিক করে দেয়:

EXEC [sp_MSforeachtable] @command1="RAISERROR('UPDATE STATISTICS(''?'') ...',10,1) WITH NOWAIT UPDATE STATISTICS ? "

EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')"

EXEC [sp_MSforeachtable] @command1="RAISERROR('UPDATE STATISTICS(''?'') ...',10,1) WITH NOWAIT UPDATE STATISTICS ? "

1

আপনার ক্ষেত্রের দ্বারা আদেশের জন্য আপনার একটি সূচি যুক্ত করা উচিত এবং আপনি দেখতে পাবেন যে গতি আরও বাড়বে। Https://stackoverflow.com/questions/1716798/sql-server-2008-ordering-by-datetime-is-too-slow দেখুন

এটি ব্যবহার করে দেখুন, আমি মনে করি না যে আপনার অনুমান, এটি কেবল জিনিসগুলিকে ধীর করে দেবে, সঠিক।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.