কেন নোলক ভেরিয়েবল অ্যাসাইনমেন্ট সহ ধীর গতিতে একটি স্ক্যান তৈরি করে?


11

আমি আমার বর্তমান পরিবেশে নোলকের বিরুদ্ধে লড়াই করছি। একটি যুক্তি যা আমি শুনেছি তা হ'ল লকিংয়ের ওভারহেড একটি ক্যোয়ারিকে ধীর করে দেয়। সুতরাং, এই ওভারহেডটি ঠিক কতটা হতে পারে তা দেখতে আমি একটি পরীক্ষা তৈরি করেছি।

আমি আবিষ্কার করেছি যে NOLOCK আসলে আমার স্ক্যানটি ধীর করে দেয়।

প্রথমে আমি আনন্দিত ছিলাম, তবে এখন আমি কেবল বিভ্রান্ত হয়ে পড়েছি। আমার পরীক্ষাটি কি কোনওভাবে অবৈধ? নলককে আসলে কিছুটা দ্রুত স্ক্যানের অনুমতি দেওয়া উচিত নয়? এখানে কি হচ্ছে?

এখানে আমার স্ক্রিপ্ট:

USE TestDB
GO

--Create a five-million row table
DROP TABLE IF EXISTS dbo.JustAnotherTable
GO

CREATE TABLE dbo.JustAnotherTable (
ID INT IDENTITY PRIMARY KEY,
notID CHAR(5) NOT NULL )

INSERT dbo.JustAnotherTable
SELECT TOP 5000000 'datas'
FROM sys.all_objects a1
CROSS JOIN sys.all_objects a2
CROSS JOIN sys.all_objects a3

/********************************************/
-----Testing. Run each multiple times--------
/********************************************/
--How fast is a plain select? (I get about 587ms)
DECLARE @trash CHAR(5), @dt DATETIME = SYSDATETIME()

SELECT @trash = notID  --trash variable prevents any slowdown from returning data to SSMS
FROM dbo.JustAnotherTable
ORDER BY ID
OPTION (MAXDOP 1)

SELECT DATEDIFF(MILLISECOND,@dt,SYSDATETIME())

----------------------------------------------
--Now how fast is it with NOLOCK? About 640ms for me
DECLARE @trash CHAR(5), @dt DATETIME = SYSDATETIME()

SELECT @trash = notID
FROM dbo.JustAnotherTable (NOLOCK)
ORDER BY ID --would be an allocation order scan without this, breaking the comparison
OPTION (MAXDOP 1)

SELECT DATEDIFF(MILLISECOND,@dt,SYSDATETIME())

আমি যা চেষ্টা করেছি তা কার্যকর হয়নি:

  • বিভিন্ন সার্ভারে চলছে (একই ফলাফল, সার্ভারগুলি 2016-SP1 এবং 2016-SP2, উভয়ই শান্ত ছিল)
  • বিভিন্ন সংস্করণে dbfiddle.ukচলছে (গোলমাল, তবে সম্ভবত একই ফলাফল)
  • ইঙ্গিতগুলির পরিবর্তে বিচ্ছিন্ন স্তর সেট করুন (একই ফলাফল)
  • টেবিলে লক বর্ধন বন্ধ করা হচ্ছে (একই ফলাফল)
  • প্রকৃত ক্যোয়ারী পরিকল্পনায় স্ক্যানের বাস্তব প্রয়োগের সময় পরীক্ষা করা (একই ফলাফল)
  • ইঙ্গিত পুনরায় কম্পাইল (একই ফলাফল)
  • কেবল ফাইলগ্রুপ পড়ুন (একই ফলাফল)

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

এটি নলক সম্পর্কে কী যা ভেরিয়েবল অ্যাসাইনমেন্ট সহ স্ক্যানকে ধীর করে দেয়?


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

1
মাইক্রোসফ্ট এসকিউএল সার্ভার 2016 (এসপি 1) (কেবি 3182545) - 13.0.4001.0 (এক্স 64) লোকালডিবিতে আমার জন্য কোনও ত্রুটি নেই।
মার্টিন স্মিথ

উত্তর:


12

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

আমি যখন এই প্রশ্নগুলি ইটিডাব্লু ট্রেসিং (পারফিউভ ব্যবহার করে) এর অধীনে চালনা করি তখন আমি নিম্নলিখিত ফলাফলগুলি পাই:

Plain  - 608 ms  
NOLOCK - 659 ms

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

পার্থক্য সন্ধান করা

এখানে পাশের পার্শ্ববর্তী তুলনাটি দেখানো হচ্ছে যে sqlmin.dll এ FetchNextRowপদ্ধতিতে 51ms পার্থক্য রয়েছে :

FetchNextRow

সরল নির্বাচনটি বামদিকে 332 এমএসে থাকে , যখন নলক সংস্করণটি ডানদিকে 383 ( 51 মিটার দীর্ঘ) হয়। আপনি দুটি দেখতে পাচ্ছেন যে দুটি কোড পাথ এইভাবে পৃথক:

  • সমভূমি SELECT

    • sqlmin!RowsetNewSS::FetchNextRow কল
      • sqlmin!IndexDataSetSession::GetNextRowValuesInternal
  • ব্যবহার NOLOCK

    • sqlmin!RowsetNewSS::FetchNextRow কল
      • sqlmin!DatasetSession::GetNextRowValuesNoLock যা হয় কল
        • sqlmin!IndexDataSetSession::GetNextRowValuesInternal অথবা
        • kernel32!TlsGetValue

এটি দেখায় যে FetchNextRowবিচ্ছিন্নতা স্তর / নলক ইঙ্গিতের ভিত্তিতে পদ্ধতিতে কিছু শাখা রয়েছে ।

NOLOCKশাখাটি বেশি সময় নেয় কেন ?

নোলক শাখাটি আসলে কল করতে কম সময় ব্যয় করে GetNextRowValuesInternal(25 মিমি কম)। তবে GetNextRowValuesNoLockকোডটিতে সরাসরি (একে একে এএকেএকে "এক্সক্লু" কলাম বলার পদ্ধতিগুলি অন্তর্ভুক্ত নয়) ms৩ এমএসের জন্য চালিত হয় - যা বেশিরভাগ পার্থক্যের (CP 63 - 25 = 38 মিমি সিপিইউতে বেড়েছে)।

তাহলে ওভারহেডের অন্যান্য 13ms (51ms মোট - 38ms হিসাবরক্ষিত হয়েছে) FetchNextRowকী?

ইন্টারফেস প্রেরণ

আমি ভেবেছিলাম এটি যে কোনও কিছুর চেয়ে কৌতূহল ছিল, তবে নোলক সংস্করণে উইন্ডোজ এপিআই পদ্ধতিতে - মোট 17 মেমের kernel32!TlsGetValueমাধ্যমে ফোন করে কিছু ইন্টারফেস প্রেরণ ওভারহেড বহন করে kernel32!TlsGetValueStub। প্লেইন সিলেক্টটি ইন্টারফেসের মধ্য দিয়ে যায় না বলে মনে হয়, তাই এটি কখনও স্টাবকে আঘাত করে না এবং কেবলমাত্র 6 এসএমTlsGetValue (11 মিমের পার্থক্য ) ব্যয় করে । আপনি প্রথম স্ক্রিনশট এ এটি দেখতে পারেন।

আমার সম্ভবত কোয়েরির আরও পুনরাবৃত্তিগুলির সাথে আবার এই ট্রেসটি চালানো উচিত, আমি মনে করি কিছু ছোট ছোট জিনিস আছে যেমন হার্ডওয়্যার বিঘ্নগুলি, যা পারফিউউয়ের 1 মিমি নমুনা হার দ্বারা নেওয়া হয়নি


এই পদ্ধতির বাইরে, আমি আরও একটি ছোট পার্থক্য লক্ষ্য করেছি যার ফলে নলক সংস্করণটি ধীর হয়ে চলেছে:

তালা ছাড়ছে

নলক শাখাটি আরও আক্রমণাত্মকভাবে sqlmin!RowsetNewSS::ReleaseRowsপদ্ধতিটি চালানোর জন্য উপস্থিত হয় , যা আপনি এই স্ক্রিনশটে দেখতে পারেন:

তালা ছাড়ছে

সরল নির্বাচনটি 12 মিমি শীর্ষে রয়েছে, যখন নোলক সংস্করণটি নীচে 26 মিমি ( 14 মিমি দীর্ঘ) এ থাকবে। আপনি "কখন" কলামে দেখতে পারেন যে নমুনার সময় কোডটি আরও ঘন ঘন প্রয়োগ করা হয়েছিল। এটি নলকের বাস্তবায়নের বিশদ হতে পারে তবে ছোট নমুনাগুলির জন্য এটি বেশিরভাগ ওভারহেডের পরিচয় দেবে বলে মনে হচ্ছে।


অন্যান্য অনেক ছোট ছোট পার্থক্য রয়েছে, তবে সেগুলি হ'ল বড় অংশগুলি।

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