Tối ưu hóa chỉ mục với ngày


26

Tôi có một bảng lớn các đối tượng (15M + hàng) trong PostgreSQL 9.0.8, mà tôi muốn truy vấn cho trường lỗi thời.

Tôi muốn chia truy vấn cho hàng triệu người, cho mục đích mở rộng & đồng thời và tôi muốn tìm nạp tất cả dữ liệu với trường update_at có ngày vài ngày trước.

Tôi đã thử nhiều chỉ mục và truy vấn, trên một triệu id và dường như tôi không thể đạt được hiệu suất dưới 100 giây với phần cứng Ronin của Heroku.

Tôi đang tìm kiếm những gợi ý mà tôi đã không cố gắng để làm cho điều này hiệu quả nhất có thể.

THỬ # 1

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE (date(updated_at)) < (date(now())-7) AND id >= 5000001 AND id < 6000001;
 INDEX USED: (date(updated_at),id)
 268578.934 ms

THỬ # 2

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE ((date(now()) - (date(updated_at)) > 7)) AND id >= 5000001 AND id < 6000001;
 INDEX USED: primary key
 335555.144 ms

THỬ # 3

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE (date(updated_at)) < (date(now())-7) AND id/1000000 = 5;
 INDEX USED: (date(updated_at),(id/1000000))
 243427.042 ms

THỬ # 4

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE (date(updated_at)) < (date(now())-7) AND id/1000000 = 5 AND updated_at IS NOT NULL;
 INDEX USED: (date(updated_at),(id/1000000)) WHERE updated_at IS NOT NULL 
 706714.812 ms

TRY # 5 (trong một tháng dữ liệu lỗi thời)

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE (EXTRACT(MONTH from date(updated_at)) = 8) AND id/1000000 = 5;
 INDEX USED: (EXTRACT(MONTH from date(updated_at)),(id/1000000))
 107241.472 ms

THỬ # 6

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE (date(updated_at)) < (date(now())-7) AND id/1000000 = 5;
 INDEX USED: ( (id/1000000 ) ASC ,updated_at DESC NULLS LAST)
 106842.395 ms

THỬ # 7 (xem: http://explain.depesz.com/s/DQP )

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE id/1000000 = 5 and (date(updated_at)) < (date(now())-7);
 INDEX USED: ( (id/1000000 ) ASC ,date(updated_at) DESC NULLS LAST);
 100732.049 ms
 Second try: 87280.728 ms 

THỬ # 8

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE (date(updated_at)) < (date(now())-7) AND id/1000000 = 5 AND updated_at IS NOT NULL;
 INDEX USED:  ( (id/1000000 ) ASC ,date(updated_at) ASC NULLS LAST);
 129133.022 ms

TRY # 9 ( chỉ mục một phần theo đề xuất của Erwin, xem: http://explain.depesz.com/s/p9A )

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE id BETWEEN 5000000 AND 5999999 AND (date(updated_at)) < '2012-10-23'::date;
 INDEX USED: (date(updated_at) DESC NULLS LAST)
 WHERE id BETWEEN 5000000 AND 6000000 AND date(updated_at) < '2012-10-23'::date;
 73861.047 ms

TRY # 10 ( CLUSTER , theo đề nghị của Erwin).

 CREATE INDEX ix_8 on objects ( (id/1000000 ) ASC ,date(updated_at) DESC NULLS LAST);
 CLUSTER entities USING ix_8;
 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE id/1000000 = 5 and (date(updated_at)) < (date(now())-7) ;
 4745.595 ms

 EXPLAIN ANALYZE SELECT count(*) FROM objects
 WHERE id/1000000 = 10 and (date(updated_at)) < (date(now())-7) ;
 17573.639 ms

==> Giải pháp này dường như là chiến thắng. Tôi sẽ phải kiểm tra kỹ lưỡng để xác minh các phản ứng ở mọi nơi trong ứng dụng của tôi.

Cài đặt DB:

chọn tên, min_val, max_val, boot_val từ pg_sinstall;

             name               |  min_val  |   max_val    |     boot_val      
--------------------------------+-----------+--------------+-------------------
allow_system_table_mods         |           |              | off
application_name                |           |              | 
archive_command                 |           |              | 
archive_mode                    |           |              | off
archive_timeout                 | 0         | 2147483647   | 0
array_nulls                     |           |              | on
authentication_timeout          | 1         | 600          | 60
autovacuum                      |           |              | on
autovacuum_analyze_scale_factor | 0         | 100          | 0.1
autovacuum_analyze_threshold    | 0         | 2147483647   | 50
autovacuum_freeze_max_age       | 100000000 | 2000000000   | 200000000
autovacuum_max_workers          | 1         | 536870911    | 3
autovacuum_naptime              | 1         | 2147483      | 60
autovacuum_vacuum_cost_delay    | -1        | 100          | 20
autovacuum_vacuum_cost_limit    | -1        | 10000        | -1
autovacuum_vacuum_scale_factor  | 0         | 100          | 0.2
autovacuum_vacuum_threshold     | 0         | 2147483647   | 50
backslash_quote                 |           |              | safe_encoding
bgwriter_delay                  | 10        | 10000        | 200
bgwriter_lru_maxpages           | 0         | 1000         | 100
bgwriter_lru_multiplier         | 0         | 10           | 2
block_size                      | 8192      | 8192         | 8192
bonjour                         |           |              | off
bonjour_name                    |           |              | 
bytea_output                    |           |              | hex
check_function_bodies           |           |              | on
checkpoint_completion_target    | 0         | 1            | 0.5
checkpoint_segments             | 1         | 2147483647   | 3
checkpoint_timeout              | 30        | 3600         | 300
checkpoint_warning              | 0         | 2147483647   | 30
client_encoding                 |           |              | SQL_ASCII
client_min_messages             |           |              | notice
commit_delay                    | 0         | 100000       | 0
commit_siblings                 | 1         | 1000         | 5
constraint_exclusion            |           |              | partition
cpu_index_tuple_cost            | 0         | 1.79769e+308 | 0.005
cpu_operator_cost               | 0         | 1.79769e+308 | 0.0025
cpu_tuple_cost                  | 0         | 1.79769e+308 | 0.01
cursor_tuple_fraction           | 0         | 1            | 0.1
custom_variable_classes         |           |              | 
DateStyle                       |           |              | ISO, MDY
db_user_namespace               |           |              | off
deadlock_timeout                | 1         | 2147483      | 1000
debug_assertions                |           |              | off
debug_pretty_print              |           |              | on
debug_print_parse               |           |              | off
debug_print_plan                |           |              | off
debug_print_rewritten           |           |              | off
default_statistics_target       | 1         | 10000        | 100
default_tablespace              |           |              | 
default_text_search_config      |           |              | pg_catalog.simple
default_transaction_isolation   |           |              | read committed
default_transaction_read_only   |           |              | off
default_with_oids               |           |              | off
effective_cache_size            | 1         | 2147483647   | 16384
effective_io_concurrency        | 0         | 1000         | 1
enable_bitmapscan               |           |              | on
enable_hashagg                  |           |              | on
enable_hashjoin                 |           |              | on
enable_indexscan                |           |              | on
enable_material                 |           |              | on
enable_mergejoin                |           |              | on
enable_nestloop                 |           |              | on
enable_seqscan                  |           |              | on
enable_sort                     |           |              | on
enable_tidscan                  |           |              | on
escape_string_warning           |           |              | on
extra_float_digits              | -15       | 3            | 0
from_collapse_limit             | 1         | 2147483647   | 8
fsync                           |           |              | on
full_page_writes                |           |              | on
geqo                            |           |              | on
geqo_effort                     | 1         | 10           | 5
geqo_generations                | 0         | 2147483647   | 0
geqo_pool_size                  | 0         | 2147483647   | 0
geqo_seed                       | 0         | 1            | 0
geqo_selection_bias             | 1.5       | 2            | 2
geqo_threshold                  | 2         | 2147483647   | 12
gin_fuzzy_search_limit          | 0         | 2147483647   | 0
hot_standby                     |           |              | off
ignore_system_indexes           |           |              | off
integer_datetimes               |           |              | on
IntervalStyle                   |           |              | postgres
join_collapse_limit             | 1         | 2147483647   | 8
krb_caseins_users               |           |              | off
krb_srvname                     |           |              | postgres
lc_collate                      |           |              | C
lc_ctype                        |           |              | C
lc_messages                     |           |              |
lc_monetary                     |           |              | C
lc_numeric                      |           |              | C
lc_time                         |           |              | C
listen_addresses                |           |              | localhost
lo_compat_privileges            |           |              | off
local_preload_libraries         |           |              |
log_autovacuum_min_duration     | -1        | 2147483      | -1
log_checkpoints                 |           |              | off
log_connections                 |           |              | off
log_destination                 |           |              | stderr
log_disconnections              |           |              | off
log_duration                    |           |              | off
log_error_verbosity             |           |              | default
log_executor_stats              |           |              | off
log_hostname                    |           |              | off
log_line_prefix                 |           |              |
log_lock_waits                  |           |              | off
log_min_duration_statement      | -1        | 2147483      | -1
log_min_error_statement         |           |              | error
log_min_messages                |           |              | warning
log_parser_stats                |           |              | off
log_planner_stats               |           |              | off
log_rotation_age                | 0         | 35791394     | 1440
log_rotation_size               | 0         | 2097151      | 10240
log_statement                   |           |              | none
log_statement_stats             |           |              | off
log_temp_files                  | -1        | 2147483647   | -1
log_timezone                    |           |              | UNKNOWN
log_truncate_on_rotation        |           |              | off
logging_collector               |           |              | off
maintenance_work_mem            | 1024      | 2097151      | 16384
max_connections                 | 1         | 536870911    | 100
max_files_per_process           | 25        | 2147483647   | 1000
max_function_args               | 100       | 100          | 100
max_identifier_length           | 63        | 63           | 63
max_index_keys                  | 32        | 32           | 32
max_locks_per_transaction       | 10        | 2147483647   | 64
max_prepared_transactions       | 0         | 536870911    | 0
max_stack_depth                 | 100       | 2097151      | 100
max_standby_archive_delay       | -1        | 2147483      | 30000
max_standby_streaming_delay     | -1        | 2147483      | 30000
max_wal_senders                 | 0         | 536870911    | 0
password_encryption             |           |              | on
port                            | 1         | 65535        | 5432
post_auth_delay                 | 0         | 2147483647   | 0
pre_auth_delay                  | 0         | 60           | 0
random_page_cost                | 0         | 1.79769e+308 | 4
search_path                     |           |              | "$user",public
segment_size                    | 131072    | 131072       | 131072
seq_page_cost                   | 0         | 1.79769e+308 | 1
server_encoding                 |           |              | SQL_ASCII
server_version                  |           |              | 9.0.8
server_version_num              | 90008     | 90008        | 90008
session_replication_role        |           |              | origin
shared_buffers                  | 16        | 1073741823   | 1024
silent_mode                     |           |              | off
sql_inheritance                 |           |              | on
ssl                             |           |              | off
ssl_renegotiation_limit         | 0         | 2097151      | 524288
standard_conforming_strings     |           |              | off
statement_timeout               | 0         | 2147483647   | 0
superuser_reserved_connections  | 0         | 536870911    | 3
synchronize_seqscans            |           |              | on
synchronous_commit              |           |              | on
syslog_facility                 |           |              | local0
syslog_ident                    |           |              | postgres
tcp_keepalives_count            | 0         | 2147483647   | 0
tcp_keepalives_idle             | 0         | 2147483647   | 0
tcp_keepalives_interval         | 0         | 2147483647   | 0
temp_buffers                    | 100       | 1073741823   | 1024
temp_tablespaces                |           |              |
TimeZone                        |           |              | UNKNOWN
timezone_abbreviations          |           |              | UNKNOWN
trace_notify                    |           |              | off
trace_recovery_messages         |           |              | log
trace_sort                      |           |              | off
track_activities                |           |              | on
track_activity_query_size       | 100       | 102400       | 1024
track_counts                    |           |              | on
track_functions                 |           |              | none
transaction_isolation           |           |              |
transaction_read_only           |           |              | off
transform_null_equals           |           |              | off
unix_socket_group               |           |              |
unix_socket_permissions         | 0         | 511          | 511
update_process_title            |           |              | on
vacuum_cost_delay               | 0         | 100          | 0
vacuum_cost_limit               | 1         | 10000        | 200
vacuum_cost_page_dirty          | 0         | 10000        | 20
vacuum_cost_page_hit            | 0         | 10000        | 1
vacuum_cost_page_miss           | 0         | 10000        | 10
vacuum_defer_cleanup_age        | 0         | 1000000      | 0
vacuum_freeze_min_age           | 0         | 1000000000   | 50000000
vacuum_freeze_table_age         | 0         | 2000000000   | 150000000
wal_block_size                  | 8192      | 8192         | 8192
wal_buffers                     | 4         | 2147483647   | 8
wal_keep_segments               | 0         | 2147483647   | 0
wal_level                       |           |              | minimal
wal_segment_size                | 2048      | 2048         | 2048
wal_sender_delay                | 1         | 10000        | 200
wal_sync_method                 |           |              | fdatasync
wal_writer_delay                | 1         | 10000        | 200
work_mem                        | 64        | 2097151      | 1024
xmlbinary                       |           |              | base64
xmloption                       |           |              | content
zero_damaged_pages              |           |              | off
(195 rows)

Cấu trúc bảng có ~ 20-30 cột, một vài số nguyên khóa ngoài, chuỗi, văn bản, booleans. Định nghĩa chỉ mục là trong bài đăng ở trên bên cạnh INDEX USEd. (Tôi đã đăng 8 chỉ mục được sử dụng bởi các truy vấn). Tôi có thêm một vài chỉ mục để cập nhật nhanh hơn và chọn cho ứng dụng của mình. Cuối cùng, tôi đang sử dụng DB đám mây và tôi không thay đổi gì cả. Tôi tự hỏi chủ yếu là nếu định nghĩa chỉ số của tôi là tốt như nó có thể nhận được trước khi đạt được những tối ưu hóa. Tuy nhiên, tôi sẽ cập nhật với thông tin.
xlash

Câu trả lời:


30

Trước hết, nó có thể? Bạn viết:

Tôi muốn tìm nạp tất cả dữ liệu với trường update_at có ngày vài ngày trước .

Nhưng WHEREđiều kiện của bạn là:

(ngày (update_at)) < (ngày (bây giờ ()) - 7)

Có nên như >vậy không?


Chỉ mục

Để hiệu suất tối ưu , bạn có thể ...

  • phân vùng các chỉ mục của bạn
  • loại trừ các hàng không liên quan khỏi các chỉ mục
  • tự động tạo lại các chỉ mục vào giờ nghỉ với vị ngữ được cập nhật.

Các chỉ mục của bạn có thể trông giống như:

CREATE INDEX objects_id_updated_at_idx (updated_at::date DESC NULLS LAST)
WHERE  id BETWEEN 0 AND 999999
AND    updated_at > '2012-10-01 0:0'::timestamp  -- some minimum date

CREATE INDEX objects_id_updated_at_idx (updated_at::date DESC NULLS LAST)
WHERE  id BETWEEN 1000000 AND 1999999
AND    updated_at > '2012-10-01 0:0'::timestamp  -- some minimum date

...

Điều kiện thứ hai loại trừ các hàng không liên quan khỏi chỉ mục ngay lập tức, điều này sẽ làm cho nó nhỏ hơn và nhanh hơn - tùy thuộc vào phân phối dữ liệu thực tế của bạn. Theo nhận xét sơ bộ của tôi, tôi giả sử bạn muốn các hàng mới hơn .

Điều kiện cũng tự động loại trừ các giá trị NULL trong updated_at- mà bạn dường như cho phép trong bảng và rõ ràng muốn loại trừ trong truy vấn. Tính hữu dụng của chỉ số suy giảm theo thời gian. Truy vấn luôn lấy các mục mới nhất. Tái tạo chỉ mục với một WHEREđiều khoản cập nhật định kỳ. Điều này đòi hỏi một khóa độc quyền trên bàn, vì vậy hãy làm điều đó vào giờ tan tầm. Ngoài ra còn có CREATE INDEX CONCURRENTLYđể giảm thiểu thời gian của khóa:

CREATE INDEX CONCURRENTLY objects_id_up_201211_idx; -- create new idx
DROP INDEX  objects_id_up_201210_idx;  -- then drop old

Câu trả lời liên quan về SO:

Để tiếp tục tối ưu hóa, bạn có thể sử dụng CLUSTERnhư chúng tôi đã đề cập trong các nhận xét. Nhưng bạn cần một chỉ số đầy đủ cho điều đó. Không hoạt động với một chỉ mục một phần. Bạn sẽ tạo tạm thời:

CREATE INDEX objects_full_idx (id/1000000, updated_at::date DESC NULLS LAST);

Hình thức này của chỉ mục đầy đủ khớp với thứ tự sắp xếp của các chỉ mục một phần ở trên.

CLUSTER objects USING objects_full_idx;
ANALYZE objects;

Điều này sẽ mất một lúc, vì bảng được viết lại vật lý. Nó cũng hiệu quả a VACUUM FULL. Nó cần một khóa ghi độc quyền trên bàn, vì vậy hãy thực hiện vào giờ tan tầm - miễn là bạn có thể đủ khả năng đó. Một lần nữa, có một sự thay thế ít xâm lấn hơn: pg numpack

Sau đó bạn có thể thả chỉ mục một lần nữa. Đó là hiệu ứng một lần. Tôi ít nhất sẽ thử điều này một lần để xem các truy vấn của bạn được hưởng lợi bao nhiêu từ nó. Hiệu ứng xấu đi với các hoạt động ghi tiếp theo. Bạn có thể lặp lại quy trình này vào giờ nghỉ nếu bạn thấy hiệu quả rõ rệt.

Nếu bảng của bạn nhận được nhiều thao tác ghi, bạn phải cân nhắc chi phí và lợi ích cho bước này. Đối với nhiều CẬP NHẬT, hãy cân nhắc cài đặt FILLFACTORthấp hơn 100. Làm điều đó trước bạn CLUSTER.

Truy vấn

SELECT count(*)
FROM   objects
WHERE  id BETWEEN 0 AND 999999  -- match conditions of partial index!
AND    updated_at > '2012-10-01 0:0'::timestamp
AND    updated_at::date > (now()::date - 7)

Hơn

Câu trả lời liên quan này trình bày một kỹ thuật nâng cao hơn để phân vùng chỉ mục:

Trong số những thứ khác, nó cung cấp mã ví dụ để tạo chỉ mục (tái) tự động.

PostgreSQL 9.2+ có một số tính năng mới cho bạn. Chỉ quét chỉ mục sẽ làm cho nó đáng giá của bạn.

Hãy chắc chắn rằng nó autovacuumđang chạy đúng. Lợi nhuận khổng lồ mà CLUSTERbạn đã báo cáo có thể một phần là do sự ngầm định VACUUM FULLmà bạn nhận được CLUSTER. Có lẽ điều này được Heroku thiết lập tự động, không chắc chắn.
Các thiết lập trong câu hỏi của bạn trông tốt. Vì vậy, đó có lẽ không phải là một vấn đề ở đây và CLUSTERthực sự hiệu quả.

Phân vùng khai báo

cuối cùng đã trưởng thành trong Postgres 12 . Tôi sẽ xem xét sử dụng ngay bây giờ thay vì phân vùng chỉ mục thủ công (hoặc ít nhất là bổ sung). Phân vùng phạm vi với updated_atnhư là phím phân vùng. (Bên cạnh nhiều cải tiến về hiệu suất chung, dữ liệu lớn và hiệu suất chỉ số btree nói riêng.)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.