2014-11-25 17 views
6

Nếu truy vấn cơ sở dữ liệu sau (postgres) được thực hiện, cuộc gọi thứ hai nhanh hơn nhiều.Cải thiện hiệu suất truy vấn đầu tiên

Tôi đoán truy vấn đầu tiên chậm vì hệ điều hành (linux) cần lấy dữ liệu từ đĩa. Truy vấn thứ hai mang lại lợi ích từ bộ nhớ đệm ở mức hệ thống tệp và trong các postgres.

Có cách nào để tối ưu hóa cơ sở dữ liệu để nhận kết quả nhanh chóng trên các cuộc gọi đầu tiên không?

cuộc gọi đầu tiên (chậm)

[email protected]:~$ psql 

foo3_bar_p=# explain analyze SELECT "foo3_beleg"."id", ... FROM "foo3_beleg" WHERE 
foo3_bar_p-# (("foo3_beleg"."id" IN (SELECT beleg_id FROM foo3_text where 
foo3_bar_p(# content @@ 'footown'::tsquery)) AND "foo3_beleg"."belegart_id" IN 
foo3_bar_p(# ('...', ...)); 
                          QUERY PLAN                     
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
Nested Loop (cost=75314.58..121963.20 rows=152 width=135) (actual time=27253.451..88462.165 rows=11 loops=1) 
    -> HashAggregate (cost=75314.58..75366.87 rows=5229 width=4) (actual time=16087.345..16113.988 rows=17671 loops=1) 
     -> Bitmap Heap Scan on foo3_text (cost=273.72..75254.67 rows=23964 width=4) (actual time=327.653..16026.787 rows=27405 loops=1) 
       Recheck Cond: (content @@ '''footown'''::tsquery) 
       -> Bitmap Index Scan on foo3_text_content_idx (cost=0.00..267.73 rows=23964 width=0) (actual time=281.909..281.909 rows=27405 loops=1) 
        Index Cond: (content @@ '''footown'''::tsquery) 
    -> Index Scan using foo3_beleg_pkey on foo3_beleg (cost=0.00..8.90 rows=1 width=135) (actual time=4.092..4.092 rows=0 loops=17671) 
     Index Cond: (id = foo3_text.beleg_id) 
     Filter: ((belegart_id)::text = ANY ('{... 
     Rows Removed by Filter: 1 
Total runtime: 88462.809 ms 
(11 rows) 

cuộc gọi thứ hai (nhanh)

Nested Loop (cost=75314.58..121963.20 rows=152 width=135) (actual time=127.569..348.705 rows=11 loops=1) 
    -> HashAggregate (cost=75314.58..75366.87 rows=5229 width=4) (actual time=114.390..133.131 rows=17671 loops=1) 
     -> Bitmap Heap Scan on foo3_text (cost=273.72..75254.67 rows=23964 width=4) (actual time=11.961..97.943 rows=27405 loops=1) 
       Recheck Cond: (content @@ '''footown'''::tsquery) 
       -> Bitmap Index Scan on foo3_text_content_idx (cost=0.00..267.73 rows=23964 width=0) (actual time=9.226..9.226 rows=27405 loops=1) 
        Index Cond: (content @@ '''footown'''::tsquery) 
    -> Index Scan using foo3_beleg_pkey on foo3_beleg (cost=0.00..8.90 rows=1 width=135) (actual time=0.012..0.012 rows=0 loops=17671) 
     Index Cond: (id = foo3_text.beleg_id) 
     Filter: ((belegart_id)::text = ANY ('... 
     Rows Removed by Filter: 1 
Total runtime: 348.833 ms 
(11 rows) 

bố trí bảng của bảng foo3_text (hàng 28m)

foo3_egs_p=# \d foo3_text 
           Table "public.foo3_text" 
    Column |   Type   |       Modifiers       
----------+-----------------------+------------------------------------------------------------ 
id  | integer    | not null default nextval('foo3_text_id_seq'::regclass) 
beleg_id | integer    | not null 
index_id | character varying(32) | not null 
value | text     | not null 
content | tsvector    | 
Indexes: 
    "foo3_text_pkey" PRIMARY KEY, btree (id) 
    "foo3_text_index_id_2685e3637668d5e7_uniq" UNIQUE CONSTRAINT, btree (index_id, beleg_id) 
    "foo3_text_beleg_id" btree (beleg_id) 
    "foo3_text_content_idx" gin (content) 
    "foo3_text_index_id" btree (index_id) 
    "foo3_text_index_id_like" btree (index_id varchar_pattern_ops) 
Foreign-key constraints: 
    "beleg_id_refs_id_6e6d40770e71292" FOREIGN KEY (beleg_id) REFERENCES foo3_beleg(id) DEFERRABLE INITIALLY DEFERRED 
    "index_id_refs_name_341600137465c2f9" FOREIGN KEY (index_id) REFERENCES foo3_index(name) DEFERRABLE INITIALLY DEFERRED 

Phần cứng thay đổi (SSD i nstead đĩa truyền thống) hoặc đĩa RAM là có thể. Nhưng có lẽ có phần cứng hiện tại cũng có thể làm kết quả nhanh hơn.

Version: PostgreSQL 9.1.2 trên x86_64-biết-linux-gnu

Hãy để lại nhận xét nếu bạn cần biết thêm chi tiết.

+0

Nếu mệnh đề WHERE luôn giống nhau, điều gì về việc phát hành định kỳ yêu cầu? Giữ dữ liệu nóng là chỉ tôi biết để tránh sự hoảng sợ của nỗ lực đầu tiên. Theo tối ưu hóa, bạn đã kiểm tra tính chọn lọc của AND "foo3_beleg". "Belegart_id". Liệu nó có làm cho cảm giác chuyển nó sang SELECT đầu tiên? – SCO

+0

@SCO không, mệnh đề WHERE khác. Cụm từ tìm kiếm (ở đây 'footown') khác nhau. – guettli

+2

Postgres 9.4 sẽ có phần mở rộng 'pg_prewarm' có thể lấp đầy bộ nhớ đệm theo yêu cầu: http://www.postgresql.org/docs/9.4/static/pgprewarm.html –

Trả lời

0

Đôi khi di chuyển "WHERE x IN" vào JOIN có thể cải thiện hiệu suất đáng kể. Hãy thử điều này:

SELECT 
    foo3_beleg.id, ... 
FROM 
    foo3_beleg b INNER JOIN 
    foo3_text t ON (t.beleg_id = b.id AND t.content @@ 'footown'::tsquery) 
WHERE 
    foo3_beleg.belegart_id IN ('...', ...); 

Dưới đây là một thí nghiệm lặp lại để hỗ trợ tuyên bố của tôi.

Tôi tình cờ có một cơ sở dữ liệu Postgres lớn tiện dụng (30 triệu hàng) (http://juliusdavies.ca/2013/j.emse/bertillonage/), vì vậy tôi đã tải nó vào postbres 9.4beta3.

Kết quả thật ấn tượng. Cách tiếp cận phụ chọn là chậm hơn khoảng 20 lần:

time psql myDb < using-in.sql 
real 0m17.212s 

time psql myDb < using-join.sql 
real 0m0.807s 

Đối với những người quan tâm trong việc tái tạo, sau đây là các truy vấn SQL liệu tôi sử dụng để kiểm tra lý thuyết của tôi.

truy vấn này sử dụng một "SELECT IN" subquery, và nó chậm hơn 20 lần (17 giây trên máy tính xách tay của tôi trên việc thực hiện đầu tiên):

-- using-in.sql 
    SELECT 
    COUNT(DISTINCT sigsha1re) AS a_intersect_b, infilesha1 
    FROM 
    files INNER JOIN sigs ON (files.filesha1 = sigs.filesha1) 
    WHERE 
    sigs.sigsha1re IN (
     SELECT sigsha1re FROM sigs WHERE sigs.sigsha1re like '0347%' 
    ) 
    GROUP BY 
    infilesha1 

Truy vấn này di chuyển điều kiện ra khỏi subquery và vào tham gia tiêu chí và nhanh hơn 20 lần (0,8 giây trên máy tính xách tay của tôi khi thực hiện lần đầu).

-- using-join.sql 
    SELECT 
    COUNT(DISTINCT sigsha1re) AS a_intersect_b, infilesha1 
    FROM 
    files INNER JOIN sigs ON (
     files.filesha1 = sigs.filesha1 AND sigs.sigsha1re like '0347%' 
    ) 
    GROUP BY 
    infilesha1 

tái bút: nếu bạn tò mò về cơ sở dữ liệu đó, bạn có thể sử dụng nó để tính toán một tệp jar tùy ý tương tự như thế nào đối với tất cả các tệp jar trong kho lưu trữ maven vào khoảng năm 2011.

./query.sh lib/commons-codec-1.5.jar | psql myDb 

similarity |      a = 39 = commons-codec-1.5.jar (bin2bin)      
------------+-------------------------------------------------------------------------------------- 
    1.000  | commons-codec-1.5.jar 
    0.447  | commons-codec-1.4.jar 
    0.174  | org.apache.sling.auth.form-1.0.2.jar 
    0.170  | org.apache.sling.auth.form-1.0.0.jar 
    0.142  | jbehave-core-3.0-beta-3.jar 
    0.142  | jbehave-core-3.0-beta-4.jar 
    0.141  | jbehave-core-3.0-beta-5.jar 
    0.141  | jbehave-core-3.0-beta-6.jar 
    0.140  | commons-codec-1.2.jar 
+0

"Đôi khi di chuyển" WHERE x IN "vào JOIN có thể cải thiện hiệu suất đáng kể." - Bạn phải nói về MySQL. Postgres đủ thông minh để viết lại cây truy vấn khi điều này là có thể. Câu hỏi của OP rõ ràng liên quan đến vấn đề "index/data is not in the cache yet". (Và câu trả lời đúng là, tôi sẽ thu thập, đã có trong các ý kiến.) –

+1

Khi nào Postgres trở nên giỏi hơn? Tôi đã gặp phải vấn đề này với 8.4 và 9.0. Chưa bao giờ thử trên các phiên bản khác. (Và tôi đã có các triệu chứng tương tự: truy vấn chậm lần đầu tiên, nhanh hơn nhiều lần thứ hai.) –

+0

Nó đã làm như vậy miễn là tôi đã sử dụng Postgres (8.0); nơi mọi thứ đã được cải thiện trong những năm qua đã có thêm các lớp truy vấn phụ bị thu gọn. –

1

Đồng ý với Julius nhưng, nếu bạn chỉ cần công cụ từ foo3_beleg, thử EXISTS trong thay vì (và nó sẽ giúp đỡ nếu bạn đã dán sql của bạn cũng vậy, không chỉ bạn giải thích kế hoạch).

select ... 
from foo3_beleg b 
where exists 
(select 1 from foo_text s where t.beleg_id = b.id) 
.... 

Tuy nhiên, tôi nghi ngờ "thức dậy" của bạn trên đèo 1 chỉ là db của bạn tải lên hàng INquery vào bộ nhớ. Điều đó có thể sẽ xảy ra bất kể, mặc dù EXISTS thường nhanh hơn nhiều so với IN (INs hiếm khi cần thiết, nếu không chứa danh sách hardcoded và cờ màu vàng nếu tôi xem lại sql).

+0

Việc sử dụng đã tồn tại sẽ không hữu ích ở đây. Câu hỏi của OP rõ ràng liên quan đến vấn đề "index/data is not in the cache yet". (Và câu trả lời đúng là, tôi sẽ thu thập, đã có trong các ý kiến.) –

+0

Vâng, nếu bạn nhận thấy, tôi khá nhiều tuyên bố rằng "... có thể xảy ra bất kể ...". Ngoài ra, tùy thuộc vào cách dữ liệu thực tế trông như thế nào và cách pg đi về tải bộ nhớ cache/kiểm tra kết quả phù hợp, hãy ghi nhớ một EXIST là nhanh vì nó là miễn phí để trở lại vào trận đấu đầu tiên. Điều này cũng có thể là trường hợp với một IN, nhưng đó là một khía cạnh tối ưu hóa, chứ không phải là một tính năng của lệnh SQL. Điểm mấu chốt: Tôi chưa bắt gặp trường hợp sử dụng hợp lệ của IN không thể là EXISTS. –

2

Postgres đang cung cấp cho bạn một cơ hội để thực hiện một số cấu hình trên thực thi truy vấn thời gian chạy để quyết định mức độ ưu tiên hoạt động I/O của bạn.

random_page_cost(floating point) -(reference) là những gì có thể giúp bạn. Về cơ bản nó sẽ thiết lập tỷ lệ hoạt động IO/CPU của bạn.

Giá trị cao hơn có nghĩa là I/O là quan trọng, tôi có đĩa tuần tự; và giá trị thấp hơn có nghĩa là I/O không quan trọng, tôi có đĩa truy cập ngẫu nhiên.

Giá trị mặc định là 4.0 và có thể bạn muốn tăng và kiểm tra xem truy vấn của bạn có thời gian ngắn hơn không.

Đừng quên, ưu tiên I/O của bạn sẽ tùy thuộc vào số cột, số lượng hàng.

NHƯNG lớn; vì các chỉ báo của bạn là btree, ưu tiên CPU của bạn sẽ giảm nhanh hơn nhiều so với các ưu tiên I/O tăng lên. Về cơ bản bạn có thể lập bản đồ các độ phức tạp cho các ưu tiên.

CPU Priority = O(log(x)) 
I/O Priority = O(x) 

Tất cả trong tất cả, điều này có nghĩa, nếu giá trị Postgre của 4.0 sẽ cho 100k mục, Bạn nên đặt nó vào (xấp xỉ). (4.0 * log(100k) * 10M)/(log(10M) * 100k) cho 10M nhập cảnh.

+0

Tôi rất tiếc, tôi sẽ không thể kiểm tra điều này trước khi tiền thưởng hết hạn ... – guettli

+0

không sao cả. hy vọng nó giúp :) – totten

1

Lần đầu tiên bạn thực hiện truy vấn, postgres sẽ tải dữ liệu từ đĩa chậm ngay cả khi có ổ cứng tốt. Lần thứ hai bạn chạy truy vấn của bạn, nó sẽ tải dữ liệu đã tải trước đó từ RAM, điều này rõ ràng là nhanh hơn.

Các giải pháp cho vấn đề này sẽ được tải dữ liệu liên quan vào một trong hai bộ đệm cache hệ điều hành hoặc các bộ đệm Cache PostgreSQL với:

int8 pg_prewarm(regclass, mode text default 'buffer', fork text default 'main', first_block int8 default null, last_block int8 default null):

Đối số đầu tiên là mối quan hệ được prewarmed . Đối số thứ hai là phương thức tiền tố được sử dụng, như được thảo luận thêm dưới đây; thứ ba là ngã ba quan hệ được làm sẵn, thường là chính. Đối số thứ tư là số khối đầu tiên để prewarm (NULL được chấp nhận như một từ đồng nghĩa với số không). Đối số thứ năm là số khối cuối cùng để mở trước (NULL có nghĩa là prewarm thông qua khối cuối cùng trong quan hệ). Giá trị trả về là số lượng các khối được tạo sẵn.

Có ba phương pháp làm ấm sẵn có. tìm nạp trước các yêu cầu tìm nạp trước không đồng bộ cho hệ điều hành, nếu điều này được hỗ trợ hoặc ném lỗi khác. đọc đọc phạm vi khối yêu cầu; không giống như tìm nạp trước, đây là đồng bộ và được hỗ trợ trên tất cả các nền tảng và bản dựng nhưng có thể chậm hơn. bộ đệm đọc phạm vi khối được yêu cầu vào bộ nhớ cache của bộ đệm cơ sở dữ liệu.Lưu ý rằng với bất kỳ phương pháp nào trong số này, hãy cố gắng tạo trước nhiều khối hơn có thể được lưu vào bộ nhớ cache - bởi hệ điều hành khi sử dụng tìm nạp trước hoặc đọc hoặc bằng PostgreSQL khi sử dụng bộ đệm - có khả năng sẽ dẫn đến các khối được đánh số thấp hơn Các khối được đánh số được đọc. Dữ liệu được ưu tiên cũng không có sự bảo vệ đặc biệt nào từ việc xóa bộ nhớ cache, vì vậy có thể cho các hoạt động hệ thống khác có thể loại bỏ các khối mới được tân trang ngay sau khi chúng được đọc; ngược lại, việc tân trang cũng có thể loại bỏ dữ liệu khác khỏi bộ nhớ cache. Vì những lý do này, prewarming thường hữu ích nhất khi khởi động, khi cache hầu như trống.

Source

Hy vọng điều này giúp!

Các vấn đề liên quan