Tối ưu hóa truy vấn cơ sở dữ liệu lớn (hơn 25 triệu hàng, sử dụng max () và GROUP BY)


13

Tôi đang sử dụng Postgres 9.3.5 và tôi có một bảng lớn trong cơ sở dữ liệu, hiện tại nó có hơn 25 triệu hàng và nó có xu hướng lớn hơn nhanh chóng. Tôi đang cố gắng chọn các hàng cụ thể (tất cả unit_idchỉ có mới nhất unit_timestampcho mỗi hàng) với một truy vấn đơn giản như:

SELECT unit_id, max(unit_timestamp) AS latest_timestamp FROM all_units GROUP BY unit_id;

Không có bất kỳ chỉ mục nào, truy vấn này mất khoảng 35 giây để thực thi. Với một chỉ mục được xác định ( CREATE INDEX partial_idx ON all_units (unit_id, unit_timestamp DESC);) thời gian truy vấn được rút ngắn xuống còn khoảng (chỉ) 19 giây.

Tôi tự hỏi liệu có thể thực hiện truy vấn của mình trong thời gian ngắn hơn không (như chỉ vài giây) và nếu vậy, tôi nên thực hiện các bước nào để tối ưu hóa nó hơn nữa?

Kết cấu bảng của tôi trông như thế này:

CREATE TABLE "all_units" (
"unit_id" int4 NOT NULL,
"unit_timestamp" timestamp(6) NOT NULL,
"lon" float4,
"lat" float4,
"speed" float4,
"status" varchar(255) COLLATE "default"
)
ALTER TABLE "all_units" ADD PRIMARY KEY ("unit_id", "unit_timestamp");

Sau EXPLAIN (ANALYZE, BUFFERS)đây là

QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
HashAggregate  (cost=663998.38..664069.73 rows=7135 width=12) (actual time=84715.050..84732.021 rows=11094 loops=1)
  Buffers: shared hit=192 read=286819
  ->  Seq Scan on ais_sorted  (cost=0.00..538335.92 rows=25132492 width=12) (actual time=0.608..41264.196 rows=25132492 loops=1)
        Buffers: shared hit=192 read=286819
Total runtime: 84746.501 ms

và cài đặt psql của tôi trên máy chủ trông như thế này:

                name                 |  context   |  min_val  |   max_val    |                boot_val
-------------------------------------+------------+-----------+--------------+-----------------------------------------
 allow_system_table_mods             | postmaster |           |              | off
 application_name                    | user       |           |              |
 archive_command                     | sighup     |           |              |
 archive_mode                        | postmaster |           |              | off
 archive_timeout                     | sighup     | 0         | 1073741823   | 0
 array_nulls                         | user       |           |              | on
 authentication_timeout              | sighup     | 1         | 600          | 60
 autovacuum                          | sighup     |           |              | on
 autovacuum_analyze_scale_factor     | sighup     | 0         | 100          | 0.1
 autovacuum_analyze_threshold        | sighup     | 0         | 2147483647   | 50
 autovacuum_freeze_max_age           | postmaster | 100000000 | 2000000000   | 200000000
 autovacuum_max_workers              | postmaster | 1         | 8388607      | 3
 autovacuum_multixact_freeze_max_age | postmaster | 10000000  | 2000000000   | 400000000
 autovacuum_naptime                  | sighup     | 1         | 2147483      | 60
 autovacuum_vacuum_cost_delay        | sighup     | -1        | 100          | 20
 autovacuum_vacuum_cost_limit        | sighup     | -1        | 10000        | -1
 autovacuum_vacuum_scale_factor      | sighup     | 0         | 100          | 0.2
 autovacuum_vacuum_threshold         | sighup     | 0         | 2147483647   | 50
 backslash_quote                     | user       |           |              | safe_encoding
 bgwriter_delay                      | sighup     | 10        | 10000        | 200
 bgwriter_lru_maxpages               | sighup     | 0         | 1000         | 100
 bgwriter_lru_multiplier             | sighup     | 0         | 10           | 2
 block_size                          | internal   | 8192      | 8192         | 8192
 bonjour                             | postmaster |           |              | off
 bonjour_name                        | postmaster |           |              |
 bytea_output                        | user       |           |              | hex
 check_function_bodies               | user       |           |              | on
 checkpoint_completion_target        | sighup     | 0         | 1            | 0.5
 checkpoint_segments                 | sighup     | 1         | 2147483647   | 3
 checkpoint_timeout                  | sighup     | 30        | 3600         | 300
 checkpoint_warning                  | sighup     | 0         | 2147483647   | 30
 client_encoding                     | user       |           |              | SQL_ASCII
 client_min_messages                 | user       |           |              | notice
 commit_delay                        | superuser  | 0         | 100000       | 0
 commit_siblings                     | user       | 0         | 1000         | 5
 config_file                         | postmaster |           |              |
 constraint_exclusion                | user       |           |              | partition
 cpu_index_tuple_cost                | user       | 0         | 1.79769e+308 | 0.005
 cpu_operator_cost                   | user       | 0         | 1.79769e+308 | 0.0025
 cpu_tuple_cost                      | user       | 0         | 1.79769e+308 | 0.01
 cursor_tuple_fraction               | user       | 0         | 1            | 0.1
 data_checksums                      | internal   |           |              | off
 data_directory                      | postmaster |           |              |
 DateStyle                           | user       |           |              | ISO, MDY
 db_user_namespace                   | sighup     |           |              | off
 deadlock_timeout                    | superuser  | 1         | 2147483647   | 1000
 debug_assertions                    | user       |           |              | off
 debug_pretty_print                  | user       |           |              | on
 debug_print_parse                   | user       |           |              | off
 debug_print_plan                    | user       |           |              | off
 debug_print_rewritten               | user       |           |              | off
 default_statistics_target           | user       | 1         | 10000        | 100
 default_tablespace                  | user       |           |              |
 default_text_search_config          | user       |           |              | pg_catalog.simple
 default_transaction_deferrable      | user       |           |              | off
 default_transaction_isolation       | user       |           |              | read committed
 default_transaction_read_only       | user       |           |              | off
 default_with_oids                   | user       |           |              | off
 dynamic_library_path                | superuser  |           |              | $libdir
 effective_cache_size                | user       | 1         | 2147483647   | 16384
 effective_io_concurrency            | user       | 0         | 1000         | 1
 enable_bitmapscan                   | user       |           |              | on
 enable_hashagg                      | user       |           |              | on
 enable_hashjoin                     | user       |           |              | on
 enable_indexonlyscan                | user       |           |              | on
 enable_indexscan                    | user       |           |              | on
 enable_material                     | user       |           |              | on
 enable_mergejoin                    | user       |           |              | on
 enable_nestloop                     | user       |           |              | on
 enable_seqscan                      | user       |           |              | on
 enable_sort                         | user       |           |              | on
 enable_tidscan                      | user       |           |              | on
 escape_string_warning               | user       |           |              | on
 event_source                        | postmaster |           |              | PostgreSQL
 exit_on_error                       | user       |           |              | off
 external_pid_file                   | postmaster |           |              |
 extra_float_digits                  | user       | -15       | 3            | 0
 from_collapse_limit                 | user       | 1         | 2147483647   | 8
 fsync                               | sighup     |           |              | on
 full_page_writes                    | sighup     |           |              | on
 geqo                                | user       |           |              | on
 geqo_effort                         | user       | 1         | 10           | 5
 geqo_generations                    | user       | 0         | 2147483647   | 0
 geqo_pool_size                      | user       | 0         | 2147483647   | 0
 geqo_seed                           | user       | 0         | 1            | 0
 geqo_selection_bias                 | user       | 1.5       | 2            | 2
 geqo_threshold                      | user       | 2         | 2147483647   | 12
 gin_fuzzy_search_limit              | user       | 0         | 2147483647   | 0
 hba_file                            | postmaster |           |              |
 hot_standby                         | postmaster |           |              | off
 hot_standby_feedback                | sighup     |           |              | off
 ident_file                          | postmaster |           |              |
 ignore_checksum_failure             | superuser  |           |              | off
 ignore_system_indexes               | backend    |           |              | off
 integer_datetimes                   | internal   |           |              | on
 IntervalStyle                       | user       |           |              | postgres
 join_collapse_limit                 | user       | 1         | 2147483647   | 8
 krb_caseins_users                   | sighup     |           |              | off
 krb_server_keyfile                  | sighup     |           |              | FILE:/etc/postgresql-common/krb5.keytab
 krb_srvname                         | sighup     |           |              | postgres
 lc_collate                          | internal   |           |              | C
 lc_ctype                            | internal   |           |              | C
 lc_messages                         | superuser  |           |              |
 lc_monetary                         | user       |           |              | C
 lc_numeric                          | user       |           |              | C
 lc_time                             | user       |           |              | C
 listen_addresses                    | postmaster |           |              | localhost
 lo_compat_privileges                | superuser  |           |              | off
 local_preload_libraries             | backend    |           |              |
 lock_timeout                        | user       | 0         | 2147483647   | 0
 log_autovacuum_min_duration         | sighup     | -1        | 2147483647   | -1
 log_checkpoints                     | sighup     |           |              | off
 log_connections                     | backend    |           |              | off
 log_destination                     | sighup     |           |              | stderr
 log_directory                       | sighup     |           |              | pg_log
 log_disconnections                  | backend    |           |              | off
 log_duration                        | superuser  |           |              | off
 log_error_verbosity                 | superuser  |           |              | default
 log_executor_stats                  | superuser  |           |              | off
 log_file_mode                       | sighup     | 0         | 511          | 384
 log_filename                        | sighup     |           |              | postgresql-%Y-%m-%d_%H%M%S.log
 log_hostname                        | sighup     |           |              | off
 log_line_prefix                     | sighup     |           |              |
 log_lock_waits                      | superuser  |           |              | off
 log_min_duration_statement          | superuser  | -1        | 2147483647   | -1
 log_min_error_statement             | superuser  |           |              | error
 log_min_messages                    | superuser  |           |              | warning
 log_parser_stats                    | superuser  |           |              | off
 log_planner_stats                   | superuser  |           |              | off
 log_rotation_age                    | sighup     | 0         | 35791394     | 1440
 log_rotation_size                   | sighup     | 0         | 2097151      | 10240
 log_statement                       | superuser  |           |              | none
 log_statement_stats                 | superuser  |           |              | off
 log_temp_files                      | superuser  | -1        | 2147483647   | -1
 log_timezone                        | sighup     |           |              | GMT
 log_truncate_on_rotation            | sighup     |           |              | off
 logging_collector                   | postmaster |           |              | off
 maintenance_work_mem                | user       | 1024      | 2147483647   | 16384
 max_connections                     | postmaster | 1         | 8388607      | 100
 max_files_per_process               | postmaster | 25        | 2147483647   | 1000
 max_function_args                   | internal   | 100       | 100          | 100
 max_identifier_length               | internal   | 63        | 63           | 63
 max_index_keys                      | internal   | 32        | 32           | 32
 max_locks_per_transaction           | postmaster | 10        | 2147483647   | 64
 max_pred_locks_per_transaction      | postmaster | 10        | 2147483647   | 64
 max_prepared_transactions           | postmaster | 0         | 8388607      | 0
 max_stack_depth                     | superuser  | 100       | 2147483647   | 100
 max_standby_archive_delay           | sighup     | -1        | 2147483647   | 30000
 max_standby_streaming_delay         | sighup     | -1        | 2147483647   | 30000
 max_wal_senders                     | postmaster | 0         | 8388607      | 0
 password_encryption                 | user       |           |              | on
 port                                | postmaster | 1         | 65535        | 5432
 post_auth_delay                     | backend    | 0         | 2147         | 0
 pre_auth_delay                      | sighup     | 0         | 60           | 0
 quote_all_identifiers               | user       |           |              | off
 random_page_cost                    | user       | 0         | 1.79769e+308 | 4
 restart_after_crash                 | sighup     |           |              | on
 search_path                         | user       |           |              | "$user",public
 segment_size                        | internal   | 131072    | 131072       | 131072
 seq_page_cost                       | user       | 0         | 1.79769e+308 | 1
 server_encoding                     | internal   |           |              | SQL_ASCII
 server_version                      | internal   |           |              | 9.3.5
 server_version_num                  | internal   | 90305     | 90305        | 90305
 session_replication_role            | superuser  |           |              | origin
 shared_buffers                      | postmaster | 16        | 1073741823   | 1024
 shared_preload_libraries            | postmaster |           |              |
 sql_inheritance                     | user       |           |              | on
 ssl                                 | postmaster |           |              | off
 ssl_ca_file                         | postmaster |           |              |
 ssl_cert_file                       | postmaster |           |              | server.crt
 ssl_ciphers                         | postmaster |           |              | DEFAULT:!LOW:!EXP:!MD5:@STRENGTH
 ssl_crl_file                        | postmaster |           |              |
 ssl_key_file                        | postmaster |           |              | server.key
 ssl_renegotiation_limit             | user       | 0         | 2147483647   | 524288
 standard_conforming_strings         | user       |           |              | on
 statement_timeout                   | user       | 0         | 2147483647   | 0
 stats_temp_directory                | sighup     |           |              | pg_stat_tmp
 superuser_reserved_connections      | postmaster | 0         | 8388607      | 3
 synchronize_seqscans                | user       |           |              | on
 synchronous_commit                  | user       |           |              | on
 synchronous_standby_names           | sighup     |           |              |
 syslog_facility                     | sighup     |           |              | local0
 syslog_ident                        | sighup     |           |              | postgres
 tcp_keepalives_count                | user       | 0         | 2147483647   | 0
 tcp_keepalives_idle                 | user       | 0         | 2147483647   | 0
 tcp_keepalives_interval             | user       | 0         | 2147483647   | 0
 temp_buffers                        | user       | 100       | 1073741823   | 1024
 temp_file_limit                     | superuser  | -1        | 2147483647   | -1
 temp_tablespaces                    | user       |           |              |
 TimeZone                            | user       |           |              | GMT
 timezone_abbreviations              | user       |           |              |
 trace_notify                        | user       |           |              | off
 trace_recovery_messages             | sighup     |           |              | log
 trace_sort                          | user       |           |              | off
 track_activities                    | superuser  |           |              | on
 track_activity_query_size           | postmaster | 100       | 102400       | 1024
 track_counts                        | superuser  |           |              | on
 track_functions                     | superuser  |           |              | none
 track_io_timing                     | superuser  |           |              | off
 transaction_deferrable              | user       |           |              | off
 transaction_isolation               | user       |           |              | default
 transaction_read_only               | user       |           |              | off
 transform_null_equals               | user       |           |              | off
 unix_socket_directories             | postmaster |           |              | /var/run/postgresql
 unix_socket_group                   | postmaster |           |              |
 unix_socket_permissions             | postmaster | 0         | 511          | 511
 update_process_title                | superuser  |           |              | on
 vacuum_cost_delay                   | user       | 0         | 100          | 0
 vacuum_cost_limit                   | user       | 1         | 10000        | 200
 vacuum_cost_page_dirty              | user       | 0         | 10000        | 20
 vacuum_cost_page_hit                | user       | 0         | 10000        | 1
 vacuum_cost_page_miss               | user       | 0         | 10000        | 10
 vacuum_defer_cleanup_age            | sighup     | 0         | 1000000      | 0
 vacuum_freeze_min_age               | user       | 0         | 1000000000   | 50000000
 vacuum_freeze_table_age             | user       | 0         | 2000000000   | 150000000
 vacuum_multixact_freeze_min_age     | user       | 0         | 1000000000   | 5000000
 vacuum_multixact_freeze_table_age   | user       | 0         | 2000000000   | 150000000
 wal_block_size                      | internal   | 8192      | 8192         | 8192
 wal_buffers                         | postmaster | -1        | 2147483647   | -1
 wal_keep_segments                   | sighup     | 0         | 2147483647   | 0
 wal_level                           | postmaster |           |              | minimal
 wal_receiver_status_interval        | sighup     | 0         | 2147483      | 10
 wal_receiver_timeout                | sighup     | 0         | 2147483647   | 60000
 wal_segment_size                    | internal   | 2048      | 2048         | 2048
 wal_sender_timeout                  | sighup     | 0         | 2147483647   | 60000
 wal_sync_method                     | sighup     |           |              | fdatasync
 wal_writer_delay                    | sighup     | 1         | 10000        | 200
 work_mem                            | user       | 64        | 2147483647   | 1024
 xmlbinary                           | user       |           |              | base64
 xmloption                           | user       |           |              | content
 zero_damaged_pages                  | superuser  |           |              | off

Bạn có thực sự cần TẤT CẢ unit_ids không? Bởi vì mệnh đề WHERE sẽ giúp ích.
Mihai

Thật không may, tôi cần TẤT CẢ chúng, và để làm cho nó tệ hơn nữa, ngay bây giờ tôi nhận được "chỉ" 11000+ đơn vị, nhưng trong tương lai tôi tin rằng sẽ có gấp 5-10 lần số đó.
errata

Hãy thử SELECT DISTINCT ON (unit_id),unit_timestamp FROM t ORDER BY unit_timestamp DESCMột chỉ mục riêng trên unit_timestamp sẽ giúp ích.
Mihai

được vật chất hóa xem một lựa chọn?
user1363989

Hừm, không biết gì về quan điểm cụ thể hóa cả. Tôi sẽ cố gắng tạo một cái và xem nếu nó sẽ hữu ích!
errata

Câu trả lời:


12

Truy vấn

Truy vấn của bạn buộc phải quét toàn bộ bảng (hoặc toàn bộ chỉ mục). Mỗi hàng có thể là một đơn vị khác biệt. Cách duy nhất để rút ngắn đáng kể quá trình sẽ là một bảng riêng biệt với tất cả các đơn vị có sẵn - sẽ giúp miễn là có ít đơn vị hơn đáng kể so với các mục trong all_units.
Vì bạn có ~ 11k đơn vị (được thêm vào trong nhận xét) cho 25 triệu mục, điều này chắc chắn sẽ giúp ích.

Tùy thuộc vào tần suất của các giá trị, có một vài kỹ thuật truy vấn để có được kết quả của bạn nhanh hơn đáng kể:

  • CTE đệ quy
  • JOIN LATERAL
  • truy vấn con tương quan

Chi tiết trong câu trả lời liên quan này trên SO:

Chỉ cần chỉ mục ngầm định của khóa chính (unit_id, unit_timestamp), truy vấn này sẽ thực hiện thủ thuật, sử dụng ẩn JOIN LATERAL:

SELECT u.unit_id, a.max_ts
FROM unit u
  , (SELECT unit_timestamp AS max_ts
     FROM   all_units
     WHERE  unit_id = u.unit_id
     ORDER  BY unit_timestamp DESC
     LIMIT  1
     ) a;

Không bao gồm các đơn vị không có mục nhập all_units, như truy vấn ban đầu của bạn.
Hoặc một truy vấn con có tương quan thấp (thậm chí có thể nhanh hơn):

SELECT u.unit_id
    , (SELECT unit_timestamp
       FROM   all_units
       WHERE  unit_id = u.unit_id
       ORDER  BY unit_timestamp DESC
       LIMIT  1) AS max_ts
FROM unit u;

Bao gồm các đơn vị không có mục nhập all_units.

Hiệu quả phụ thuộc vào số lượng mục trên mỗi đơn vị . Càng nhiều mục, càng có tiềm năng cho một trong những truy vấn này.

Trong thử nghiệm cục bộ nhanh với các bảng tương tự (500 "đơn vị", 1M hàng trong bảng lớn), truy vấn với các truy vấn con tương quan nhanh hơn ~ 500 lần so với ban đầu của bạn. Chỉ quét chỉ mục trên chỉ mục PK của bảng lớn so với quét tuần tự trong truy vấn ban đầu của bạn.

Vì bảng của bạn tends to get even larger rapidly, một khung nhìn cụ thể hóa có lẽ không phải là một lựa chọn.

Ngoài ra còn có DISTINCT ONmột kỹ thuật truy vấn khả thi khác, nhưng hầu như không nhanh hơn truy vấn ban đầu của bạn, vì vậy không phải là câu trả lời bạn đang tìm kiếm. Chi tiết tại đây:

Mục lục

của bạn partial_idx:

CREATE INDEX partial_idx ON all_units (unit_id, unit_timestamp DESC);

thực tế không phải là một chỉ số một phần và cũng dư thừa. Postgres có thể quét các chỉ mục ngược với tốc độ thực tế, PK phục vụ tốt. Thả chỉ số bổ sung này.

Bố trí bảng

Một vài điểm cho định nghĩa bảng của bạn.

CREATE TABLE all_units (
unit_timestamp timestamp,
unit_id int4,
lon     float4,
lat     float4,
speed   float4,
status  varchar(255),   -- might be improved.
PRIMARY KEY (unit_id, unit_timestamp)
);
  • timestamp(6)không có ý nghĩa gì nhiều, nó thực sự giống như chỉ timestamp, nó đã lưu tối đa 6 chữ số phân số.

  • Tôi đã chuyển đổi vị trí của hai cột đầu tiên để tiết kiệm 4 byte đệm, tương đương ~ 100 MB cho các hàng 25M (kết quả chính xác phụ thuộc vào status). Các bảng nhỏ hơn thường nhanh hơn cho mọi thứ.

  • Nếu statuskhông phải là văn bản miễn phí, nhưng một số loại ghi chú được tiêu chuẩn hóa, bạn có thể thay thế nó bằng một cái gì đó rẻ hơn rất nhiều. Thông tin thêm về varchar(255)Postgres .

Cấu hình máy chủ

Bạn cần cấu hình máy chủ của bạn. Hầu hết các thiết lập của bạn dường như là mặc định bảo thủ. 1 MB trên shared_buffershoặc work_memdường như thấp đến mức cài đặt với hàng triệu hàng. Và random_pare_cost = 4là cao cho bất kỳ hệ thống hiện đại với nhiều RAM. Bắt đầu với hướng dẫn và Wiki Postgres:


Để đầy đủ, bạn nên thêm DISTINCT ONmthod vào danh sách các kỹ thuật truy vấn cho vấn đề này (tất nhiên nếu OP muốn hiển thị tất cả các hàng như anh ta yêu cầu, và không chỉ 2 cột như mã hiển thị)
ypercubeᵀᴹ

@ypercube: Để hoàn thiện, vâng. Nhưng nó khó có thể nhanh hơn. Không phải là câu trả lời mà OP đang tìm kiếm.
Erwin Brandstetter

Bạn có thể giải thích cách CTE đệ quy sẽ nhanh hơn (hoặc thậm chí: cách giải quyết vấn đề ngay từ đầu - tôi chưa bao giờ nghĩ về CTE đệ quy cho vấn đề "tối đa")
a_horse_with_no_name

1
@Mihai: không, nó chỉ có quét chỉ mục bitmap (nghĩa là nó tạo ra chỉ mục bitmap "đang hoạt động")
a_horse_with_no_name

3
@ErwinBrandstetter TRỞ LÊN! Trước hết cảm ơn bạn rất nhiều vì câu trả lời công phu này, nó cực kỳ hữu ích, tôi không thể giải thích cho bạn bao nhiêu! :) Thứ hai, truy vấn của bạn (cái thứ hai) là TUYỆT VỜI! Thời gian truy vấn bây giờ là khoảng 0,2 giây !!! :) Tôi cũng đã cố gắng chơi xung quanh với truy vấn đầu tiên nhưng tôi đã không quản lý để giải quyết lỗi tôi gặp: invalid reference to FROM-clause entry for table "u"trong WHEREmệnh đề này trong truy vấn bên trong. Xin lỗi, nhưng bản thân tôi không giỏi về cơ sở dữ liệu nên tôi đã không áp đặt nó vì truy vấn thứ hai đã giúp ích rất nhiều.
errata
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.