বন্যপ্রাণভাবে সঠিকভাবে সারি অনুমানের কারণে ধীরে ধীরে পুরো পাঠ্য সন্ধান করুন


10

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

ডাটাবেস স্কিমা নীচে, এটি প্রায় 2 মিলিয়ন সারি:

rt4 = # \ d + সংযুক্তি
                                                    সারণী "পাবলিক.ট্যাচমেন্টস"
     কলাম | প্রকার | সংশোধক | স্টোরেজ | বিবরণ
----------------- + + ----------------------------- - -------------------------------------------------- ------ + + ---------- + + -------------
 আইডি | পূর্ণসংখ্যা | পরেরটি নাল ডিফল্ট নয় ('সংযুক্তি_আইডি_সেক' :: রেগক্লাস) | সরল |
 লেনদেন | পূর্ণসংখ্যা | নাল না | সরল |
 অভিভাবক | পূর্ণসংখ্যা | নাল ডিফল্ট 0 | সরল |
 ম্যাসেজিড | চরিত্রের পার্থক্য (160) | | প্রসারিত |
 বিষয় | চরিত্রের পার্থক্য (255) | | প্রসারিত |
 ফাইলের নাম | চরিত্রের পার্থক্য (255) | | প্রসারিত |
 কন্টেন্টটাইপ | চরিত্রের পার্থক্য (80) | | প্রসারিত |
 কনটেনটকোডিং | চরিত্রের পার্থক্য (80) | | প্রসারিত |
 সামগ্রী | পাঠ্য | | প্রসারিত |
 শিরোনাম | পাঠ্য | | প্রসারিত |
 স্রষ্টা | পূর্ণসংখ্যা | নাল ডিফল্ট 0 | সরল |
 তৈরি | সময় অঞ্চল ছাড়া টাইমস্ট্যাম্প | | সরল |
 কন্টেন্টইন্ডেক্স | tsvector | | প্রসারিত |
ইনডেক্সে:
    "সংযুক্তি_পকি" প্রাথমিক কী, বিটি (আইডি)
    "সংযুক্তি 1" বিটিরি (পিতামাতা)
    "সংযুক্তি 2" বিটিরি (লেনদেন)
    "সংযুক্তি 3" বিটিরি (পিতা বা মাতা, লেনদেন)
    "কন্টেন্টইন্ডেক্স_আইডিএক্স" জিন (কন্টেন্টইন্ডেক্স)
ওআইডি রয়েছে: না

আমি খুব দ্রুত (<1s) এর সাথে একটি ডেটাবেসকে নিজের ক্যোয়ারী দিয়ে জিজ্ঞাসা করতে পারি যেমন:

select objectid
from attachments
join transactions on attachments.transactionid = transactions.id
where contentindex @@ to_tsquery('frobnicate');

যাইহোক, আরটি একই টেবিলটিতে একটি পূর্ণ পাঠ্য সূচি অনুসন্ধান করার কথা বলে এমন একটি ক্যোয়ারি চালায়, সাধারণত এটি সম্পূর্ণ হতে কয়েকশ সেকেন্ড সময় নেয়। কোয়েরি বিশ্লেষণ আউটপুট নীচে:

প্রশ্ন

SELECT COUNT(DISTINCT main.id)
FROM Tickets main
JOIN Transactions Transactions_1 ON ( Transactions_1.ObjectType = 'RT::Ticket' )
                                AND ( Transactions_1.ObjectId = main.id )
JOIN Attachments Attachments_2 ON ( Attachments_2.TransactionId = Transactions_1.id )
WHERE (main.Status != 'deleted')
AND ( ( ( Attachments_2.ContentIndex @@ plainto_tsquery('frobnicate') ) ) )
AND (main.Type = 'ticket')
AND (main.EffectiveId = main.id);

EXPLAIN ANALYZE আউটপুট

                                                                             QUERY প্ল্যান 
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------
 সমষ্টি (ব্যয় = 51210.60..51210.61 সারি = 1 প্রস্থ = 4) (আসল সময় = 477778.806..477778.806 সারি = 1 লুপ = 1)
   -> নেস্টেড লুপ (ব্যয় = 0.00..51210.57 সারি = 15 প্রস্থ = 4) (আসল সময় = 17943.986..477775.174 সারি = 4197 লুপ = 1)
         -> নেস্টেড লুপ (ব্যয় = 0.00..40643.08 সারি = 6507 প্রস্থ = 8) (আসল সময় = 8.526..20610.380 সারি = 1714818 লুপ = 1)
               -> টিকিট মূলগুলিতে সিক স্ক্যান (ব্যয় = 0.00..9818.37 সারি = 598 প্রস্থ = 8) (আসল সময় = 0.008..256.042 সারি = 96990 লুপ = 1)
                     ফিল্টার: ((স্থিতি) :: পাঠ্য 'মোছা' :: পাঠ্য) এবং (আইডি = কার্যকর) এবং ((টাইপ) :: পাঠ্য = 'টিকিট' :: পাঠ্য))
               -> লেনদেনের ক্ষেত্রে লেনদেন 1 এ সূচি স্ক্যান 1 (ব্যয় = 0.00..51.36 সারি = 15 প্রস্থ = 8) (প্রকৃত সময় = 0.102..0.202 সারি = 18 লুপ = 96990)
                     সূচকের অবস্থা: ((অবজেক্ট টাইপ) :: পাঠ্য = 'আরটি :: টিকিট' :: পাঠ্য) এবং (আপত্তিযুক্ত = মূল.আইডি)
         -> সংযুক্তিগুলির সংযুক্তিগুলিতে সংযুক্তি 2 ব্যবহার করে সূচক স্ক্যান করুন (ব্যয় = 0.00..1.61 সারি = 1 প্রস্থ = 4) (প্রকৃত সময় = 0.266..0.266 সারি = 0 লুপ = 1714818)
               সূচকের অবস্থা:
               ফিল্টার: (কন্টেন্টইন্ডেক্স @@ প্লেইনটো_টেস্কোয়ারি ('ফ্রোবনেট' :: টেক্সট))
 মোট রানটাইম: 477778.883 এমএস

যতদূর আমি বলতে পারি, সমস্যাটি মনে হচ্ছে এটি contentindexক্ষেত্রের ( contentindex_idx) তৈরি করা সূচকটি ব্যবহার করছে না , বরং এটি সংযুক্তি টেবিলের সাথে সংখ্যক মিলের সারিগুলিতে একটি ফিল্টার করছে। বর্ণিত আউটপুটে সারি গণনাগুলি সাম্প্রতিককালের পরেও ANALYZE: বুনোভাবে ভুল বলে মনে হচ্ছে : আনুমানিক সারিগুলি = 6507 প্রকৃত সারি = 1714818।

এর সাথে কোথায় যেতে হবে আমি সত্যিই নিশ্চিত নই।


আপগ্রেড করলে অতিরিক্ত সুবিধা পাওয়া যাবে। প্রচুর সাধারণ উন্নতি ছাড়াও , বিশেষত: 9.2 সূচি-কেবল স্ক্যান এবং স্কেলিবিলিটিতে উন্নতি করতে পারে। আসন্ন 9.4 জিএন সূচকগুলির জন্য বড় বিকাশ আনতে চলেছে।
এরউইন ব্র্যান্ডস্টেটার

উত্তর:


5

এটি এক হাজার এবং এক উপায়ে উন্নত করা যায়, তবে এটি মিলিসেকেন্ডের বিষয় হওয়া উচিত ।

আরও ভাল প্রশ্ন

কুয়াশা মুছে ফেলার জন্য এটি কেবলমাত্র আপনার ক্যোয়ারী উপকরণ এবং পুনরায় ফর্ম্যাট করা হয়েছে:

SELECT count(DISTINCT t.id)
FROM   tickets      t
JOIN   transactions tr ON tr.objectid = t.id
JOIN   attachments  a  ON a.transactionid = tr.id
WHERE  t.status <> 'deleted'
AND    t.type = 'ticket'
AND    t.effectiveid = t.id
AND    tr.objecttype = 'RT::Ticket'
AND    a.contentindex @@ plainto_tsquery('frobnicate');

আপনার ক্যোয়ারিতে বেশিরভাগ সমস্যা প্রথম দুটি টেবিলের মধ্যে রয়েছে ticketsএবং transactionsযা প্রশ্ন থেকে অনুপস্থিত। আমি শিক্ষিত অনুমান দিয়ে পূরণ করছি।

  • t.status, t.objecttypeএবং tr.objecttypeসম্ভবত না হওয়া উচিত text, তবে enumবা সম্ভবত কিছু খুব ছোট মান একটি বর্ণন সারণী উল্লেখ করে।

EXISTS আধা-যোগ

ধরে tickets.idনেওয়া প্রাথমিক কী, এই পুনর্লিখনের ফর্মটি আরও সস্তা হওয়া উচিত:

SELECT count(*)
FROM   tickets t
WHERE  status <> 'deleted'
AND    type = 'ticket'
AND    effectiveid = id
AND    EXISTS (
   SELECT 1
   FROM   transactions tr
   JOIN   attachments  a ON a.transactionid = tr.id
   WHERE  tr.objectid = t.id
   AND    tr.objecttype = 'RT::Ticket'
   AND    a.contentindex @@ plainto_tsquery('frobnicate')
   );

সারিগুলিকে দুটি 1: n এর সাথে গুন করার পরিবর্তে কেবল এক সাথে একাধিক ম্যাচ ভেঙে ফেলার জন্য, count(DISTINCT id)একটি EXISTSসেমি-জয়েন্ট ব্যবহার করুন , যা প্রথম ম্যাচটি পাওয়া মাত্রই আরও তাকাতে থামতে পারে এবং একই সাথে চূড়ান্ত DISTINCTপদক্ষেপকে অপ্রচলিত করে । প্রতি ডকুমেন্টেশন:

কমপক্ষে একটি সারি ফিরে আসে কিনা তা নির্ধারণের জন্য সাধারণত সাবকোরিটি কেবল দীর্ঘ পর্যাপ্ত সম্পাদন করা হবে, সম্পূর্ণ করার সমস্ত উপায় নয়।

কার্যকারিতা নির্ভর করে টিকিটপ্রতি কতগুলি লেনদেন এবং প্রতি লেনদেনের সংযুক্তি রয়েছে।

এর সাথে যোগদানের ক্রম নির্ধারণ করুন join_collapse_limit

আপনি যদি জানেন যে জন্য আপনার অনুসন্ধান শব্দ attachments.contentindexহয় খুব নির্বাচনী - QUERY এ অন্যান্য শর্ত চেয়ে বেশি নির্বাচনী (যা সম্ভবত, কিন্তু না 'সমস্যা' এর জন্য 'frobnicate' ক্ষেত্রে দেখা যায়), আপনি যোগদান করে ক্রম জোর করতে পারেন। ক্যোয়ারী পরিকল্পনাকারী খুব সাধারণ শব্দগুলি বাদ দিয়ে নির্দিষ্ট শব্দের বাছাইয়ের বিষয়টি খুব কমই বিচার করতে পারেন। প্রতি ডকুমেন্টেশন:

join_collapse_limit( integer)

[...]
যেহেতু ক্যোয়ারী পরিকল্পনাকারী সর্বদা অনুকূল যোগদানের ক্রমটি পছন্দ করে না, উন্নত ব্যবহারকারীগণ অস্থায়ীভাবে এই ভেরিয়েবলটি 1 এ সেট করতে পারবেন এবং তারপরে তারা স্পষ্টভাবে ইচ্ছাকৃত জোড় ক্রমটি নির্দিষ্ট করতে পারবেন।

SET LOCALএটি কেবল বর্তমান লেনদেনের জন্য সেট করার উদ্দেশ্যে ব্যবহার করুন ।

BEGIN;
SET LOCAL join_collapse_limit = 1;

SELECT count(DISTINCT t.id)
FROM   attachments  a                              -- 1st
JOIN   transactions tr ON tr.id = a.transactionid  -- 2nd
JOIN   tickets      t  ON t.id = tr.objectid       -- 3rd
WHERE  t.status <> 'deleted'
AND    t.type = 'ticket'
AND    t.effectiveid = t.id
AND    tr.objecttype = 'RT::Ticket'
AND    a.contentindex @@ plainto_tsquery('frobnicate');

ROLLBACK; -- or COMMIT;

WHEREশর্তের ক্রম সর্বদা অপ্রাসঙ্গিক। এখানে যোগদানের ক্রমটি প্রাসঙ্গিক।

বা "অপশন 2" এ ব্যাখ্যা করেছেন @ জাজানেসের মতো একটি সিটিই ব্যবহার করুন অনুরূপ প্রভাব জন্য।

ইনডেক্সে

বি-ট্রি সূচি

ticketsবেশিরভাগ প্রশ্নের সাথে অভিন্নভাবে ব্যবহৃত হয় এমন সমস্ত শর্তাদি নিন এবং এতে একটি আংশিক সূচক তৈরি করুন tickets:

CREATE INDEX tickets_partial_idx
ON tickets(id)
WHERE  status <> 'deleted'
AND    type = 'ticket'
AND    effectiveid = id;

শর্তগুলির মধ্যে একটি যদি পরিবর্তনশীল হয় তবে এটিকে WHEREশর্ত থেকে বাদ দিন এবং তার পরিবর্তে কলামটি সূচী কলাম হিসাবে প্রিন্ট করুন।

অন্য একটি transactions:

CREATE INDEX transactions_partial_idx
ON transactions(objecttype, objectid, id)

তৃতীয় কলামটি কেবলমাত্র ইনডেক্স-কেবল স্ক্যানগুলি সক্ষম করতে।

এছাড়াও, যেহেতু আপনার দুটি সংখ্যক কলামের সাথে এই যৌগিক সূচক রয়েছে attachments:

"attachments3" btree (parent, transactionid)

এই অতিরিক্ত সূচকটি সম্পূর্ণ বর্জ্য , এটি মুছুন:

"attachments1" btree (parent)

বিবরণ:

জিআইএন সূচক

transactionidএটিকে আরও কার্যকর করার জন্য আপনার জিআইএন সূচকগুলিতে যুক্ত করুন । এটি অন্য একটি রূপালী বুলেট হতে পারে , কারণ এটি সম্ভাব্যভাবে কেবলমাত্র সূচি-স্ক্যানকে মঞ্জুরি দেয়, বড় টেবিলে পরিদর্শন পুরোপুরি বাদ দেয় ।
অতিরিক্ত মডিউল দ্বারা সরবরাহ করা আপনার অতিরিক্ত অপারেটর ক্লাসগুলির প্রয়োজন btree_gin। বিস্তারিত নির্দেশাবলী:

"contentindex_idx" gin (transactionid, contentindex)

একটি integerকলামের 4 বাইট সূচকটি আরও বড় করে না। এছাড়াও, ভাগ্যক্রমে আপনার জন্য, জিআইএন সূচকগুলি একটি গুরুত্বপূর্ণ দিকের বি-ট্রি সূচকগুলি থেকে আলাদা। প্রতি ডকুমেন্টেশন:

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

বোল্ড জোর আমার। সুতরাং আপনার কেবল একটি (বড় এবং কিছুটা ব্যয়বহুল) জিআইএন সূচক প্রয়োজন।

ছক সংজ্ঞা

integer not null columnsসামনের দিকে সরান । এটি স্টোরেজ এবং কর্মক্ষমতা উপর বেশ কয়েকটি ছোটখাটো ইতিবাচক প্রভাব ফেলে। এক্ষেত্রে সারি প্রতি 4 - 8 বাইট সাশ্রয় করে।

                      Table "public.attachments"
         Column      |            Type             |         Modifiers
    -----------------+-----------------------------+------------------------------
     id              | integer                     | not null default nextval('...
     transactionid   | integer                     | not null
     parent          | integer                     | not null default 0
     creator         | integer                     | not null default 0  -- !
     created         | timestamp                   |                     -- !
     messageid       | character varying(160)      |
     subject         | character varying(255)      |
     filename        | character varying(255)      |
     contenttype     | character varying(80)       |
     contentencoding | character varying(80)       |
     content         | text                        |
     headers         | text                        |
     contentindex    | tsvector                    |

3

বিকল্প 1

ইফেক্টিফিকআইডি এবং আইডির মধ্যে সম্পর্কের প্রকৃত স্বরূপ সম্পর্কে পরিকল্পনাকারীর কোনও অন্তর্দৃষ্টি নেই এবং তাই সম্ভবত এই ধারাটিটি মনে করে:

main.EffectiveId = main.id

এটি আসলে তুলনায় অনেক বেশি নির্বাচনী হতে চলেছে। যদি এটি আমার মনে হয় তবে এটি কার্যকর, প্রায় সবসময়ই মেইন.ইডের সমান, তবে পরিকল্পনাকারী তা জানেন না।

এই ধরণের সম্পর্ক সংরক্ষণের একটি সম্ভবত আরও ভাল উপায় হ'ল কার্যকরভাবে আইডি হিসাবে একইভাবে "বোঝার জন্য এফেক্টিআইডিএডের NULL মানটি সংজ্ঞায়িত করা এবং কোনও তাত্পর্য থাকলেই সেখানে কিছু সংরক্ষণ করা।

ধরে নিই যে আপনি আপনার স্কিমাটি পুনর্গঠিত করতে চান না, আপনি এই ধারাটিকে আবার কিছু লিখে আবার এটিকে ঘিরে ধরার চেষ্টা করতে পারেন:

main.EffectiveId+0 between main.id+0 and main.id+0

পরিকল্পনাকারী ধরে নিতে পারে যে এটি betweenএকটি সমতার চেয়ে কম নির্বাচনী এবং এটি এটি বর্তমান জাল থেকে বেরিয়ে আসতে যথেষ্ট।

বিকল্প 2

আর একটি পদ্ধতির সিটিই ব্যবহার করা:

WITH attach as (
    SELECT * from Attachments 
        where ContentIndex @@ plainto_tsquery('frobnicate') 
)
<rest of query goes here, with 'attach' used in place of 'Attachments'>

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

বিকল্প 3

খারাপ সারি অনুমানটি আরও তদন্ত করতে, আপনার নীচের কোয়েরিটি সমস্ত 2 ^ 3 = 8 বিভিন্ন মন্তব্য এবং মন্তব্যগুলিতে প্রকাশের অনুমতিতে চালানো উচিত। এটি খারাপ অনুমানটি কোথা থেকে আসছে তা নির্ধারণে সহায়তা করবে।

explain analyze
SELECT * FROM Tickets main WHERE 
   main.Status != 'deleted' AND 
   main.Type = 'ticket' AND 
   main.EffectiveId = main.id;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.