পোস্টগ্রেএসকিউএল-এ পারফর্ম করার জন্য দর্শনগুলি কি ক্ষতিকারক?


45

নীচে ডিবি ডিজাইন সম্পর্কে একটি বইয়ের একটি উদ্ধৃত অংশ রয়েছে (ডেটাবেস ডিজাইনের ISNN: 0-7645-7490-6):

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

নিম্নলিখিত পোস্টগ্র্যাসকিউএল 9.5 ডকুমেন্টেশনের একটি সংক্ষিপ্তসার:

দৃশ্যের উদার ব্যবহার করা ভাল এসকিউএল ডাটাবেস ডিজাইনের একটি মূল দিক। সামঞ্জস্যপূর্ণ ইন্টারফেসের পিছনে দৃশ্যগুলি আপনাকে আপনার টেবিলগুলির কাঠামোর বিশদটি সজ্জিত করতে দেয়, যা আপনার অ্যাপ্লিকেশনটির বিকাশের সাথে সাথে পরিবর্তিত হতে পারে।

দুটি উত্স একে অপরের বিরোধিতা বলে মনে হচ্ছে ("ভিউগুলি দিয়ে নকশা করবেন না" "ভিউগুলি দিয়ে নকশা করুন")।

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

আমার ব্যাখ্যাটি কি সঠিক এবং পিজি কোথাও কোথাও দৃষ্টিতে এবং এর বাইরে ক্লজ যুক্ত করে? নাকি এগুলি একের পর এক আলাদা করে চালায়? কোন সংক্ষিপ্ত, স্ব রয়েছে, সঠিক (সংকলনযোগ্য), উদাহরণ?


আমি মনে করি যে প্রশ্নটি সঠিক নয় কারণ উভয় সূত্র একই জিনিস সম্পর্কে কথা বলছে না। প্রথমটি কোনও ভিউ থেকে ক্যোয়ারির সাথে সম্পর্কিত এবং ফিল্টার প্রয়োগের পরে: SELECT * FROM my_view WHERE my_column = 'blablabla';দ্বিতীয়টি আপনার ডেটা মডেলটিকে যে অ্যাপ্লিকেশনটি ব্যবহার করে তাতে স্বচ্ছ করতে ভিউগুলি ব্যবহার করার বিষয়ে। প্রথম উত্সগুলি আপনাকে WHERE my_column = 'blablabla'দৃশ্যের সংজ্ঞা অনুযায়ী ফিল্টারটি অন্তর্ভুক্ত করতে নির্দেশ করে , ফলস্বরূপ একটি কার্যকর কার্যকর পরিকল্পনার ফলস্বরূপ।
ইমেজ

উত্তর:


49

বইটি ভুল।

একটি দৃশ্য থেকে নির্বাচন হয় ঠিক যেমন দ্রুত বা অন্তর্নিহিত SQL বক্তব্য চলমান হিসাবে ধীর - আপনি সহজে ব্যবহার করে পরীক্ষা করতে পারবেন explain analyze

পোস্টগ্র্রেস অপ্টিমাইজার (এবং অন্যান্য অনেক আধুনিক ডিবিএমএসের জন্য অনুকূলিতকরণ) দৃশ্যের পূর্বাভাসগুলিকে আসল ভিউ স্টেটমেন্টের দিকে ঠেলে দিতে সক্ষম হবে - তবে শর্ত থাকে যে এটি একটি সাধারণ বিবৃতি (আবার এটি ব্যবহার করে যাচাই করা যেতে পারে explain analyze)।

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


3
প্রস্তাবিত কাউন্টার-উত্তরগুলির কয়েকটি দেওয়া, আপনি সাধারণ বক্তব্যটি কী তা সম্পর্কে কিছুটা ব্যাখ্যা করতে পারেন ।
আরডিফোজ 20

আপনি কীভাবে explain analyzeবিবৃতিটি ব্যবহার করবেন তা ব্যাখ্যা করতে পারেন ?
ডাস্টিন মিশেল

@ ডাস্টিনমিচেলস: ম্যানুয়ালটি দেখুন: postgresql.org/docs/current/used-explain.html
a_horse_with_no_name

19

আপনাকে @a_horse কী ব্যাখ্যা করেছে তার একটি উদাহরণ দিতে :

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

খুব সাধারণ উদাহরণ,
তথ্য স্কিমা থেকে একটি টেবিলের সমস্ত দৃশ্যমান কলাম পেতে ...

SELECT column_name
FROM   information_schema.columns
WHERE  table_name = 'big'
AND    table_schema = 'public';

... সিস্টেম ক্যাটালগ থেকে:

SELECT attname
FROM   pg_catalog.pg_attribute
WHERE  attrelid = 'public.big'::regclass
AND    attnum > 0
AND    NOT attisdropped;

উভয়ের জন্য ক্যোয়ারী পরিকল্পনা এবং সম্পাদনের সময়টির তুলনা করুন EXPLAIN ANALYZE

  • প্রথম ক্যোয়ারী ভিউয়ের উপর ভিত্তি করে তৈরি হয়েছে information_schema.columns, যা একাধিক টেবিলের সাথে যোগ দেয় আমাদের এর জন্য মোটেও প্রয়োজন হয় না।

  • দ্বিতীয় ক্যোয়ারী কেবল একটি টেবিল স্ক্যান করে pg_catalog.pg_attribute, অতএব আরও দ্রুত। (তবে প্রথম ক্যোয়ারিতে এখনও সাধারণ ডিবিতে কয়েকটি এমএস প্রয়োজন needs)

বিবরণ:


7

সম্পাদনা করুন:

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

আমি এখন মনে করি মূল প্রশ্নের আরও ভাল উত্তর আছে।

মূল প্রশ্নটি হল দৃষ্টিভঙ্গি ব্যবহারের জন্য গাইডিং অনুশীলন হওয়া উচিত কিনা (উদাহরণস্বরূপ, রুটিনগুলিতে এসকিউএল পুনরাবৃত্তি করা যা দু'বার বা তার বেশি বজায় রাখা দরকার)।

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

আমার উইন্ডো ফাংশনের জটিলতা অপ্রয়োজনীয়। এর জন্য ব্যাখ্যা পরিকল্পনা:

SELECT DISTINCT ts.train_service_key,
            pc.assembly_key,
            count(*) OVER 
              (PARTITION BY ts.train_service_key) AS train_records
FROM staging.train_service ts
   JOIN staging.portion_consist pc 
     USING (ds_code, train_service_key)
WHERE assembly_key = '185132';

এর চেয়ে অনেক কম ব্যয়বহুল:

SELECT *
FROM (SELECT DISTINCT ts.train_service_key,
            pc.assembly_key,
            count(*) OVER
              (PARTITION BY ts.train_service_key) AS train_records
FROM staging.train_service ts
   JOIN staging.portion_consist pc
     USING (ds_code, train_service_key)) AS query
WHERE assembly_key = '185132';

আশা করি এটি কিছুটা সুনির্দিষ্ট এবং সহায়ক।

আমার সাম্প্রতিক অভিজ্ঞতায় (আমাকে এই প্রশ্নটি সন্ধানের কারণ হিসাবে), উপরোক্ত গৃহীত উত্তরগুলি সমস্ত পরিস্থিতিতে সঠিক নয়। আমার একটি অপেক্ষাকৃত সাধারণ ক্যোয়ারী রয়েছে যাতে একটি উইন্ডো ফাংশন অন্তর্ভুক্ত:

SELECT DISTINCT ts.train_service_key,
                pc.assembly_key,
                dense_rank() OVER (PARTITION BY ts.train_service_key
                ORDER BY pc.through_idx DESC, pc.first_portion ASC,
               ((CASE WHEN (NOT ts.primary_direction)
                 THEN '-1' :: INTEGER
                 ELSE 1
                 END) * pc.first_seq)) AS coach_block_idx
FROM (staging.train_service ts
JOIN staging.portion_consist pc USING (ds_code, train_service_key))

যদি আমি এই ফিল্টারটি যুক্ত করি:

where assembly_key = '185132'

আমি যে ব্যাখ্যা পরিকল্পনা পাই তা নীচে:

QUERY PLAN
Unique  (cost=11562.66..11568.77 rows=814 width=43)
  ->  Sort  (cost=11562.66..11564.70 rows=814 width=43)
    Sort Key: ts.train_service_key, (dense_rank() OVER (?))
    ->  WindowAgg  (cost=11500.92..11523.31 rows=814 width=43)
          ->  Sort  (cost=11500.92..11502.96 rows=814 width=35)
                Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
                ->  Nested Loop  (cost=20.39..11461.57 rows=814 width=35)
                      ->  Bitmap Heap Scan on portion_consist pc  (cost=19.97..3370.39 rows=973 width=38)
                            Recheck Cond: (assembly_key = '185132'::text)
                            ->  Bitmap Index Scan on portion_consist_assembly_key_index  (cost=0.00..19.72 rows=973 width=0)
                                  Index Cond: (assembly_key = '185132'::text)
                      ->  Index Scan using train_service_pk on train_service ts  (cost=0.43..8.30 rows=1 width=21)
                            Index Cond: ((ds_code = pc.ds_code) AND (train_service_key = pc.train_service_key))

এটি ট্রেন পরিষেবা টেবিলের প্রাথমিক কী সূচক এবং অংশ_সংশ্লিষ্ট তালিকায় একটি অ-অনন্য সূচক ব্যবহার করছে। এটি 90ms এ কার্যকর করে।

আমি একটি ভিউ তৈরি করেছি (একে একে পরিষ্কার করার জন্য এটি এখানে আটকানো হয়েছে তবে এটি একটি দৃশ্যে আক্ষরিকভাবে জিজ্ঞাস্য):

CREATE OR REPLACE VIEW staging.v_unit_coach_block AS
SELECT DISTINCT ts.train_service_key,
            pc.assembly_key,
            dense_rank() OVER (PARTITION BY ts.train_service_key
              ORDER BY pc.through_idx DESC, pc.first_portion ASC, (
                (CASE
              WHEN (NOT ts.primary_direction)
                THEN '-1' :: INTEGER
              ELSE 1
              END) * pc.first_seq)) AS coach_block_idx
 FROM (staging.train_service ts
  JOIN staging.portion_consist pc USING (ds_code, train_service_key))

আমি যখন অভিন্ন ফিল্টারটি দিয়ে এই দর্শনটি জিজ্ঞাসা করি:

select * from staging.v_unit_coach_block
where assembly_key = '185132';

এই ব্যাখ্যা পরিকল্পনা:

QUERY PLAN
Subquery Scan on v_unit_coach_block  (cost=494217.13..508955.10     rows=3275 width=31)
Filter: (v_unit_coach_block.assembly_key = '185132'::text)
 ->  Unique  (cost=494217.13..500767.34 rows=655021 width=43)
    ->  Sort  (cost=494217.13..495854.68 rows=655021 width=43)
          Sort Key: ts.train_service_key, pc.assembly_key, (dense_rank() OVER (?))
          ->  WindowAgg  (cost=392772.16..410785.23 rows=655021 width=43)
                ->  Sort  (cost=392772.16..394409.71 rows=655021 width=35)
                      Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
                      ->  Hash Join  (cost=89947.40..311580.26 rows=655021 width=35)
                            Hash Cond: ((pc.ds_code = ts.ds_code) AND (pc.train_service_key = ts.train_service_key))
                            ->  Seq Scan on portion_consist pc  (cost=0.00..39867.86 rows=782786 width=38)
                            ->  Hash  (cost=65935.36..65935.36 rows=1151136 width=21)
                                  ->  Seq Scan on train_service ts  (cost=0.00..65935.36 rows=1151136 width=21)

এটি উভয় টেবিলগুলিতে পূর্ণ স্ক্যান করছে এবং 17 টি গ্রহণ করে।

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

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

আমার উত্তর, অতএব, এমন উদাহরণ রয়েছে যেখানে মতামতগুলি যে ভিত্তিতে তৈরি হয়েছে সেই প্রশ্নের হিসাবে ঠিকঠাকভাবে সম্পাদন করে না, তাই সাবধানতার পরামর্শ দেওয়া হয়। আমি পোস্টগ্রিজ এসকিউএল 9.6.6 এর উপর ভিত্তি করে আমাজন অরোরা ব্যবহার করছি।


2
অন্যান্য উত্তরে সাবধানতা নোট করুন - " প্রদত্ত এটি একটি সাধারণ বিবৃতি "।
আরডিফোজ

পার্শ্ব নোট হিসাবে, CASE WHEN (NOT ts.primary_direction) THEN '-1' :: INTEGER ELSE 1 ENDপ্রয়োজন অনুসারে ক্যোরিয়াকে ধীরে ধীরে কমিয়ে দেবে আপনি ক্রমে আরও দুটি কন্ডিশনাল লেখার চেয়ে আরও ভাল।
ইভান ক্যারল

@ ইভানক্রোল আমি কিছু সময়ের জন্য এটির সাথে লড়াই করেছি। CASE WHEN (NOT ts.primary_direction) THEN dense_rank() OVER (PARTITION BY ts.train_service_key ORDER BY pc.through_idx DESC, pc.first_portion ASC, pc.first_seq DESC) ELSE dense_rank() OVER (PARTITION BY ts.train_service_key ORDER BY pc.through_idx DESC, pc.first_portion ASC, pc.first_seq ASC) END AS coach_block_idx
সবেমাত্র

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

1
@ ইভানক্রোল আপনার মন্তব্য আমাকে সেখানে পৌঁছে দিতে অনুরোধ জানায় (তাই আমার সম্পাদিত উত্তর)। ধন্যবাদ.
এনজায়াইচ

0

(আমি দর্শনের বিশাল অনুরাগী, তবে আপনাকে এখানে পিজির প্রতি খুব সাবধানতা অবলম্বন করতে হবে এবং প্রশ্নগুলি / কোডের আরও ভাল বোধগম্যতা এবং রক্ষণাবেক্ষণের জন্য আমি সাধারণত সবাইকে পিজিতে মতামত ব্যবহার করতে উত্সাহিত করতে চাই ))

প্রকৃতপক্ষে এবং দুঃখের সাথে (সতর্কতা :) পোস্টগ্র্রেসে ভিউ ব্যবহার করা আমাদের প্রকৃত সমস্যা সৃষ্টি করেছিল এবং এর অভ্যন্তরীণ বৈশিষ্ট্যগুলির উপর নির্ভর করে আমাদের কার্য সম্পাদনকে খারাপভাবে হ্রাস পেয়েছে :-( (কমপক্ষে v10.1 সহ) ( ওরাকলের মতো আধুনিক ডিবি সিস্টেমগুলি)

সুতরাং, সম্ভবত (এবং এটি আমার প্রশ্ন) ভিউয়ের বিরুদ্ধে যে কোনও ফিল্টারিং ... এর ফলে অন্তর্নিহিত টেবিলগুলির বিপরীতে একটি একক কোয়েরি কার্যকর হয়।

(আপনি ঠিক কী বোঝাতে চেয়েছেন তার উপর নির্ভর করে - না - মধ্যবর্তী টেম্প টেবিলগুলি এমনভাবে রূপায়িত করা যেতে পারে যা আপনি হতে চান না বা যেখানে ভবিষ্যদ্বাণীগুলি সরিয়ে দেওয়া হয় না ...)

আমি কমপক্ষে দুটি প্রধান "বৈশিষ্ট্য" জানি, যা আমাদের ওরাকল থেকে পোস্টগ্র্রেসে স্থানান্তরিত করার মাঝখানে নামিয়ে দেয় যাতে আমাদের কোনও প্রকল্পে পিজি ত্যাগ করতে হয়েছিল:

  • সিটিই ( withক্লাস সাবকিউরিজ / সাধারণ টেবিল এক্সপ্রেশন ) আরও জটিল প্রশ্নগুলি কাঠামোর জন্য (সাধারণত ছোট অ্যাপ্লিকেশনগুলিতে) দরকারী, তবে পিজিতে নকশাকৃতভাবে "লুকানো" অপ্টিমাইজার ইঙ্গিত হিসাবে প্রয়োগ করা হয় (যেমন নন-ইনডেক্সড টেম্প টেবিল উত্পন্ন) এবং এইভাবে ঘোষিত এসকিউএল ( ওরাকল ডকুমেন্ট ) এর ( আমার এবং আরও অনেকের পক্ষে গুরুত্বপূর্ণ) ধারণাটি লঙ্ঘন করে : যেমন

    • সাধারণ জিজ্ঞাসা:

      explain
      
        select * from pg_indexes where indexname='pg_am_name_index'
      
      /* result: 
      
      Nested Loop Left Join  (cost=12.38..26.67 rows=1 width=260)
        ...
        ->  Bitmap Index Scan on pg_class_relname_nsp_index  (cost=0.00..4.29 rows=2 width=0)
                                               Index Cond: (relname = 'pg_am_name_index'::name)
        ...
      */
      
    • কিছু সিটিই ব্যবহার করে পুনরায় লিখিত:

      explain
      
        with 
      
        unfiltered as (
          select * from pg_indexes
        ) 
      
        select * from unfiltered where indexname='pg_am_name_index'
      
      /* result:
      
      CTE Scan on unfiltered  (cost=584.45..587.60 rows=1 width=288)
         Filter: (indexname = 'pg_am_name_index'::name)
         CTE unfiltered
           ->  Hash Left Join  (cost=230.08..584.45 rows=140 width=260)  
      ...
      */
      
    • আলোচনার সাথে আরও উত্সসমূহ: https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

  • overস্টেটমেন্ট সহ উইন্ডো ফাংশনগুলি সম্ভাব্য অপ্রয়োজনীয় (সাধারণত দর্শনে ব্যবহৃত হয়, যেমন আরও জটিল প্রশ্নের উপর ভিত্তি করে প্রতিবেদনের উত্স হিসাবে)


withক্লাসের জন্য আমাদের কর্মসূচী

আমরা সমস্ত "ইনলাইন মতামত" একটি বিশেষ উপসর্গের সাথে বাস্তব দর্শনে রূপান্তর করব যাতে তারা তালিকাগুলির তালিকা / নাম স্থানটি বিশৃঙ্খলা না করে এবং সহজেই মূল "বাইরের দৃশ্যের" সাথে সম্পর্কিত হতে পারে: - /


উইন্ডো ফাংশন জন্য আমাদের সমাধান

আমরা এটি সফলভাবে ওরাকল ডাটাবেস ব্যবহার করে প্রয়োগ করেছি।


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