私はPostgres 9.3.5を使用していて、データベースに大きなテーブルがあります。現在、2,500万行を超えており、急速に大きくなる傾向があります。次のような簡単なクエリを使用して、特定の行(各_unit_id
_ sに最新の_unit_timestamp
_のみを含む)を選択しようとしています。
_SELECT unit_id, max(unit_timestamp) AS latest_timestamp FROM all_units GROUP BY unit_id;
_
インデックスがない場合、このクエリの実行には約35秒かかります。インデックスが定義されている場合(CREATE INDEX partial_idx ON all_units (unit_id, unit_timestamp DESC);
)、クエリ時間は約(わずか)19秒に短縮されます。
クエリをさらに短い時間(わずか数秒など)で実行できるようになるかどうか、また実行できる場合、それをさらに最適化するためにどのような手順を実行する必要がありますか?
私のテーブル構造ダンプは次のようになります:
_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");
_
EXPLAIN (ANALYZE, BUFFERS)
は次のとおりです。
_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
_
サーバー上の私のpsql設定は次のようになります:
_ 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
_
クエリはテーブル全体(またはインデックス全体)をスキャンするように強制されます。すべての行couldは別の個別の単位です。プロセスを大幅に短縮する唯一の方法は、使用可能なすべての単位を含む個別のテーブルです。これは、 _all_units
_。
2500万件のエントリに11,000ユニット(コメントで追加)があるため、これは間違いなく役立ちます。
値の頻度に応じて、結果をかなり速く取得するいくつかのクエリテクニックがあります。
JOIN LATERAL
_SOに関するこの関連回答の詳細:
_(unit_id, unit_timestamp)
_の主キーの暗黙的なインデックスのみが必要な場合、このクエリは暗黙的な_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;
_
元のクエリのように、_all_units
_にエントリのないユニットを除外します。
または相関性の低いサブクエリ(おそらくさらに高速):
_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;
_
_all_units
_にエントリのないユニットを含みます。
効率はエントリの数によって異なりますユニットごと。エントリが多いほど、これらのクエリのいずれかの可能性が高くなります。
同様のテーブル(500「ユニット」、大きなテーブルに100万行)のあるローカルテストで、相関サブクエリを持つクエリは〜オリジナルより500倍高速です。大きなテーブルのPKインデックスに対するインデックスのみのスキャンと、元のクエリの順次スキャン。
テーブルは_tends to get even larger rapidly
_なので、 マテリアライズドビュー はおそらくオプションではありません。
別の可能なクエリ手法として_DISTINCT ON
_もありますが、元のクエリよりも速くなることはほとんどないため、探しているのはnot the answerです。詳細はこちら:
あなたの_partial_idx
_:
CREATE INDEX partial_idx ON all_units (unit_id, unit_timestamp DESC);
実際には 部分インデックス ではなく、冗長でもあります。 Postgresは実質的に同じ速度で逆方向にインデックスをスキャンできます。PKは適切に機能します。 Dropこの追加のインデックス。
テーブル定義のポイント。
_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)
はあまり意味がありませんが、実質的には timestamp
と実質的に同じであり、すでに最大で6桁の小数部を節約しています。
最初の2列の位置を切り替えて、4バイトのパディングを節約しました。これは、2500万行で最大100 MBになります(正確な結果はstatus
によって異なります)。小さいテーブルは通常、すべてに対して高速です。
status
がフリーテキストではなく、ある種の標準化されたメモである場合は、もっと安いものに置き換えることができます。 Postgresのvarchar(255)
の詳細 。
サーバーを構成する必要があります。ほとんどの設定は控えめなデフォルトのようです。数百万行のインストールの場合、_shared_buffers
_または_work_mem
_で1 MBが低いように見えます。また、RAMが十分にある現代のシステムでは、_random_pare_cost = 4
_は高すぎます。マニュアルとPostgres Wikiから始めます。