খুব ধীর সরল JOIN ক্যোয়ারী


12

সাধারণ ডিবি কাঠামো (একটি অনলাইন ফোরামের জন্য):

CREATE TABLE users (
    id integer NOT NULL PRIMARY KEY,
    username text
);
CREATE INDEX ON users (username);

CREATE TABLE posts (
    id integer NOT NULL PRIMARY KEY,
    thread_id integer NOT NULL REFERENCES threads (id),
    user_id integer NOT NULL REFERENCES users (id),
    date timestamp without time zone NOT NULL,
    content text
);
CREATE INDEX ON posts (thread_id);
CREATE INDEX ON posts (user_id);

প্রায় 80k এন্ট্রি usersএবং সারণীতে 2,6 মিলিয়ন এন্ট্রি posts। শীর্ষস্থানীয় ১০০ জন ব্যবহারকারীকে তাদের পোস্টের মাধ্যমে পেতে এই সাধারণ ক্যোয়ারিতে ২,৪০ সেকেন্ড সময় লাগে :

EXPLAIN ANALYZE SELECT u.id, u.username, COUNT(p.id) AS PostCount FROM users u
                    INNER JOIN posts p on p.user_id = u.id
                    WHERE u.username IS NOT NULL
                    GROUP BY u.id
ORDER BY PostCount DESC LIMIT 100;
Limit  (cost=316926.14..316926.39 rows=100 width=20) (actual time=2326.812..2326.830 rows=100 loops=1)
  ->  Sort  (cost=316926.14..317014.83 rows=35476 width=20) (actual time=2326.809..2326.820 rows=100 loops=1)
        Sort Key: (count(p.id)) DESC
        Sort Method: top-N heapsort  Memory: 32kB
        ->  HashAggregate  (cost=315215.51..315570.27 rows=35476 width=20) (actual time=2311.296..2321.739 rows=34608 loops=1)
              Group Key: u.id
              ->  Hash Join  (cost=1176.89..308201.88 rows=1402727 width=16) (actual time=16.538..1784.546 rows=1910831 loops=1)
                    Hash Cond: (p.user_id = u.id)
                    ->  Seq Scan on posts p  (cost=0.00..286185.34 rows=1816634 width=8) (actual time=0.103..1144.681 rows=2173916 loops=1)
                    ->  Hash  (cost=733.44..733.44 rows=35476 width=12) (actual time=15.763..15.763 rows=34609 loops=1)
                          Buckets: 65536  Batches: 1  Memory Usage: 2021kB
                          ->  Seq Scan on users u  (cost=0.00..733.44 rows=35476 width=12) (actual time=0.033..6.521 rows=34609 loops=1)
                                Filter: (username IS NOT NULL)
                                Rows Removed by Filter: 11335

Execution time: 2301.357 ms

সঙ্গে set enable_seqscan = falseএমনকি খারাপ:

Limit  (cost=1160881.74..1160881.99 rows=100 width=20) (actual time=2758.086..2758.107 rows=100 loops=1)
  ->  Sort  (cost=1160881.74..1160970.43 rows=35476 width=20) (actual time=2758.084..2758.098 rows=100 loops=1)
        Sort Key: (count(p.id)) DESC
        Sort Method: top-N heapsort  Memory: 32kB
        ->  GroupAggregate  (cost=0.79..1159525.87 rows=35476 width=20) (actual time=0.095..2749.859 rows=34608 loops=1)
              Group Key: u.id
              ->  Merge Join  (cost=0.79..1152157.48 rows=1402727 width=16) (actual time=0.036..2537.064 rows=1910831 loops=1)
                    Merge Cond: (u.id = p.user_id)
                    ->  Index Scan using users_pkey on users u  (cost=0.29..2404.83 rows=35476 width=12) (actual time=0.016..41.163 rows=34609 loops=1)
                          Filter: (username IS NOT NULL)
                          Rows Removed by Filter: 11335
                    ->  Index Scan using posts_user_id_index on posts p  (cost=0.43..1131472.19 rows=1816634 width=8) (actual time=0.012..2191.856 rows=2173916 loops=1)
Planning time: 1.281 ms
Execution time: 2758.187 ms

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

বিজ্ঞানের জন্য, আমি মাইক্রোসফ্ট এসকিউএল সার্ভার একই সার্ভারে ইনস্টল করেছি (যা আর্চলিনাক্স, 8 কোর জিয়ন, 24 জিবি র‌্যাম, এসএসডি) চালায় এবং পোস্টগ্রাস থেকে সমস্ত ডেটা স্থানান্তরিত করে - একই টেবিলের কাঠামো, একই সূচকগুলি, একই ডেটা। শীর্ষস্থানীয় 100 পোস্টারগুলি পেতে 0,3 সেকেন্ডে একই ক্যোয়ারী :

SELECT TOP 100 u.id, u.username, COUNT(p.id) AS PostCount FROM dbo.users u
                    INNER JOIN dbo.posts p on p.user_id = u.id
                    WHERE u.username IS NOT NULL
                    GROUP BY u.id, u.username
ORDER BY PostCount DESC

উৎপাদ একই একই তথ্য থেকে ফলাফল, কিন্তু 8 বার দ্রুত এটা আছে। এবং এটি লিনাক্সের এমএস এসকিউএল এর বিটা সংস্করণ, আমি অনুমান করি এটি "হোম" ওএস - উইন্ডোজ সার্ভারে চলছে - এটি আরও দ্রুততর হতে পারে।

আমার পোস্টগ্রাইএসকিউএল কোয়েরিটি কি পুরোপুরি ভুল, না পোস্টগ্র্রেএসকিউএল স্লো?

অতিরিক্ত তথ্য

সংস্করণটি প্রায় নতুন (9.6.1, বর্তমানে 9.6.2 নতুন, আর্চলিনাক্সের সেকেলে প্যাকেজ রয়েছে এবং এটি আপডেট করা খুব ধীর)। কনফিগ:

max_connections = 75
shared_buffers = 3584MB       
effective_cache_size = 10752MB
work_mem = 24466kB         
maintenance_work_mem = 896MB   
dynamic_shared_memory_type = posix  
min_wal_size = 1GB
max_wal_size = 2GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100

EXPLAIN ANALYZEফলাফল: https://pastebin.com/HxucRgnk

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

এমএস এসকিউএল সার্ভার 14.0.405.200-1, ডিফল্ট কনফারেন্স।

আমি এটি কোনও এপিআইতে ব্যবহার করি (বিশ্লেষণ না করে প্লেইন সিলেক্ট সহ) এবং ক্রিমের সাথে এই এপিআই এর শেষ পয়েন্টটি কল করে এটি বলছে যে এটি 2500 এমএস + লাগে - এইচটিটিপি এবং ওয়েব সার্ভারের ওভারহেডের 50 এমএস যোগ করে (একই সার্ভারে এপিআই এবং এসকিউএল চালানো) - এটা একই. আমি এখানে বা সেখানে প্রায় 100 এমএসের যত্ন নিই না, আমি যা যত্ন করি তা পুরো দুই সেকেন্ড।

explain analyze SELECT user_id, count(9) FROM posts group by user_id;700 এমএস লাগে। postsটেবিলের আকার 2154 এমবি।


2
এটি যেমন শোনাচ্ছে, আপনার ব্যবহারকারীদের কাছ থেকে সুন্দর ফ্যাট পোস্ট রয়েছে (গড়ে গড়ে ~ 1 কেবি)। এটিকে postsটেবিলের বাকী অংশ থেকে আলাদা করার জন্য এটি বোধগম্য হতে পারে, সেই জাতীয় টেবিলটি ব্যবহার করে CREATE TABLE post_content (post_id PRIMARY KEY REFERENCES posts (id), content text);, এই ধরণের প্রশ্নের 'নষ্ট' হওয়া বেশিরভাগ I / O কে বাঁচানো যেতে পারে। পোস্টগুলি যদি এর চেয়ে ছোট হয় তবে একটি VACUUM FULLঅন postsসহায়তা করতে পারে।
dezso

হ্যাঁ, পোস্টগুলিতে সামগ্রীর কলাম রয়েছে যা একটি পোস্টের সমস্ত এইচটিএমএল রয়েছে। আপনার পরামর্শের জন্য আপনাকে ধন্যবাদ, আগামীকাল চেষ্টা করবে। প্রশ্নটি হ'ল - এমএসএসকিউএল পোস্টের টেবিলটির ওজনও 1.5 গিগাবাইটেরও বেশি এবং সামগ্রীতে একই রকম এন্ট্রি রয়েছে, তবে বেশ দ্রুত হতে পারে - কেন?
লার্স

2
আপনি সম্ভবত এসকিউএল সার্ভার থেকেও একটি প্রকৃত বাস্তবায়ন পরিকল্পনা পোস্ট করতে পারেন। এমনকি আমার মতো লোকদের পোস্টগ্র্রেসের কাছেও সত্যিই আকর্ষণীয় হতে পারে।
dezso

হুম, দ্রুত অনুমান, আপনি কি এটি এটিকে পরিবর্তন GROUP BY u.idকরতে GROUP BY p.user_idএবং চেষ্টা করতে পারেন? আমার অনুমান, পোস্টগ্র্রেস প্রথম এবং দ্বিতীয় গ্রুপে যোগ দেয় কারণ আপনি ব্যবহারকারীদের সারণী শনাক্তকারী দ্বারা গ্রুপিং করছেন, যদিও আপনাকে শীর্ষে এন - সারিগুলি পেতে কেবলমাত্র ইউজার_আইডি পোস্টের প্রয়োজন আছে।
UldisK

উত্তর:


1

আর একটি ভাল ক্যোয়ারির বৈকল্পিক হ'ল:

SELECT p.user_id, p.cnt AS PostCount
FROM users u
INNER JOIN (
    select user_id, count(id) as cnt from posts group by user_id
) as p on p.user_id = u.id
WHERE u.username IS NOT NULL          
ORDER BY PostCount DESC LIMIT 100;

এটি সিটিই ব্যবহার করে না এবং সঠিক উত্তর দেয় (এবং সিটিই উদাহরণ 100 টিরও কম সারি তৈরি করতে পারে কারণ এটি প্রথমে সীমাবদ্ধ করে ব্যবহারকারীদের সাথে যোগ দেয়)।

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


8

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

with
    __posts as(
        select
            user_id,
            count(1) as num_posts
        from
            posts
        group by
            user_id
        order by
            num_posts desc
        limit 100
    )
select
    users.username,
    __posts.num_posts
from
    users
    inner join __posts on(
        __posts.user_id = users.id
    )
order by
    num_posts desc

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

আপনি যে সর্বোত্তম পরামর্শটি সত্যই নিতে পারেন তা হ'ল এটি একাধিক উপায়ে চেষ্টা করা এবং আপনার প্রশ্নের পরিকল্পনাগুলি পরীক্ষা করা।


-1

আপনি work_mem বাড়াতে চেষ্টা করেছেন? 24 এমবি খুব ছোট বলে মনে হচ্ছে এবং তাই হ্যাশ জোড়কে একাধিক ব্যাচ ব্যবহার করতে হবে (যা টেম্প ফাইলগুলিতে লেখা থাকে)।


এটি খুব ছোট নয়। 240 মেগাবাইট বাড়ানো কিছুই করে না। Postgresql.conf- এ কী সহায়তা করবে এই দুটি লাইন যুক্ত করে সমান্তরাল অনুসন্ধানগুলি সক্ষম করছে: max_parallel_workers_per_gather = 4এবংmax_worker_processes = 16
লার্স
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.