আমার সূচকটি একটি নির্বাচন শীর্ষে কেন ব্যবহার হচ্ছে না?


15

এখানে রান-ডাউন: আমি একটি নির্বাচন প্রশ্ন করছি। WHEREএবং ORDER BYধারাগুলির প্রতিটি কলাম একটি IX_MachineryId_DateRecordedকী -ক্লাস্টারযুক্ত সূচীতে থাকে কীটির অংশ হিসাবে বা INCLUDEকলাম হিসাবে । আমি সমস্ত কলামগুলি নির্বাচন করছি , যাতে এর ফলে বুকমার্কের অনুসন্ধান হবে, তবে আমি কেবল নিচ্ছি TOP (1), তাই সার্ভারটি কেবলমাত্র একবারে অনুসন্ধান শেষ করতে হবে তা বলতে পারে।

সর্বাধিক গুরুত্বপূর্ণ, যখন আমি জিজ্ঞাসাটিকে সূচকটি ব্যবহার করতে বলিIX_MachineryId_DateRecorded , তখন এটি এক সেকেন্ডেরও কম সময়ে চলে। যদি আমি সার্ভারটিকে কোন সূচকটি ব্যবহার করতে হয় তা সিদ্ধান্ত নিতে দিই, এটি বাছাই করে IX_MachineryIdএবং এটি এক মিনিট পর্যন্ত সময় নেয়। এটি সত্যই আমাকে পরামর্শ দেয় যে আমি সূচকটি ঠিক করেছি, এবং সার্ভারটি একটি খারাপ সিদ্ধান্ত নিচ্ছে। কেন?

CREATE TABLE [dbo].[MachineryReading] (
    [Id]                 INT              IDENTITY (1, 1) NOT NULL,
    [Location]           [sys].[geometry] NULL,
    [Latitude]           FLOAT (53)       NOT NULL,
    [Longitude]          FLOAT (53)       NOT NULL,
    [Altitude]           FLOAT (53)       NULL,
    [Odometer]           INT              NULL,
    [Speed]              FLOAT (53)       NULL,
    [BatteryLevel]       INT              NULL,
    [PinFlags]           BIGINT           NOT NULL,
    [DateRecorded]       DATETIME         NOT NULL,
    [DateReceived]       DATETIME         NOT NULL,
    [Satellites]         INT              NOT NULL,
    [HDOP]               FLOAT (53)       NOT NULL,
    [MachineryId]        INT              NOT NULL,
    [TrackerId]          INT              NOT NULL,
    [ReportType]         NVARCHAR (1)     NULL,
    [FixStatus]          INT              DEFAULT ((0)) NOT NULL,
    [AlarmStatus]        INT              DEFAULT ((0)) NOT NULL,
    [OperationalSeconds] INT              DEFAULT ((0)) NOT NULL,
    CONSTRAINT [PK_dbo.MachineryReading] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.MachineryReading_dbo.Machinery_MachineryId] FOREIGN KEY ([MachineryId]) REFERENCES [dbo].[Machinery] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_dbo.MachineryReading_dbo.Tracker_TrackerId] FOREIGN KEY ([TrackerId]) REFERENCES [dbo].[Tracker] ([Id]) ON DELETE CASCADE
);

GO
CREATE NONCLUSTERED INDEX [IX_MachineryId]
    ON [dbo].[MachineryReading]([MachineryId] ASC);

GO
CREATE NONCLUSTERED INDEX [IX_TrackerId]
    ON [dbo].[MachineryReading]([TrackerId] ASC);

GO
CREATE NONCLUSTERED INDEX [IX_MachineryId_DateRecorded]
    ON [dbo].[MachineryReading]([MachineryId] ASC, [DateRecorded] ASC)
    INCLUDE([OperationalSeconds], [FixStatus]);

সারণিটি মাস ব্যাপ্তিতে বিভক্ত করা হয়েছে (যদিও আমি এখনও বুঝতে পারি না যে সেখানে কী চলছে)।

ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-01-01T00:00:00.000') 

ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-02-01T00:00:00.000') 
...

CREATE UNIQUE CLUSTERED INDEX [PK_dbo.MachineryReadingPs] ON MachineryReading(DateRecorded, Id) ON PartitionSchemeMonthRange(DateRecorded)

আমি প্রায়শই যে ক্যোয়ারী চালাতাম:

SELECT TOP (1) [Id], [Location], [Latitude], [Longitude], [Altitude], [Odometer], [ReportType], [FixStatus], [AlarmStatus], [Speed], [BatteryLevel], [PinFlags], [DateRecorded], [DateReceived], [Satellites], [HDOP], [OperationalSeconds], [MachineryId], [TrackerId]
    FROM [dbo].[MachineryReading]
    --WITH(INDEX(IX_MachineryId_DateRecorded)) --This makes all the difference
    WHERE ([MachineryId] = @p__linq__0) AND ([DateRecorded] >= @p__linq__1) AND ([DateRecorded] < @p__linq__2) AND ([OperationalSeconds] > 0)
    ORDER BY [DateRecorded] ASC

অনুসন্ধানের পরিকল্পনা: https://www.brentozar.com/pastetheplan/?id=r1c-RpxNx

বাধ্যতামূলক সূচক সহ ক্যোয়ারী প্ল্যান: https://www.brentozar.com/pastetheplan/?id=SywwTagVe

অন্তর্ভুক্ত পরিকল্পনাগুলি হ'ল প্রকৃত বাস্তবায়ন পরিকল্পনা, তবে মঞ্চের ডেটাবেজে (লাইভের আকারের প্রায় 1/100 তম)। আমি লাইভ ডাটাবেসে ফিড করতে দ্বিধা বোধ করছি কারণ আমি প্রায় এক মাস আগে এই সংস্থায় শুরু করেছি।

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


1
হাই @ অ্যান্ড্রুউইলিয়ামসন, এটি কোনও পরিসংখ্যানের সমস্যা হতে পারে। আপনি যদি অদক্ষিত পরিকল্পনা থেকে প্রকৃত পরিকল্পনাটি দেখতে পান তবে সারিগুলির আনুমানিক সংখ্যা 1.22 এবং আসলটি 19039 This এটি ঘুরে দেখা যায় কী আপনি পরবর্তী পরিকল্পনার পরে দেখতে পাবেন যা মূল অনুসন্ধানের দিকে নিয়ে যায়। আপনি পরিসংখ্যান আপডেট করার চেষ্টা করেছেন? যদি তা না হয় তবে স্টেজিং ডাটাবেসে পুরো স্ক্যান দিয়ে চেষ্টা করুন।
জেসিজেসি

উত্তর:


21

যদি আমি সার্ভারটিকে কোন সূচকটি ব্যবহার করতে হয় তা সিদ্ধান্ত নিতে দিই, এটি বাছাই করে IX_MachineryIdএবং এটি এক মিনিট পর্যন্ত সময় নেয়।

সেই সূচকটি বিভাজনযুক্ত নয়, সুতরাং অপ্টিমাইজার সনাক্ত করে যে এটি বাছাই ছাড়াই ক্যোয়ারীতে নির্দিষ্ট করা ক্রম সরবরাহ করতে ব্যবহার করা যেতে পারে। অ-অনন্য-অনিক্লাস্টারড সূচক হিসাবে এটিতে সাবকিজ হিসাবে ক্লাস্টারড ইনডেক্সের কীগুলি রয়েছে, সুতরাং সূচকটি অনুসন্ধান MachineryIdএবং DateRecordedব্যাপ্তিটি অনুসন্ধান করতে ব্যবহার করা যেতে পারে :

সূচি সন্ধান করুন

সূচকটি অন্তর্ভুক্ত করে না OperationalSeconds, সুতরাং পরীক্ষার জন্য পরিকল্পনাকে (পার্টিশনযুক্ত) ক্লাস্টারড ইনডেক্সে সারি প্রতি মানটি দেখতে হবে OperationalSeconds > 0:

খুঁজে দেখো

অপ্টিমাইজারটি অনুমান করে যে এক সারিতে অবিবাহিত সূচক থেকে পড়তে হবে এবং এটি সন্তুষ্ট করার জন্য সন্ধান করতে হবে TOP (1)। এই গণনাটি সারি লক্ষ্যের উপর ভিত্তি করে (দ্রুত একটি সারিটি সন্ধান করুন), এবং মানগুলির অভিন্ন বিতরণ অনুমান করে।

আসল পরিকল্পনা থেকে আমরা দেখতে পাচ্ছি যে 1 টি সারির অনুমানটি সঠিক নয়। প্রকৃতপক্ষে, 19,039 সারিগুলিকে প্রক্রিয়া করতে হবে এটি আবিষ্কার করতে যে কোনও সারি প্রশ্নের শর্ত পূরণ করে না। এটি সারি লক্ষ্য অপ্টিমাইজেশনের জন্য সবচেয়ে খারাপ পরিস্থিতি (1 টি সারি আনুমানিক, সমস্ত সারি প্রকৃতই প্রয়োজন):

প্রকৃত / অনুমান

আপনি ট্রেস পতাকা 4138 দিয়ে সারি লক্ষ্যগুলি অক্ষম করতে পারেন । এর ফলে সম্ভবত এসকিউএল সার্ভার একটি আলাদা পরিকল্পনা বেছে নেবে, সম্ভবত আপনি যেটিকে বাধ্য করেছেন। যে কোনও ক্ষেত্রে, অন্তর্ভুক্ত করে সূচকটিকে IX_MachineryIdআরও অনুকূল করা যায় OperationalSeconds

নন-অ্যালাইন্টড ননক্র্লাস্টারড ইনডেক্সগুলি (বেসগুলি টেবিল থেকে আলাদা করে সূচকগুলি আলাদাভাবে বিভাজন করা) একেবারেই অস্বাভাবিক quite

এটি সত্যই আমাকে পরামর্শ দেয় যে আমি সূচকটি ঠিক করেছি, এবং সার্ভারটি একটি খারাপ সিদ্ধান্ত নিচ্ছে। কেন?

যথারীতি, অপটিমাইজার এটি বিবেচনা করে সবচেয়ে সস্তা পরিকল্পনা নির্বাচন করছে।

IX_MachineryIdপরিকল্পনার আনুমানিক ব্যয় 0.01 ব্যয় ইউনিট, (ভুল) সারি লক্ষ্য অনুমানের উপর ভিত্তি করে যে একটি সারি পরীক্ষা করা হবে এবং ফিরে আসবে।

IX_MachineryId_DateRecordedপরিকল্পনার আনুমানিক ব্যয় 0.27 ইউনিট থেকে অনেক বেশি, কারণ এটি সূচক থেকে 5,515 সারি পড়তে বাছাই করে এবং যেটি সর্বনিম্ন (দ্বারা DateRecorded) সাজায় তা প্রত্যাশা করে :

শীর্ষ এন বাছাই

এই সূচকটি পার্টিশনযুক্ত এবং DateRecordedসরাসরি ক্রমে সারিগুলি ফিরতে পারবেন না (পরে দেখুন)। এটা চাইতে পারেন MachineryIdএবং DateRecordedপরিসর প্রতিটি পার্টিশন মধ্যে , কিন্তু একটি বাছাই করুন প্রয়োজন বোধ করা হয়:

পার্টিশনড সিক

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


যাতে আপনি উৎস ক্যোয়ারী আপডেট করা উচিত ধরনের তথ্য এর @Fromএবং @Toপরামিতি মেলেDateRecorded কলাম ( datetime)। এই মুহুর্তে, এসকিউএল সার্ভার রানটাইম (মেশা ইন্টারভাল অপারেটর এবং এর সাবট্রি ব্যবহার করে) টাইপটি মিল না হওয়ার কারণে একটি গতিশীল পরিসীমা গণনা করছে:

<ScalarOperator ScalarString="GetRangeWithMismatchedTypes([@From],NULL,(22))">
<ScalarOperator ScalarString="GetRangeWithMismatchedTypes([@To],NULL,(22))">

এই রূপান্তরটি অপ্টিমাইজারটিকে আরোহী পার্টিশন আইডি ( DateRecordedআরোহী ক্রমে মানগুলির একটি পরিসীমা আবরণ করা ) এবং অসমতার পূর্বাভাসের মধ্যে সম্পর্ক সম্পর্কে সঠিকভাবে যুক্তি থেকে বাধা দেয় DateRecorded

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

ডেমো

একটি সাধারণ বিভাজনযুক্ত টেবিল এবং সূচক:

CREATE PARTITION FUNCTION PF (datetime)
AS RANGE LEFT FOR VALUES ('20160101', '20160201', '20160301');

CREATE PARTITION SCHEME PS AS PARTITION PF ALL TO ([PRIMARY]);

CREATE TABLE dbo.T (c1 integer NOT NULL, c2 datetime NOT NULL) ON PS (c2);

CREATE INDEX i ON dbo.T (c1, c2) ON PS (c2);

INSERT dbo.T (c1, c2) 
VALUES (1, '20160101'), (1, '20160201'), (1, '20160301');

ম্যাচ টাইপ সঙ্গে কোয়েরি

-- Types match (datetime)
DECLARE 
    @From datetime = '20010101',
    @To datetime = '20090101';

-- Seek with no sort
SELECT T2.c2 
FROM dbo.T AS T2 
WHERE T2.c1 = 1 
AND T2.c2 >= @From
AND T2.c2 < @To
ORDER BY 
    T2.c2;

কোন ধরণের সন্ধান করুন

মিলহীন প্রকারের সাথে প্রশ্ন

-- Mismatched types (datetime2 vs datetime)
DECLARE 
    @From datetime2 = '20010101',
    @To datetime2 = '20090101';

-- Merge Interval and Sort
SELECT T2.c2 
FROM dbo.T AS T2 
WHERE T2.c1 = 1 
AND T2.c2 >= @From
AND T2.c2 < @To
ORDER BY 
    T2.c2;

অন্তর্বর্তী এবং বাছাই করুন


5

সূচকটি ক্যোয়ারির জন্য বেশ ভাল বলে মনে হচ্ছে এবং আমি নিশ্চিত নই কেন এটি অপ্টিমাইজার দ্বারা নির্বাচিত হয়নি (পরিসংখ্যান? পার্টিশন? আজার সীমাবদ্ধতা ?, আসলেই কোনও ধারণা নেই।)

একটি নির্দিষ্ট ফিল্টার হিসাবে যদি > 0একটি নির্দিষ্ট মান হয় এবং একটি ক্যোয়ারী এক্সিকিউশন থেকে অন্যটিতে পরিবর্তন না হয় তবে ফিল্টারড সূচকটি নির্দিষ্ট ক্যোয়ারির জন্য আরও ভাল :

CREATE NONCLUSTERED INDEX IX_MachineryId_DateRecorded_filtered
    ON dbo.MachineryReading
        (MachineryId, DateRecorded) 
    WHERE (OperationalSeconds > 0) ;

আপনার সূচকের মধ্যে দুটি পার্থক্য রয়েছে যেখানে OperationalSeconds3 র্থ কলাম এবং ফিল্টার সূচক রয়েছে:

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

  • দ্বিতীয় এবং এটি আরও সূক্ষ্ম এবং ক্যোয়ারির জন্য গুরুত্বপূর্ণ এটি হল যে এটিতে কেবল সারি রয়েছে যা ক্যোয়ারীতে ব্যবহৃত ফিল্টারটির সাথে মেলে। এটি 3 য় কলামের মানগুলির উপর নির্ভর করে অত্যন্ত গুরুত্বপূর্ণ হতে পারে।
    উদাহরণস্বরূপ একটি নির্দিষ্ট জন্য পরামিতি সেট MachineryIdএবং DateRecorded1000 সারি উত্পাদ পারে। এই সমস্ত সারিগুলির বা প্রায় (OperationalSeconds > 0)সমস্তগুলি যদি ফিল্টারটির সাথে মিলে যায় তবে উভয় সূচকই ভাল আচরণ করবে। তবে যদি ফিল্টারটির সাথে সারি সারিগুলি খুব কম হয় (বা কেবলমাত্র সর্বশেষ এক বা কিছুই নয়), প্রথম সূচকটি কোনও মিল খুঁজে না পাওয়া পর্যন্ত অনেকগুলি বা এই সমস্ত 1000 সারি পেরিয়ে যেতে হবে। অন্যদিকে ফিল্টার করা সূচকের সাথে একটি মিলের সারি (বা 0 টি সারি ফিরতে) খুঁজে পেতে কেবল একটির দরকার কারণ কেবলমাত্র ফিল্টারটির সাথে সারি সারিগুলি সংরক্ষণ করা হয়।


1
সূচি যোগ করা কি ক্যোয়ারিকে আরও দক্ষ করে তুলেছে?
ypercubeᵀᴹ 21

মঞ্চের ডেটাবেজে নয় (সঠিকভাবে পরীক্ষার জন্য এটিতে আরও বেশি ডেটা দরকার), আমি এখনও লাইভে চেষ্টা করে দেখিনি, নতুন সূচকগুলি এটি তৈরিতে এক ঘন্টা সময় নেয়। আমি আমাদের লাইভ ডাটাবেসে কিছু করতেও বেশ দ্বিধা বোধ করছি, কারণ এটি ইতিমধ্যে ধীরে চলছে running মঞ্চে আমাদের লাইভ ক্লোনিংয়ের জন্য আমাদের আরও ভাল সিস্টেমের প্রয়োজন।
অ্যান্ড্রু উইলিয়ামসন 21
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.