Hiệu suất là một trong những nhiệm vụ quan trọng nhất và phức tạp nhất khi quản lý cơ sở dữ liệu. Nó có thể bị ảnh hưởng bởi cấu hình, phần cứng hoặc thậm chí là thiết kế của hệ thống. Theo mặc định, PostgreSQL được định cấu hình có tính đến tính tương thích và ổn định, vì hiệu suất phụ thuộc rất nhiều vào phần cứng và vào chính hệ thống của chúng ta. Chúng ta có thể có một hệ thống với nhiều dữ liệu được đọc nhưng thông tin không thay đổi thường xuyên. Hoặc chúng ta có thể có một hệ thống ghi liên tục. Vì lý do này, không thể xác định cấu hình mặc định hoạt động cho tất cả các loại khối lượng công việc.
Trong blog này, chúng ta sẽ xem cách phân tích khối lượng công việc hoặc các truy vấn đang chạy. Sau đó, chúng tôi sẽ xem xét một số tham số cấu hình cơ bản để cải thiện hiệu suất của cơ sở dữ liệu PostgreSQL của chúng tôi. Như chúng tôi đã đề cập, chúng tôi sẽ chỉ thấy một số tham số. Danh sách các tham số PostgreSQL rất phong phú, chúng tôi chỉ đề cập đến một số tham số chính. Tuy nhiên, người ta luôn có thể tham khảo tài liệu chính thức để đi sâu vào các thông số và cấu hình có vẻ quan trọng hoặc hữu ích nhất trong môi trường của chúng ta.
GIẢI THÍCH
Một trong những bước đầu tiên chúng ta có thể thực hiện để hiểu cách cải thiện hiệu suất của cơ sở dữ liệu là phân tích các truy vấn được thực hiện.
PostgreSQL đưa ra một kế hoạch truy vấn cho mỗi truy vấn mà nó nhận được. Để xem kế hoạch này, chúng tôi sẽ sử dụng EXPLAIN.
Cấu trúc của một kế hoạch truy vấn là một cây gồm các nút kế hoạch. Các nút ở mức thấp hơn của cây là các nút quét. Chúng trả về các hàng thô từ một bảng. Có nhiều loại nút quét khác nhau cho các phương pháp truy cập bảng khác nhau. Đầu ra EXPLAIN có một dòng cho mỗi nút trong cây kế hoạch.
world=# EXPLAIN SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
QUERY PLAN
--------------------------------------------------------------------------
Nested Loop (cost=0.00..734.81 rows=50662 width=144)
-> Seq Scan on city t1 (cost=0.00..93.19 rows=347 width=31)
Filter: ((id > 100) AND (population > 700000))
-> Materialize (cost=0.00..8.72 rows=146 width=113)
-> Seq Scan on country t2 (cost=0.00..7.99 rows=146 width=113)
Filter: (population < 7000000)
(6 rows)
Lệnh này hiển thị cách các bảng trong truy vấn của chúng tôi sẽ được quét. Hãy xem những giá trị này tương ứng với những gì mà chúng ta có thể quan sát trong GIẢI THÍCH.
- Tham số đầu tiên hiển thị hoạt động mà công cụ đang thực hiện trên dữ liệu trong bước này.
- Chi phí khởi động ước tính. Đây là khoảng thời gian trước khi giai đoạn đầu ra có thể bắt đầu.
- Tổng chi phí ước tính. Điều này được nêu trên giả định rằng nút kế hoạch đã chạy đến khi hoàn thành. Trên thực tế, nút cha của một nút có thể ngừng đọc tất cả các hàng có sẵn.
- Số lượng hàng ước tính được xuất ra bởi nút kế hoạch này. Một lần nữa, nút được giả định là đã chạy đến khi hoàn thành.
- Chiều rộng trung bình ước tính của các hàng do nút kế hoạch này xuất ra.
Phần quan trọng nhất của màn hình là chi phí thực hiện câu lệnh ước tính, là dự đoán của người lập kế hoạch về thời gian chạy câu lệnh đó. Khi so sánh mức độ hiệu quả của một truy vấn với truy vấn kia, trên thực tế, chúng tôi sẽ so sánh các giá trị chi phí của chúng.
Điều quan trọng là phải hiểu rằng chi phí của một nút cấp trên bao gồm chi phí của tất cả các nút con của nó. Cũng cần phải nhận ra rằng chi phí chỉ phản ánh những điều mà người lập kế hoạch quan tâm. Đặc biệt, chi phí không xem xét thời gian dành để truyền các hàng kết quả cho khách hàng, có thể là một yếu tố quan trọng trong thời gian trôi qua thực tế; nhưng người lập kế hoạch bỏ qua nó vì nó không thể thay đổi nó bằng cách thay đổi kế hoạch.
Các chi phí được đo lường theo đơn vị tùy ý được xác định bởi các thông số chi phí của người lập kế hoạch. Thực hành truyền thống là đo lường chi phí theo đơn vị tìm nạp trang đĩa; nghĩa là, seq_page_cost được đặt theo quy ước là 1.0 và các thông số chi phí khác được đặt tương ứng với điều đó.
GIẢI THÍCH PHÂN TÍCH
Với tùy chọn này, EXPLAIN thực thi truy vấn, sau đó hiển thị số hàng thực và thời gian chạy thực được tích lũy trong mỗi nút kế hoạch, cùng với các ước tính giống như EXPLAIN hiển thị.
Hãy xem một ví dụ về việc sử dụng công cụ này.
world=# EXPLAIN ANALYZE SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.00..734.81 rows=50662 width=144) (actual time=0.081..22.066 rows=51100 loops=1)
-> Seq Scan on city t1 (cost=0.00..93.19 rows=347 width=31) (actual time=0.069..0.618 rows=350 loops=1)
Filter: ((id > 100) AND (population > 700000))
Rows Removed by Filter: 3729
-> Materialize (cost=0.00..8.72 rows=146 width=113) (actual time=0.000..0.011 rows=146 loops=350)
-> Seq Scan on country t2 (cost=0.00..7.99 rows=146 width=113) (actual time=0.007..0.058 rows=146 loops=1)
Filter: (population < 7000000)
Rows Removed by Filter: 93
Planning time: 0.136 ms
Execution time: 24.627 ms
(10 rows)
Nếu chúng tôi không tìm thấy lý do tại sao các truy vấn của chúng tôi mất nhiều thời gian hơn bình thường, chúng tôi có thể kiểm tra blog này để biết thêm thông tin.
VACUUM
Quy trình VACUUM chịu trách nhiệm về một số nhiệm vụ bảo trì trong cơ sở dữ liệu, một trong số đó là khôi phục bộ nhớ bị chiếm bởi các bộ dữ liệu đã chết. Trong hoạt động bình thường của PostgreSQL, các bộ giá trị bị xóa hoặc bị che khuất bởi một bản cập nhật sẽ không bị xóa khỏi bảng của chúng một cách vật lý; chúng vẫn hiện diện cho đến khi thực hiện một VACUUM. Vì vậy, cần phải làm VACUUM định kỳ, đặc biệt là trong các bảng được cập nhật thường xuyên.
Nếu VACUUM chiếm quá nhiều thời gian hoặc tài nguyên, điều đó có nghĩa là chúng tôi phải làm việc đó thường xuyên hơn để mỗi thao tác có ít công việc phải làm sạch hơn.
Trong mọi trường hợp, bạn có thể cần phải tắt VACUUM, chẳng hạn như khi tải dữ liệu với số lượng lớn.
VACUUM chỉ đơn giản là lấy lại không gian và làm cho nó có sẵn để sử dụng lại. Dạng lệnh này có thể hoạt động song song với việc đọc và ghi bình thường của bảng, vì không có khóa riêng. Tuy nhiên, không gian bổ sung không được trả lại cho hệ điều hành (trong hầu hết các trường hợp); nó chỉ có sẵn để sử dụng lại trong cùng một bảng.
VACUUM FULL ghi lại tất cả nội dung của bảng trong một tệp đĩa mới mà không có thêm dung lượng, điều này cho phép không gian chưa sử dụng quay trở lại hệ điều hành. Biểu mẫu này chậm hơn nhiều và yêu cầu một khóa riêng trên mỗi bảng trong khi xử lý.
PHÂN TÍCH VACUUM thực hiện một VACUUM và sau đó là một PHÂN TÍCH cho mỗi bảng đã chọn. Đây là một cách thực tế để kết hợp các tập lệnh bảo trì định kỳ.
ANALYZE thu thập thống kê về nội dung của các bảng trong cơ sở dữ liệu và lưu trữ kết quả trong pg_statistic. Sau đó, công cụ lập kế hoạch truy vấn sử dụng các thống kê này để giúp xác định các kế hoạch thực thi hiệu quả nhất cho các truy vấn.
Tải xuống Báo cáo chính thức hôm nay Quản lý &Tự động hóa PostgreSQL với ClusterControlTìm hiểu về những điều bạn cần biết để triển khai, giám sát, quản lý và mở rộng PostgreSQLTải xuống Báo cáo chính thứcThông số cấu hình
Để sửa đổi các tham số này, chúng ta phải chỉnh sửa tệp $ PGDATA / postgresql.conf. Chúng tôi phải lưu ý rằng một số trong số chúng yêu cầu khởi động lại cơ sở dữ liệu của chúng tôi.
max_connections
Xác định số lượng tối đa các kết nối đồng thời đến cơ sở dữ liệu của chúng tôi. Có tài nguyên bộ nhớ có thể được định cấu hình cho mỗi máy khách, do đó, số lượng máy khách tối đa có thể đề xuất lượng bộ nhớ tối đa được sử dụng.
superuser_reserved_connections
Trong trường hợp đạt đến giới hạn max_connection, những kết nối này được dành riêng cho superuser.
shared_buffers
Đặt dung lượng bộ nhớ mà máy chủ cơ sở dữ liệu sử dụng cho bộ đệm bộ nhớ được chia sẻ. Nếu bạn có một máy chủ cơ sở dữ liệu chuyên dụng với 1 GB RAM trở lên, giá trị ban đầu hợp lý cho shared_buffers là 25% bộ nhớ hệ thống của bạn. Các cấu hình lớn hơn cho shared_buffers thường yêu cầu max_wal_size tăng tương ứng, để kéo dài quá trình ghi một lượng lớn dữ liệu mới hoặc đã sửa đổi trong một khoảng thời gian dài hơn.
temp_buffers
Đặt số lượng bộ đệm tạm thời tối đa được sử dụng cho mỗi phiên. Đây là các bộ đệm phiên cục bộ chỉ được sử dụng để truy cập các bảng tạm thời. Một phiên sẽ chỉ định các bộ đệm tạm thời nếu cần đến giới hạn do bộ đệm tạm thời đưa ra.
work_mem
Chỉ định dung lượng bộ nhớ sẽ được sử dụng bởi các hoạt động bên trong của ORDER BY, DISTINCT, JOIN và bảng băm trước khi ghi vào các tệp tạm thời trên đĩa. Khi định cấu hình giá trị này, chúng ta phải tính đến việc một số phiên đang thực hiện các hoạt động này cùng một lúc và mỗi hoạt động sẽ được phép sử dụng nhiều bộ nhớ như được chỉ định bởi giá trị này trước khi nó bắt đầu ghi dữ liệu vào các tệp tạm thời.
Tùy chọn này được gọi là sort_mem trong các phiên bản cũ hơn của PostgreSQL.
Maint_work_mem
Chỉ định dung lượng bộ nhớ tối đa mà các hoạt động bảo trì sẽ sử dụng, chẳng hạn như VACUUM, CREATE INDEX và ALTER TABLE ADD FOREIGN KEY. Vì chỉ một trong số các hoạt động này có thể được thực thi đồng thời bởi một phiên và một cài đặt thường không có nhiều hoạt động trong số chúng chạy đồng thời, nó có thể lớn hơn work_mem. Các cấu hình lớn hơn có thể cải thiện hiệu suất cho VACUUM và khôi phục cơ sở dữ liệu.
Khi autovacuum được thực thi, bộ nhớ này có thể được chỉ định số lần tham số autovacuum_max_workers được định cấu hình, vì vậy chúng ta phải tính đến điều này, hoặc nếu không, hãy định cấu hình tham số autovacuum_work_mem để quản lý điều này một cách riêng biệt.
fsync
Nếu fsync được bật, PostgreSQL sẽ cố gắng đảm bảo rằng các bản cập nhật được ghi vật lý vào đĩa. Điều này đảm bảo rằng cụm cơ sở dữ liệu có thể được phục hồi về trạng thái nhất quán sau sự cố hệ điều hành hoặc phần cứng.
Mặc dù vô hiệu hóa fsync thường cải thiện hiệu suất, nhưng nó có thể gây mất dữ liệu trong trường hợp mất điện hoặc sự cố hệ thống. Do đó, chỉ nên hủy kích hoạt fsync nếu bạn có thể dễ dàng tạo lại toàn bộ cơ sở dữ liệu của mình từ dữ liệu bên ngoài.
checkpoint_segment (PostgreSQL <9.5)
Số lượng phân đoạn tệp bản ghi tối đa giữa các điểm điều khiển WAL tự động (mỗi phân đoạn thường là 16 megabyte). Việc tăng thông số này có thể làm tăng lượng thời gian cần thiết để khôi phục lỗi. Trong một hệ thống có nhiều lưu lượng, nó có thể ảnh hưởng đến hiệu suất nếu nó được đặt ở giá trị rất thấp. Bạn nên tăng giá trị của checkpoint_segment trên các hệ thống có nhiều sửa đổi dữ liệu.
Ngoài ra, một phương pháp hay là lưu các tệp WAL trên đĩa không phải là PGDATA. Điều này hữu ích cho cả việc cân bằng việc viết và bảo mật trong trường hợp lỗi phần cứng.
Kể từ PostgreSQL 9.5, biến cấu hình "checkpoint_searies" đã bị xóa và được thay thế bằng "max_wal_size" và "min_wal_size"
max_wal_size (PostgreSQL> =9.5)
Kích thước tối đa mà WAL được phép phát triển giữa các điểm kiểm soát. Kích thước của WAL có thể vượt quá max_wal_size trong các trường hợp đặc biệt. Việc tăng thông số này có thể làm tăng lượng thời gian cần thiết để khôi phục lỗi.
min_wal_size (PostgreSQL> =9.5)
Khi tệp WAL được giữ dưới giá trị này, nó sẽ được tái chế để sử dụng trong tương lai tại một trạm kiểm soát, thay vì bị xóa. Điều này có thể được sử dụng để đảm bảo dành đủ không gian WAL để xử lý các xung đột biến trong việc sử dụng WAL, chẳng hạn như khi thực hiện các công việc hàng loạt lớn.
wal_sync_method
Phương pháp được sử dụng để buộc cập nhật WAL vào đĩa. Nếu fsync bị tắt, cài đặt này không có hiệu lực.
wal_buffers
Dung lượng bộ nhớ dùng chung cho dữ liệu WAL chưa được ghi vào đĩa. Cài đặt mặc định là khoảng 3% bộ đệm chia sẻ, không nhỏ hơn 64KB hoặc hơn kích thước của một đoạn WAL (thường là 16MB). Đặt giá trị này thành ít nhất một vài MB có thể cải thiện hiệu suất ghi trên máy chủ có nhiều giao dịch đồng thời.
effect_cache_size
Giá trị này được người lập kế hoạch truy vấn sử dụng để tính đến các kế hoạch có thể có hoặc có thể không vừa trong bộ nhớ. Điều này được tính đến trong ước tính chi phí của việc sử dụng một chỉ số; giá trị cao làm cho khả năng quét chỉ mục được sử dụng nhiều hơn và giá trị thấp làm cho khả năng quét tuần tự sẽ được sử dụng nhiều hơn. Giá trị hợp lý sẽ là 50% RAM.
default_stosystem_target
PostgreSQL thu thập số liệu thống kê từ mỗi bảng trong cơ sở dữ liệu của nó để quyết định cách các truy vấn sẽ được thực thi trên chúng. Theo mặc định, nó không thu thập quá nhiều thông tin và nếu bạn không nhận được kế hoạch thực thi tốt, bạn nên tăng giá trị này và sau đó chạy lại ANALYZE trong cơ sở dữ liệu (hoặc đợi AUTOVACUUM).
sync_commit
Chỉ định liệu cam kết giao dịch có đợi các bản ghi WAL được ghi vào đĩa hay không trước khi lệnh trả về chỉ báo "thành công" cho máy khách. Các giá trị có thể có là:"on", "remote_apply", "remote_write", "local" và "off". Cài đặt mặc định là "bật". Khi nó bị vô hiệu hóa, có thể có độ trễ giữa thời gian khách hàng quay lại và khi giao dịch được đảm bảo an toàn trước khóa máy chủ. Không giống như fsync, việc vô hiệu hóa tham số này không tạo ra bất kỳ rủi ro nào về sự không nhất quán của cơ sở dữ liệu:sự cố của hệ điều hành hoặc cơ sở dữ liệu có thể dẫn đến việc mất một số giao dịch gần đây được cho là đã cam kết, nhưng trạng thái của cơ sở dữ liệu sẽ giống hệt như nếu các giao dịch đó đã bị hủy bỏ sạch sẽ. Do đó, hủy kích hoạt sync_commit có thể là một giải pháp thay thế hữu ích khi hiệu suất quan trọng hơn sự chắc chắn chính xác về độ bền của giao dịch.
Ghi nhật ký
Có một số loại dữ liệu để ghi có thể hữu ích hoặc không. Hãy xem một số trong số chúng:
- log_min_error_statement:Đặt mức ghi nhật ký tối thiểu.
- log_min_duration_statement:Được sử dụng để ghi lại các truy vấn chậm trong hệ thống.
- log_line_prefix:Tuân theo thông tin ở đầu mỗi dòng nhật ký.
- log_statement:Bạn có thể chọn giữa KHÔNG, DDL, MOD, TẤT CẢ. Việc sử dụng "tất cả" có thể gây ra các vấn đề về hiệu suất.
Thiết kế
Trong nhiều trường hợp, thiết kế cơ sở dữ liệu của chúng tôi có thể ảnh hưởng đến hiệu suất. Chúng tôi phải cẩn thận trong thiết kế của mình, chuẩn hóa lược đồ của chúng tôi và tránh dữ liệu dư thừa. Trong nhiều trường hợp, có nhiều bàn nhỏ thay vì một bàn lớn sẽ rất tiện lợi. Nhưng như chúng tôi đã nói trước đây, mọi thứ phụ thuộc vào hệ thống của chúng tôi và không có một giải pháp khả thi.
Chúng ta cũng phải sử dụng các chỉ mục một cách có trách nhiệm. Chúng ta không nên tạo chỉ mục cho từng trường hoặc kết hợp các trường, vì mặc dù chúng ta không phải di chuyển toàn bộ bảng, nhưng chúng ta đang sử dụng không gian đĩa và thêm chi phí để ghi các thao tác.
Một công cụ rất hữu ích khác là quản lý nhóm kết nối. Nếu chúng ta có một hệ thống có nhiều tải, chúng ta có thể sử dụng điều này để tránh bão hòa các kết nối trong cơ sở dữ liệu và có thể sử dụng lại chúng.
Phần cứng
Như chúng tôi đã đề cập ở phần đầu của blog này, phần cứng là một trong những yếu tố quan trọng ảnh hưởng trực tiếp đến hiệu suất của cơ sở dữ liệu của chúng tôi. Hãy xem một số điểm cần lưu ý.
- Bộ nhớ:Chúng ta càng có nhiều RAM, chúng ta càng có thể xử lý nhiều dữ liệu bộ nhớ hơn và điều đó có nghĩa là hiệu suất tốt hơn. Tốc độ ghi và đọc trên đĩa chậm hơn nhiều so với tốc độ trong bộ nhớ, do đó, chúng ta có càng nhiều thông tin trong bộ nhớ, chúng ta sẽ có hiệu suất tốt hơn.
- CPU:Có thể không có ý nghĩa gì khi nói điều này, nhưng chúng ta càng có nhiều CPU thì càng tốt. Trong mọi trường hợp, điều đó không phải là quan trọng nhất về phần cứng, nhưng nếu chúng ta có một CPU tốt, khả năng xử lý của chúng ta sẽ được cải thiện và điều đó ảnh hưởng trực tiếp đến cơ sở dữ liệu của chúng ta.
- Đĩa cứng:Chúng tôi có một số loại đĩa có thể sử dụng, SCSI, SATA, SAS, IDE. Chúng tôi cũng có đĩa trạng thái rắn. Chúng ta phải so sánh chất lượng / giá cả, mà chúng ta nên sử dụng để so sánh tốc độ của nó. Nhưng loại đĩa không phải là thứ duy nhất cần xem xét, chúng ta còn phải xem cách cấu hình chúng. Nếu chúng ta muốn có hiệu suất tốt, chúng ta có thể sử dụng RAID10, giữ WAL trên một đĩa khác bên ngoài RAID. Bạn không nên sử dụng RAID5 vì hiệu suất của loại RAID này cho cơ sở dữ liệu không tốt.
Kết luận
Sau khi tính đến các điểm được đề cập trong blog này, chúng tôi có thể thực hiện một điểm chuẩn để xác minh hoạt động của cơ sở dữ liệu.
Điều quan trọng là phải giám sát cơ sở dữ liệu của chúng tôi để xác định xem chúng tôi có đang gặp phải vấn đề về hiệu suất hay không và có thể giải quyết nó càng sớm càng tốt. Đối với tác vụ này, có một số công cụ như Nagios, ClusterControl hoặc Zabbix, trong số những công cụ khác, cho phép chúng tôi không chỉ theo dõi mà với một số công cụ trong số chúng, cho phép chúng tôi chủ động hành động trước khi sự cố xảy ra. Với ClusterControl, ngoài việc giám sát, quản trị và một số tiện ích khác, chúng tôi có thể nhận được các khuyến nghị về những hành động mà chúng tôi có thể thực hiện khi nhận được cảnh báo về hiệu suất. Điều này cho phép chúng tôi có ý tưởng về cách giải quyết các vấn đề tiềm ẩn.
Blog này không nhằm mục đích hướng dẫn đầy đủ về cách cải thiện hiệu suất cơ sở dữ liệu. Hy vọng rằng nó cung cấp một bức tranh rõ ràng hơn về những thứ có thể trở nên quan trọng và một số thông số cơ bản có thể được cấu hình. Đừng ngần ngại cho chúng tôi biết nếu chúng tôi đã bỏ lỡ bất kỳ điều quan trọng nào.