การเพิ่มประสิทธิภาพดัชนีพร้อมวันที่


27

ฉันมีตารางวัตถุขนาดใหญ่ (แถว 15M +) ใน PostgreSQL 9.0.8 ซึ่งฉันต้องการค้นหาเขตข้อมูลที่ล้าสมัย

ฉันต้องการแบ่งคำถามเป็นล้าน ๆ เพื่อความยืดหยุ่นในการปรับขนาดและการทำงานพร้อมกันและฉันต้องการดึงข้อมูลทั้งหมดด้วยฟิลด์ updated_at ด้วยวันที่ไม่กี่วันที่ผ่านมา

ฉันได้ลองใช้ดัชนีจำนวนมากและข้อความค้นหาหลายล้านรายการและดูเหมือนว่าฉันจะไม่สามารถทำงานได้ภายใน 100 วินาทีด้วยฮาร์ดแวร์ Ronin ของ Heroku

ฉันกำลังมองหาคำแนะนำที่ฉันไม่ได้พยายามทำให้มีประสิทธิภาพมากที่สุด

ลอง # 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

ลอง # 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

ลอง # 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

ลอง # 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

ลอง # 5 (สำหรับข้อมูลล้าสมัยหนึ่งเดือน)

 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

ลอง # 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

ลอง # 7 (ดู: 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 

ลอง # 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

ลอง # 9 ( ดัชนีบางส่วนตามคำแนะนำของเออร์วินดู: 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ตามข้อเสนอแนะของเออร์วิน)

 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

==> วิธีนี้ดูเหมือนว่าจะเป็นผู้ชนะ ฉันจะต้องทดสอบอย่างละเอียดเพื่อยืนยัน counterimpacts ทุกที่ในใบสมัครของฉัน

การตั้งค่าฐานข้อมูล:

เลือกชื่อ, min_val, max_val, boot_val จาก pg_settings;

             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)

โครงสร้างของตารางมีคอลัมน์ประมาณ 20-30 คอลัมน์มีคีย์ต่างประเทศจำนวนเต็มจำนวนหนึ่งสตริงข้อความบูลีน นิยามดัชนีอยู่ในการโพสต์ด้านบนถัดจาก INDEX USEd (ฉันโพสต์ 8 ดัชนีที่ใช้โดยการสืบค้น) ฉันมีดัชนีเพิ่มอีกเล็กน้อยสำหรับการอัปเดตที่รวดเร็วขึ้นและเลือกสำหรับแอปพลิเคชันของฉัน ในที่สุดฉันใช้ cloud DB และฉันไม่ได้เปลี่ยนแปลงอะไรเลย ฉันสงสัยว่าส่วนใหญ่ถ้าคำจำกัดความของดัชนีของฉันดีเท่าที่จะได้รับก่อนที่จะเพิ่มประสิทธิภาพ อย่างไรก็ตามฉันจะอัพเดทด้วยข้อมูล
xlash

คำตอบ:


30

ก่อนอื่นได้ไหม ที่คุณเขียน:

ฉันต้องการที่จะดึงข้อมูลทั้งหมดที่มีข้อมูล updated_at ที่มีวันที่ของไม่กี่วันที่ผ่านมา

แต่WHEREสภาพของคุณคือ:

(date (updated_at)) < (date (now ()) - 7)

ไม่ควรที่จะเป็น>อย่างไร


ดัชนี

เพื่อประสิทธิภาพที่ดีที่สุดคุณสามารถ ...

  • แบ่งดัชนีของคุณ
  • แยกแถวที่ไม่เกี่ยวข้องออกจากดัชนี
  • สร้างดัชนีใหม่โดยอัตโนมัตินอกเวลาทำการโดยใช้เพรดิเคตที่อัปเดตแล้ว

ดัชนีของคุณอาจมีลักษณะดังนี้:

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

...

เงื่อนไขที่สองไม่รวมแถวที่ไม่เกี่ยวข้องออกจากดัชนีทันทีซึ่งควรทำให้เล็กลงและเร็วขึ้น - ขึ้นอยู่กับการกระจายข้อมูลที่แท้จริงของคุณ ตามความคิดเห็นเบื้องต้นของฉันฉันถือว่าคุณต้องการแถวที่ใหม่กว่า

เงื่อนไขยังไม่รวมค่า NULL โดยอัตโนมัติupdated_atซึ่งคุณดูเหมือนจะอนุญาตในตารางและต้องการแยกออกจากแบบสอบถามอย่างชัดเจน ประโยชน์ของดัชนีจะลดลงเมื่อเวลาผ่านไป แบบสอบถามจะดึงรายการล่าสุดเสมอ สร้างดัชนีใหม่ด้วยส่วนWHEREคำสั่งที่ปรับปรุงเป็นระยะ ต้องใช้การล็อกแบบเอกสิทธิ์เฉพาะบุคคลบนตารางดังนั้นทำในเวลาปิด นอกจากนี้ยังมีCREATE INDEX CONCURRENTLYการลดระยะเวลาของการล็อค:

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

คำตอบที่เกี่ยวข้องกับ SO:

เพื่อเพิ่มประสิทธิภาพต่อไปคุณสามารถใช้CLUSTERเหมือนที่เราพูดถึงในความคิดเห็น แต่คุณต้องการดัชนีแบบเต็มสำหรับสิ่งนั้น ไม่ทำงานกับดัชนีบางส่วน คุณจะสร้างชั่วคราว:

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

รูปแบบของดัชนีแบบเต็มนี้ตรงกับลำดับการเรียงของดัชนีบางส่วนด้านบน

CLUSTER objects USING objects_full_idx;
ANALYZE objects;

จะใช้เวลาสักครู่เนื่องจากตารางจะถูกเขียนใหม่ทางร่างกาย VACUUM FULLนอกจากนี้ยังมีประสิทธิภาพ มันต้องการล็อคการเขียนพิเศษบนโต๊ะดังนั้นจึงทำได้นอกเวลาทำการ - หากคุณสามารถซื้อได้ อีกครั้งมีทางเลือกที่รุกรานน้อยกว่า: pg_repack

จากนั้นคุณสามารถปล่อยดัชนีอีกครั้ง มันเป็นเอฟเฟกต์ครั้งเดียว อย่างน้อยฉันก็จะลองทำสิ่งนี้ครั้งเดียวเพื่อดูว่าข้อความค้นหาของคุณได้รับประโยชน์อย่างไร ผลกระทบจะลดลงเมื่อทำการเขียนในภายหลัง คุณสามารถทำซ้ำขั้นตอนนี้ได้ในเวลาไม่กี่ชั่วโมง

หากตารางของคุณได้รับการเขียนจำนวนมากคุณต้องชั่งน้ำหนักต้นทุนและผลประโยชน์สำหรับขั้นตอนนี้ สำหรับการปรับปรุงหลายคนคิดว่าการตั้งค่าFILLFACTORต่ำกว่า 100 Do ว่าก่อนที่CLUSTERคุณ

สอบถาม

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)

มากกว่า

คำตอบที่เกี่ยวข้องนี้นำเสนอเทคนิคขั้นสูงสำหรับการแบ่งดัชนี:

เหนือสิ่งอื่นใดมันมีรหัสตัวอย่างสำหรับการสร้างดัชนีอัตโนมัติ

PostgreSQL 9.2+มีคุณสมบัติใหม่มากมายสำหรับคุณ การสแกนเฉพาะดัชนีเท่านั้นจะทำให้คุ้มค่าในขณะที่คุณ

ตรวจสอบให้แน่ใจว่าautovacuumทำงานอย่างถูกต้อง กำไรขนาดใหญ่โดยการCLUSTERที่คุณได้รายงานอาจจะเป็นเพราะในส่วนที่ส่อให้เห็นว่าคุณได้รับจากVACUUM FULL CLUSTERบางทีนี่อาจถูกตั้งค่าโดย Heroku โดยอัตโนมัติไม่แน่ใจ
การตั้งค่าในคำถามของคุณดูดี นั่นอาจไม่ใช่ปัญหาที่นี่และCLUSTERมันก็มีประสิทธิภาพจริงๆ

การแบ่งพาร์ติชันแบบประกาศ

ได้ครบกำหนดในที่สุดPostgres 12 ฉันจะพิจารณาใช้ตอนนี้แทนการแบ่งดัชนีคู่มือ (หรืออย่างน้อยก็เพิ่มเติม) การแบ่งพาร์ติชันช่วงด้วยupdated_atเป็นคีย์พาร์ติชัน (นอกเหนือจากการปรับปรุงประสิทธิภาพทั่วไปหลายประการข้อมูลขนาดใหญ่และประสิทธิภาพดัชนี btree โดยเฉพาะ)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.