সাবকিউরি সহ বড় টেবিলের উপর ধীর আপডেট update


16

সঙ্গে SourceTable> থাকার 15MM রেকর্ড ও Bad_Phraseথাকার> 3K রেকর্ড, নিম্নলিখিত প্রশ্নের সাথে SQL সার্ভার 2005 SP4 চালানোর প্রায় 10 ঘণ্টা সময় লাগে।

UPDATE [SourceTable] 
SET 
    Bad_Count=
             (
               SELECT 
                  COUNT(*) 
               FROM Bad_Phrase 
               WHERE 
                  [SourceTable].Name like '%'+Bad_Phrase.PHRASE+'%'
             )

ইংরেজিতে, এই প্রশ্নের সাথে একটি ক্ষেত্রের সাবস্ট্রিং হয় Bad_Phrase তালিকাভুক্ত স্বতন্ত্র বাক্যাংশ সংখ্যা গণনা করেন Nameসালে SourceTableএবং তারপর মাঠে যে ফলাফলের স্থাপন Bad_Count

এই কোয়েরিটি কীভাবে আরও দ্রুত চালানো যায় সে সম্পর্কে আমি কিছু পরামর্শ চাই।


3
সুতরাং আপনি টেবিলটি 3K বার স্ক্যান করছেন, এবং সমস্ত 15 এমএম সারিগুলি 3K বারের মধ্যে সম্ভাব্যভাবে আপডেট করছেন এবং আপনি কি এটি দ্রুত হওয়ার প্রত্যাশা করছেন?
অ্যারন বারট্র্যান্ড

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

3
ম্যাথিউ, আপনি কি সম্পূর্ণ পাঠ্য সূচকে বিবেচনা করেছেন? আপনি কন্টেইনসের মতো জিনিস ব্যবহার করতে পারেন এবং এখনও সেই অনুসন্ধানের জন্য সূচীকরণের সুবিধা পান।
সোয়াশেক

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

2
@ সোয়াশেক ফুল-পাঠ্যটি বিবেচনা করা ভাল ধারণা (এটি আমি বিশ্বাস করি ২০০৫ সালে এটি নতুন, তাই এটি এখানে প্রযোজ্যও হতে পারে), তবে পোস্টার পূর্ণ-পাঠ্য সূচী শব্দের পরে যে অনুরোধটির জন্য জিজ্ঞাসা করেছিল তা প্রদান করা সম্ভব হবে না এবং না স্বেচ্ছাসেবী সাবস্ট্রিংস। অন্যভাবে বলেছেন, পূর্ণ-পাঠ্য "চমত্কার" শব্দের মধ্যে "পিঁপড়ে" এর জন্য কোনও মিল খুঁজে পাবে না। তবে এটি সম্ভবত ব্যবসায়ের প্রয়োজনীয়তাগুলি সংশোধন করা যেতে পারে যাতে পূর্ণ পাঠ্য প্রযোজ্য হয়।
জেফ প্যাটারসন

উত্তর:


21

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

একটি নকল ডেটা সেট তৈরি করতে নতুন স্ক্রিপ্ট এবং নতুন পদ্ধতির চেষ্টা করুন

টি এল; ডিআর

আমার মেশিনে এবং এই জাল ডেটা সেটটিতে, আসল পদ্ধতিটি চালাতে প্রায় 4 ঘন্টা সময় নেয় । প্রস্তাবিত নতুন পদ্ধতির জন্য প্রায় 10 মিনিট সময় লাগে , যথেষ্ট উন্নতি। প্রস্তাবিত পদ্ধতির সংক্ষিপ্তসার এখানে দেওয়া হল:

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


আসল পদ্ধতির: অ্যালগরিদমিক বিশ্লেষণ

মূল UPDATEবিবৃতিটির পরিকল্পনা থেকে , আমরা দেখতে পাচ্ছি যে কাজের পরিমাণ নামের সংখ্যার (15 মিমি) এবং বাক্যাংশের সংখ্যা (3 কে) উভয়ের ক্ষেত্রেই রৈখিকভাবে আনুপাতিক। সুতরাং আমরা যদি উভয় নাম এবং বাক্যাংশের সংখ্যা 10 দ্বারা এক করে করি তবে সামগ্রিক চলমান সময়টি 100 ডলার গতি হতে চলেছে।

কোয়েরিটি আসলে দৈর্ঘ্যের সমানুপাতিক name; এটি ক্যোয়ারী পরিকল্পনায় কিছুটা গোপন থাকা সত্ত্বেও, টেবিলের স্পুলটি অনুসন্ধানের জন্য এটি "মৃত্যুদণ্ডের সংখ্যা" এর মধ্য দিয়ে আসে। আসল পরিকল্পনায়, আমরা দেখতে পাচ্ছি যে এটি প্রতি একবার নয় name, তবে একবারে প্রতি চরিত্রের মধ্যে একবার অফসেট হয় name। সুতরাং রান-টাইম জটিলতায় এই পদ্ধতিটি ও ( # names* # phrases* name length)।

এখানে চিত্র বর্ণনা লিখুন


নতুন পদ্ধতির: কোড

এই কোডটি পুরো পেস্টবিনেও পাওয়া যায় তবে আমি সুবিধার জন্য এটি এখানে অনুলিপি করেছি। Pastebin এছাড়াও পূর্ণ পদ্ধতি সংজ্ঞা, যার মধ্যে রয়েছে @minIdএবং @maxIdভেরিয়েবল যে আপনার নিচে দেখুন বর্তমান ব্যাচ সীমানা নির্ধারণ করতে।

-- For each name, generate the string at each offset
DECLARE @maxBadPhraseLen INT = (SELECT MAX(LEN(phrase)) FROM Bad_Phrase)
SELECT s.id, sub.sub_name
INTO #SubNames
FROM (SELECT * FROM SourceTable WHERE id BETWEEN @minId AND @maxId) s
CROSS APPLY (
    -- Create a row for each substring of the name, starting at each character
    -- offset within that string.  For example, if the name is "abcd", this CROSS APPLY
    -- will generate 4 rows, with values ("abcd"), ("bcd"), ("cd"), and ("d"). In order
    -- for the name to be LIKE the bad phrase, the bad phrase must match the leading X
    -- characters (where X is the length of the bad phrase) of at least one of these
    -- substrings. This can be efficiently computed after indexing the substrings.
    -- As an optimization, we only store @maxBadPhraseLen characters rather than
    -- storing the full remainder of the name from each offset; all other characters are
    -- simply extra space that isn't needed to determine whether a bad phrase matches.
    SELECT TOP(LEN(s.name)) SUBSTRING(s.name, n.n, @maxBadPhraseLen) AS sub_name 
    FROM Numbers n
    ORDER BY n.n
) sub
-- Create an index so that bad phrases can be quickly compared for a match
CREATE CLUSTERED INDEX IX_SubNames ON #SubNames (sub_name)

-- For each name, compute the number of distinct bad phrases that match
-- By "match", we mean that the a substring starting from one or more 
-- character offsets of the overall name starts with the bad phrase
SELECT s.id, COUNT(DISTINCT b.phrase) AS bad_count
INTO #tempBadCounts
FROM dbo.Bad_Phrase b
JOIN #SubNames s
    ON s.sub_name LIKE b.phrase + '%'
GROUP BY s.id

-- Perform the actual update into a "bad_count_new" field
-- For validation, we'll compare bad_count_new with the originally computed bad_count
UPDATE s
SET s.bad_count_new = COALESCE(b.bad_count, 0)
FROM dbo.SourceTable s
LEFT JOIN #tempBadCounts b
    ON b.id = s.id
WHERE s.id BETWEEN @minId AND @maxId


নতুন পদ্ধতির: ক্যোয়ারী পরিকল্পনা

প্রথমত, আমরা প্রতিটি অক্ষর অফসেট থেকে শুরু করে স্ট্রিং তৈরি করি

এখানে চিত্র বর্ণনা লিখুন

তারপরে এই সাবস্ট্রিংগুলিতে একটি ক্লাস্টার্ড সূচক তৈরি করুন

এখানে চিত্র বর্ণনা লিখুন

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

এখানে চিত্র বর্ণনা লিখুন

শেষ অবধি, LEFT OUTER JOINকোনও খারাপ বাক্য খুঁজে পাওয়া যায়নি এমন কোনও নামের জন্য একটি 0 গণনা নির্ধারণ করতে একটি ব্যবহার করে প্রকৃত আপডেট বিবৃতিটি সম্পাদন করুন ।

এখানে চিত্র বর্ণনা লিখুন


নতুন পদ্ধতির: অ্যালগরিদমিক বিশ্লেষণ

নতুন পদ্ধতির দুটি ধাপে বিভক্ত করা যেতে পারে, প্রাক-প্রক্রিয়াজাতকরণ এবং ম্যাচিং। আসুন নিম্নলিখিত ভেরিয়েবলগুলি সংজ্ঞা দিন:

  • N নাম #
  • B = # খারাপ বাক্যাংশের
  • L = গড় নামের দৈর্ঘ্য, অক্ষরে

প্রাক-প্রক্রিয়াজাতকরণের পর্বটি সাবস্ট্রিংগুলি O(N*L * LOG(N*L))তৈরি করতে N*Lএবং তারপরে তাদেরকে বাছাই করার জন্য।

আসল O(B * LOG(N*L))মিলটি প্রতিটি খারাপ বাক্যাংশের জন্য সাবস্ট্রিংগুলিতে সন্ধান করার জন্য।

এইভাবে, আমরা একটি অ্যালগরিদম তৈরি করেছি যা খারাপ বাক্যাংশের সংখ্যার সাথে রৈখিকভাবে স্কেল করে না, একটি মূল পারফরম্যান্স আনলক করার সাথে সাথে আমরা 3K বাক্য এবং এর বাইরেও স্কেল করি। অন্যভাবে বলা হয়েছে, 300 টি খারাপ শব্দগুচ্ছ থেকে 3 কে খারাপ বাক্যাংশে যাওয়ার জন্য মূল প্রয়োগটি প্রায় 10x লাগে takes আমরা যদি 3 কে খারাপ বাক্যাংশ থেকে 30 কে যেতে যাই তবে একইভাবে এটি আরও 10x সময় লাগবে। তবে নতুন বাস্তবায়নটি উপ-রৈখিকভাবে স্কেল করবে এবং 30K বাজে বাক্যাংশগুলিকে ছোট করে দিলে 3K খারাপ বাক্যাংশগুলিতে পরিমাপ করা সময়টি 2x এরও কম সময় নেয়।


অনুমান / গুহাত

  • আমি সামগ্রিক কাজটি পরিমিত আকারের ব্যাচগুলিতে ভাগ করছি। এটি উভয় পদ্ধতির জন্য সম্ভবত এটি একটি ভাল ধারণা, তবে নতুন পদ্ধতির জন্য এটি বিশেষত গুরুত্বপূর্ণ যাতে SORTসাবস্ট্রিংগুলি প্রতিটি ব্যাচের জন্য স্বতন্ত্র এবং স্মৃতিতে সহজেই ফিট করে। আপনি প্রয়োজন হিসাবে ব্যাচের আকারটি পরিচালনা করতে পারেন তবে সমস্ত ব্যাগের 15 মিমি সারি চেষ্টা করা বুদ্ধিমানের কাজ হবে না।
  • আমি এসকিউএল ২০১৪-তে আছি, এসকিউএল 2005 নয়, যেহেতু আমার এসকিউএল 2005 মেশিনে অ্যাক্সেস নেই। এসকিউএল 2005-এ উপলব্ধ না এমন কোনও সিনট্যাক্স ব্যবহার না করার বিষয়ে আমি সতর্কতা অবলম্বন করেছি, তবে এসকিউএল 2012+ তে টেম্পডবিটি অলস লেখার বৈশিষ্ট্য এবং এসকিউএল ২০১৪ এর সমান্তরাল নির্বাচন অন্তর্ভুক্তি থেকে আমি কোনও সুবিধা পাচ্ছি
  • নাম এবং বাক্যাংশ উভয়ের দৈর্ঘ্য নতুন পদ্ধতির পক্ষে মোটামুটি গুরুত্বপূর্ণ। আমি ধরে নিচ্ছি যে খারাপ বাক্যাংশগুলি সাধারণত মোটামুটি ছোট হয় কারণ এটি বাস্তব-বিশ্বের ব্যবহারের ক্ষেত্রে মেলে match নামগুলি খারাপ বাক্যগুলির তুলনায় বেশ খানিকটা দীর্ঘ, তবে ধরে নেওয়া হয় হাজার হাজার অক্ষর নয়। আমি মনে করি এটি একটি ন্যায্য ধারণা, এবং দীর্ঘ নামের স্ট্রিংগুলি আপনার মূল পদ্ধতির পাশাপাশি ধীর করে দেবে।
  • উন্নয়নের কিছু অংশ (তবে এর সবের কাছাকাছি কোথাও নেই) এই কারণে যে নতুন পদ্ধতির সমান্তরালতাকে পুরাতন পদ্ধতির (যা একক থ্রেডযুক্ত চালিত) এর চেয়ে বেশি কার্যকরভাবে লাভ করতে পারে to আমি একটি কোয়াড কোর ল্যাপটপে আছি, সুতরাং এই কোরগুলি ব্যবহার করতে পারে এমন পদ্ধতির সাথে ভাল লাগছে।


সম্পর্কিত ব্লগ পোস্ট

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


6

আসুন হারুন বার্ট্র্যান্ডের দ্বারা উত্থাপিত সুস্পষ্ট বিষয়টিকে এক সেকেন্ডের মন্তব্যে রাখি :

সুতরাং আপনি টেবিলটি 3K বার স্ক্যান করছেন, এবং সমস্ত 15 এমএম সারিগুলি 3K বারের মধ্যে সম্ভাব্যভাবে আপডেট করছেন এবং আপনি কি এটি দ্রুত হওয়ার প্রত্যাশা করছেন?

আপনার subquery উভয় পক্ষের ওয়াইল্ড কার্ড ব্যবহার করে তা নাটকীয়ভাবে ব্যর্থতার সাথে প্রভাব ফেলে । এই ব্লগ পোস্ট থেকে একটি উদ্ধৃতি নিতে:

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

প্রতিটি "খারাপ শব্দ" এবং "পণ্য" এর জন্য "বাদাম" শব্দটি SourceTableসরিয়ে ফেলুন, তারপরে হারুনের মন্তব্যের সাথে এটি একত্রিত করুন এবং আপনার বর্তমান অ্যালগোরিদম ব্যবহার করে এটি দ্রুত চালিত করা কেন এটি অত্যন্ত কঠোর (অসম্ভব পড়ুন) তা আপনার শুরু করা উচিত ।

আমি কয়েকটি বিকল্প দেখতে পাচ্ছি:

  1. এমন একটি দানব সার্ভার কিনতে ব্যবসায়ের প্রতিশ্রুতি দিন যাতে এত শক্তি থাকে যে এটি শিয়ার ব্রুট ফোর্স দ্বারা ক্যোয়ারীটি অতিক্রম করে। (এটি ঘটবে না তাই আপনার আঙ্গুলগুলি ক্রস করুন অন্যান্য বিকল্পগুলি আরও ভাল)
  2. আপনার বিদ্যমান অ্যালগরিদম ব্যবহার করে, ব্যথাটি একবার গ্রহণ করুন এবং তারপরে এটি ছড়িয়ে দিন। এটি সন্নিবেশ করানো মন্দ শব্দগুলি গণনা করার সাথে জড়িত যা যা সন্নিবেশগুলিকে কমিয়ে দেবে এবং যখন কোনও নতুন খারাপ শব্দ প্রবেশ / সন্ধান করা হয় তখন কেবল পুরো টেবিলটি আপডেট করে।
  3. জেফের উত্তরটি আলিঙ্গন করুন । এটি একটি দুর্দান্ত অ্যালগরিদম, এবং আমি যে কোনও কিছু নিয়ে আসতাম তার থেকে অনেক ভাল।
  4. বিকল্প 2 করুন তবে জেফের সাথে আপনার অ্যালগরিদমটি বিকল্প করুন।

আপনার প্রয়োজনীয়তার উপর নির্ভর করে আমি বিকল্প 3 বা 4 এর জন্য সুপারিশ করব।


0

প্রথম এটি একটি অদ্ভুত আপডেট

Update [SourceTable]  
   Set [SourceTable].[Bad_Count] = [fix].[count]
  from [SourceTable] 
  join ( Select count(*) 
           from [Bad_Phrase]  
          where [SourceTable].Name like '%' + [Bad_Phrase].[PHRASE] + '%')

'%' + এর মতো [খারাপ প্রতিক্রিয়া] [[PHRASE] আপনাকে মারছে
যা সূচক ব্যবহার করতে পারে না

গতির জন্য ডেটা ডিজাইনটি অনুকূল নয়
আপনি কি [ব্যাডপ্রেস] ভেঙে ফেলতে পারেন? [ফরাসী ভাষায়] একক বাক্যাংশ (শব্দ) / শব্দে পরিণত করুন?
যদি একই বাক্যাংশ / শব্দটি
একাধিকরূপে উপস্থিত হয় আপনি এটির একটি উচ্চ গণনা রাখতে চাইলে একবারে একবারে এটি প্রবেশ করতে পারেন তাই খারাপ ফারাশে সারিগুলির সংখ্যা বাড়তে পারে
যদি আপনি পারেন তবে এটি আরও দ্রুত হবে faster

Update [SourceTable]  
   Set [SourceTable].[Bad_Count] = [fix].[count]
  from [SourceTable] 
  join ( select [PHRASE], count(*) as count 
           from [Bad_Phrase] 
          group by [PHRASE] 
       ) as [fix]
    on [fix].[PHRASE] = [SourceTable].[name]  
 where [SourceTable].[Bad_Count] <> [fix].[count]

নিশ্চিত নয় যে 2005 এটি সমর্থন করে তবে সম্পূর্ণ পাঠ্য সূচক এবং ব্যবহার করে


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

1
@ এরিক "আপনি কি [ব্যাডপ্রেস] ভেঙে ফেলতে পারবেন? সত্যিই আপনি কি মনে করেন না যে কোনও ডেটা ডিজাইনের সমাধান হতে পারে? যদি উদ্দেশ্য খারাপ জিনিস সন্ধান করা হয় তবে এক বা একাধিক গণনা সহ "এরিক" যথেষ্ট।
পাপারাজ্জো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.