ฉันได้ลงลึกเกี่ยวกับการตั้งค่าหน่วยความจำ PostgreSQL ในช่วงนี้ โดยมองหาวิธีการปรับแต่งและเพิ่มประสิทธิภาพ หลังจากที่ได้ค้นคว้าข้อมูลและทำการทดสอบหลายอย่าง ฉันพบการตั้งค่าที่สำคัญบางอย่างที่สามารถทำให้เกิดความแตกต่างอย่างมาก.

ถ้าคุณกำลังใช้งาน PostgreSQL และต้องการเพิ่มประสิทธิภาพการใช้หน่วยความจำ นี่คือการตั้งค้าที่สำคัญบางอย่างที่ควรใส่ใจ.

การตั้งค่าหน่วยความจำที่สำคัญที่ต้องปรับ

มาดูการตั้งค่าหน่วยความจำ PostgreSQL ที่สำคัญที่สุดที่คุณควรพิจารณาปรับกัน.

  1. shared_buffers

คิดว่า shared_buffers เป็นที่เก็บหน่วยความจำส่วนตัวของ PostgreSQL ที่เก็บข้อมูลที่เข้าถึงบ่อย แนวคิดคือการเก็บข้อมูลของคุณในหน่วยความจำให้มากที่สุดเพื่อลดการอ่านจากดิสก์ที่มีค่าใช้จ่ายสูง.

การตั้งค่าที่แนะนำ:

ผู้เชี่ยวชาญส่วนใหญ่แนะนำให้ตั้งค่านี้ประมาณ 25% ของ RAM รวมของระบบคุณ.

ยกตัวอย่างเช่น ถ้าคุณมี 16GB ของ RAM การตั้งค่าให้ประมาณ 4GB เป็นจุดเริ่มต้นที่ดี.

  1. work_mem

เมื่อใดก็ตามที่ PostgreSQL ประมวลผลคำค้นหาที่เกี่ยวข้องกับการเรียงลำดับ การเชื่อมโยง หรือการจัดกลุ่มข้อมูล มันจะใช้ work_mem การตั้งค่านี้ควบคุมว่ามีหน่วยความจำเท่าไหร่ที่แต่ละการดำเนินการจะได้รับก่อนที่จะเริ่มเขียนไฟล์ชั่วคราวลงดิสก์.

ตัวอย่าง:

  • ถ้า work_mem = 50MB และคำค้นหาของคุณมี 5 งานการเรียงลำดับ มันอาจใช้หน่วยความจำ 50MB x 5 = 250MB.
  • ตอนนี้ลองนึกภาพ 10 ผู้ใช้ ที่รันคำค้นหาที่คล้ายกัน – นั่นคือ 250MB x 10 = 2.5GB ของหน่วยความจำที่ถูกใช้งาน.

เคล็ดลับ:

  • ตั้งค่า ต่ำเกินไป และคำค้นหาอาจช้าลงเนื่องจากการใช้ดิสก์.
  • ตั้งค่า สูงเกินไป และคุณอาจจะหมดหน่วยความเร็วอย่างรวดเร็ว โดยเฉพาะอย่างยิ่งเมื่อมีผู้ใช้จำนวนมาก.

บางครั้ง ค่าของ work_mem ที่ใหญ่กว่าสามารถทำให้ช้าลงได้จริง เพราะ PostgreSQL ต้องจัดการกับหน่วยความจำขนาดใหญ่.

  1. effective_cache_size

การตั้งค่านี้จะแตกต่างออกไปเล็กน้อย – มันไม่ได้จัดสรรหน่วยความจำจริง แต่บอก PostgreSQL ว่ามัน คิดว่า มันสามารถใช้หน่วยความจำจาก OS cache ได้เท่าไหร่ PostgreSQL ใช้ค่าตัวนี้ในการตัดสินใจว่าควรใช้ดัชนีหรือทำการสแกนแบบต่อเนื่อง.

การตั้งค่าผิด:

  • ต่ำเกินไป? PostgreSQL อาจหลีกเลี่ยงการใช้ดัชนี โดยคิดว่าหน่วยความจำมีน้อย.
  • สูงเกินไป? PostgreSQL อาจมีความหวังมากเกินไปและตัดสินใจผิดพลาด.

กฎทั่วไป: ตั้งค่านี้ให้ 50-75% ของ RAM รวมของคุณ โดยเฉพาะถ้า PostgreSQL เป็นงานหลักบนเครื่อง.

  1. maintenance_work_mem

การตั้งค่านี้เกี่ยวกับจำนวนหน่วยความจำที่ PostgreSQL สามารถใช้สำหรับการทำงานต่างๆ เช่น VACUUM, CREATE INDEX และงานบำรุงรักษาอื่นๆ การตั้งค่านี้ให้สูงขึ้นสามารถทำให้การดำเนินการเหล่านี้เร็วขึ้นโดยไม่ทำให้กิจกรรมฐานข้อมูลปกติช้าลง.

การตั้งค่าที่แนะนำ:

ถ้าคุณมีพื้นที่ว่าง ให้เพิ่มเป็น 1GB หรือมากกว่า เพื่อให้การทำงานบำรุงรักษาเป็นไปอย่างรวดเร็ว.

วิธีการปรับเปลี่ยนการตั้งค่าหน่วยความจำ

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

  1. การปรับเปลี่ยนการตั้งค่าหน่วยความจำทั่วโลก

สำหรับการเปลี่ยนแปลงถาวรที่มีผลต่อทุกเซสชันและคำค้นหา ให้ปรับไฟล์กำหนดค่าของ PostgreSQL (postgresql.conf) หรือใช้ ALTER SYSTEM:


ALTER SYSTEM SET work_mem = '500MB';
ALTER SYSTEM SET shared_buffers = '8GB';
ALTER SYSTEM SET effective_cache_size = '20GB';
SELECT pg_reload_conf();  -- ใช้การเปลี่ยนแปลงโดยไม่ต้องรีสตาร์ท
  1. การเปลี่ยนแปลงการตั้งค่าต่อเซสชัน

สำหรับการปรับเปลี่ยนชั่วคราวที่มีผลเฉพาะการเชื่อมต่อปัจจุบัน:

SET work_mem = '100MB';
SET effective_cache_size = '16GB';

ตัวอย่างจริงของการปรับแต่งหน่วยความจำ PostgreSQL

สถานการณ์ ##o

ฐานข้อมูล PostgreSQL 10 ที่ทำงานบนเซิร์ฟเวอร์ 30GB RAM, โดยมักจะมี RAM ว่าง 15GB กำลังประสบปัญหา การส่งออก CSV ช้า เมื่อประมวลผลธุรกรรมทางการเงิน คำค้นหาที่ซับซ้อนที่เชื่อมโยง สี่ตารางใหญ่ ใช้เวลาถึง 33 วินาที ในการแสดงผล:


SELECT * FROM finance_transaction ft
INNER JOIN billing_record br ON br.id = ft.billing_record
INNER JOIN transaction_log tl ON ft.transaction_log = tl.id
INNER JOIN transaction_audit_history tah ON tah.finance_transaction = ft.id;

ขนาดของตาราง:

  • finance_transaction (~1 ล้านแถว)
  • transaction_log (~1 ล้านแถว)
  • transaction_audit_history (~4 ล้านแถว)
  • billing_record (~1 ล้านแถว)

การตั้งค่าหน่วยความจำเริ่มต้น:

การตั้งค่าค่าปริยาย
shared_buffers128MB
work_mem4MB
maintenance_work_mem64MB
effective_cache_size4GB

แผนการดำเนินการค้นหา (EXPLAIN ANALYZE)

Gather  (cost=1071962.33..3055242.09 rows=6022483 width=4782) (actual time=26603.645..33465.648 rows=4847517 loops=1)
  Workers Planned: 4
  Workers Launched: 4
  ->  Hash Join  (cost=1070962.33..2451993.79 rows=1505621 width=4782) (actual time=26817.703..29038.177 rows=969503 loops=5)
        Hash Cond: (ft.id = tah.finance_transaction)
        ->  Hash Join  (cost=716707.62..1254298.44 rows=403544 width=4591) (actual time=18471.362..19081.998 rows=259931 loops=5)
              Hash Cond: (br.id = ft.billing_record)
              ->  Parallel Seq Scan on billing_record br  (cost=0.00..39488.92 rows=335492 width=3598) (actual time=0.005..42.297 rows=268393 loops=5)
              ->  Hash  (cost=501594.96..501594.96 rows=1564453 width=993) (actual time=18468.587..18468.589 rows=1299656 loops=5)
                    Buckets: 4096  Batches: 512  Memory Usage: 708kB
                    ->  Hash Join  (cost=103452.20..501594.96 rows=1564453 width=993) (actual time=9094.933..15042.241 rows=1300948 loops=5)
                          Hash Cond: (tl.id = ft.transaction_log)
                          ->  Seq Scan on transaction_log tl  (cost=0.00..33484.53 rows=1564453 width=766) (actual time=0.019..133.507 rows=1564453 loops=5)
                          ->  Hash  (cost=46549.31..46549.31 rows=1300631 width=227) (actual time=9092.767..9092.768 rows=1300948 loops=5)
                                Buckets: 16384  Batches: 128  Memory Usage: 2199kB
                                ->  Seq Scan on finance_transaction ft  (cost=0.00..46549.31 rows=1300631 width=227) (actual time=0.005..143.568 rows=1300948 loops=5)
        ->  Hash  (cost=165645.54..165645.54 rows=4852654 width=191) (actual time=8267.360..8267.360 rows=4852654 loops=5)
              Buckets: 32768  Batches: 512  Memory Usage: 2059kB
              ->  Seq Scan on transaction_audit_history tah  (cost=0.00..165645.54 rows=4852654 width=191) (actual time=0.020..789.242 rows=4852654 loops=5)
Planning time: 0.346 ms
Execution time: 33586.541 ms

วิเคราะห์แผนการดำเนินการค้นหา:

ปัญหาการสังเกตจาก EXPLAIN ANALYZEสาเหตุที่เป็นไปได้วิธีแก้ไข
การใช้ดิสก์ใน Hash JoinsBuckets: 4096 → Batches: 512 Memory Usage: 708kBwork_mem ต่ำเกินไป (ค่าปริยาย 4MB)เพิ่ม work_mem เป็น 300MB ALTER SYSTEM SET work_mem = '300MB';
I/O สูงจาก Sequential ScansParallel Seq Scans บน billing_record, finance_transaction, และ transaction_logshared_buffers ต่ำเกินไป (ค่าปริยาย 128MB)เพิ่ม shared_buffers เป็น 8GB ALTER SYSTEM SET shared_buffers = '8GB';
Query Planner ประเมิน RAM ที่มีอยู่ต่ำเกินไปใช้การสแกนแบบต่อเนื่องมากกว่าการสแกนดัชนีeffective_cache_size ต่ำเกินไป (ค่าปริยาย 4GB)เพิ่ม effective_cache_size เป็น 20GB ALTER SYSTEM SET effective_cache_size = '20GB';

หลังจากเพิ่มการตั้งค่าหน่วยความจำ นี่คือผลลัพธ์ EXPLAIN ANALYZE:

Gather  (cost=1071962.33..3055242.09 rows=6022483 width=4782) (actual time=16228.246..23884.145 rows=4847517 loops=1)
  Workers Planned: 4
  Workers Launched: 4
  ->  Hash Join  (cost=1070962.33..2451993.79 rows=1505621 width=4782) (actual time=17226.338..20072.376 rows=969503 loops=5)
        Hash Cond: (ft.id = tah.finance_transaction)
        ->  Hash Join  (cost=716707.62..1254298.44 rows=403544 width=4591) (actual time=14289.482..15561.150 rows=259931 loops=5)
              Hash Cond: (br.id = ft.billing_record)
              ->  Parallel Seq Scan on billing_record br  (cost=0.00..39488.92 rows=335492 width=3598) (actual time=0.005..37.096 rows=268393 loops=5)
              ->  Hash  (cost=501594.96..501594.96 rows=1564453 width=993) (actual time=14287.210..14287.212 rows=1299656 loops=5)
                    Buckets: 524288  Batches: 8  Memory Usage: 46914kB
                    ->  Hash Join  (cost=103452.20..501594.96 rows=1564453 width=993) (actual time=3728.706..6986.704 rows=1300948 loops=5)
                          Hash Cond: (tl.id = ft.transaction_log)
                          ->  Seq Scan on transaction_log tl  (cost=0.00..33484.53 rows=1564453 width=766) (actual time=0.018..123.814 rows=1564453 loops=5)
                          ->  Hash  (cost=46549.31..46549.31 rows=1300631 width=227) (actual time=3704.309..3704.309 rows=1300948 loops=5)
                                Buckets: 2097152  Batches: 2  Memory Usage: 151889kB
                                ->  Seq Scan on finance_transaction ft  (cost=0.00..46549.31 rows=1300631 width=227) (actual time=0.018..139.211 rows=1300948 loops=5)
        ->  Hash  (cost=165645.54..165645.54 rows=4852654 width=191) (actual time=2927.466..2927.466 rows=4852654 loops=5)
              Buckets: 2097152  Batches: 4  Memory Usage: 246002kB
              ->  Seq Scan on transaction_audit_history tah  (cost=0.00..165645.54 rows=4852654 width=191) (actual time=0.025..630.852 rows=4852654 loops=5)
Planning time: 0.341 ms
Execution time: 24013.565 ms

การเปรียบเทียบประสิทธิภาพ: ก่อนและหลังการเพิ่มการตั้งค่าหน่วยความจำ

เมตริกก่อน (การตั้งค่าปริยาย)หลัง (การตั้งค่าหน่วยความจำที่ปรับแต่ง)การปรับปรุง
เวลาการดำเนินการทั้งหมด33.58s24.01s⬇ ~28.5% เร็วขึ้น (~9.5s ประหยัด)
Hash Join (ft.id = tah.finance_transaction)29.03s20.07s⬇ ~9s เร็วขึ้น
Hash Join (br.id = ft.billing_record)19.08s15.56s⬇ ~3.5s เร็วขึ้น
Hash Batches (ft.transaction_log)128 Batches2 Batches✅ ลดการใช้ดิสก์อย่างมาก
Hash Batches (transaction_audit_history)512 Batches4 Batches✅ ลดการใช้ดิสก์

การเลือกการตั้งค่าหน่วยความจำที่ถูกต้อง: วิธีการแบบกรณีต่อกรณี

การปรับแต่งการตั้งค่าหน่วยความจำ PostgreSQL ไม่ใช่โซลูชันที่ใช้ได้กับทุกกรณี. มันขึ้นอยู่กับขนาดฐานข้อมูลของคุณ ผู้ใช้พร้อมกัน และรูปแบบคำค้นหา. หาวิธีที่ถูกต้อง work_mem, shared_buffers, และ effective_cache_size.

  • ถ้า เซิร์ฟเวอร์ของคุณมี RAM ว่างมากมาย คุณสามารถจัดสรรหน่วยความจำเพิ่มเติมให้กับ PostgreSQL.
  • ถ้าคุณมี ผู้ใช้พร้อมกันจำนวนมาก การจัดสรรหน่วยความจำมากเกินไปต่อคำค้นหาอาจ ทำให้ระบบไม่เสถียร.
  • รัน EXPLAIN ANALYZE บนคำค้นหาที่ช้าเพื่อตรวจสอบ การใช้ดิสก์และความไม่ประสิทธิภาพของ hash join.

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

✅ เปลี่ยนการตั้งค่าสำหรับคำค้นหาหนักเพียงคำเดียว:

SET LOCAL work_mem = '300MB';
SELECT * FROM finance_transaction ft
INNER JOIN billing_record br ON br.id = ft.billing_record
INNER JOIN transaction_log tl ON ft.transaction_log = tl.id
INNER JOIN transaction_audit_history tah ON tah.finance_transaction = ft.id;

บทสรุป

การปรับแต่งการตั้งค่าหน่วยความจำ PostgreSQL สามารถทำให้เกิดความแตกต่างอย่างมากในประสิทธิภาพ ช่วยให้คำค้นหาของคุณทำงานได้เร็วขึ้นและลดการใช้ดิสก์ที่ไม่จำเป็น กุญแจสำคัญคือการหาสมดุลที่ถูกต้องตามภาระงานของฐานข้อมูลของคุณ RAM ที่มีอยู่ และพฤติกรรมของคำค้นหาของคุณ.

ข้อคิดสำคัญ:

  • รู้จักภาระงานของคุณ – อย่าเพียงแค่เพิ่มการตั้งค่าหน่วยความจำอย่างไม่คิดหน้าคิดหลัง; วิเคราะห์ว่าคำค้นหาของคุณทำงานอย่างไรเสียก่อน.
  • ใช้ EXPLAIN ANALYZE – สิ่งนี้จะช่วยให้คุณเห็นปัญหาต่างๆ เช่น การใช้ดิสก์และการเชื่อมโยงที่ไม่ประสิทธิภาพ.
  • ปรับแต่ง work_mem อย่างระมัดระวัง – ค่าที่สูงขึ้นสามารถทำให้การเชื่อมโยงและการเรียงลำดับเร็วขึ้น แต่การตั้งค่าสูงเกินไปอาจทำให้ RAM ของคุณหมด.
  • เปลี่ยนการตั้งค่าในระดับที่ถูกต้อง – ใช้การตั้งค่าทั่วไปเพื่อการปรับปรุงโดยรวม การตั้งค่าเซสชันสำหรับการปรับเปลี่ยนชั่วคราว และการตั้งค่าต่อคำค้นหาสำหรับการดำเนินการที่หนักจริงๆ.
  • ติดตามผลกระทบ – ตรวจสอบบันทึก ติดตามประสิทธิภาพ และปรับเปลี่ยนตามความจำเป็น.

ในท้ายที่สุด การปรับแต่ง PostgreSQL เป็น ทั้งวิทยาศาสตร์และการทดลอง เริ่มต้นจากเล็กน้อย ติดตามผลลัพธ์ และปรับเปลี่ยนไปเรื่อยๆ ด้วยการตั้งค่าที่ถูกต้อง ฐานข้อมูลของคุณจะทำงาน ได้ราบรื่น เร็วขึ้น และมีประสิทธิภาพมากขึ้น. 🚀