PostgreSQL: Không còn VACUUM, không còn Bloat
(orioledata.com)- OrioleDB, một engine lưu trữ mới cho PostgreSQL, loại bỏ quy trình VACUUM vốn tiêu tốn nhiều tài nguyên và giải quyết vấn đề Bloat của bảng
- PostgreSQL hiện tại cần vacuum thủ công và autovacuum do cấu trúc MVCC, và cả hai cách đều tiêu tốn đáng kể tài nguyên hệ thống
- OrioleDB xử lý thay đổi dữ liệu hiệu quả và hợp nhất dữ liệu bị phân mảnh thông qua undo log ở mức hàng·khối và tự động gộp trang
- Trong benchmark tổng hợp, nhờ chỉ cập nhật một chỉ mục, tự động gộp các trang thưa và áp dụng WAL ở mức hàng, hệ thống đạt TPS cao hơn 5 lần và tiết kiệm tài nguyên
- Giải quyết bài toán lâu năm là VACUUM để giảm gánh nặng bảo trì và mang lại cải thiện về hiệu năng·trải nghiệm người dùng cho PostgreSQL
Lịch sử và giới hạn của PostgreSQL VACUUM
- Quy trình VACUUM là di sản lịch sử bắt nguồn từ khái niệm infinite time-travel của dự án Berkley Postgres, và khái niệm này sau đó đã bị cộng đồng loại bỏ
- Tuy nhiên, điều đó đã dẫn tới việc triển khai hệ thống MVCC(Multi-Version Concurrency Control) dễ bị Bloat bảng
- MVCC có lợi cho xử lý giao dịch đồng thời, nhưng lại kéo theo nhu cầu vacuum thủ công để dọn dẹp dữ liệu cũ và không cần thiết
- Vacuum thủ công là công việc tốn nhiều công sức và là nguyên nhân tiềm ẩn gây kém hiệu quả hệ thống
- Sau đó, autovacuum tự động được đưa vào để giảm gánh nặng thao tác thủ công
- Đây là một bước tiến lớn, nhưng không phải giải pháp hoàn hảo, và dù tự động thì vẫn tiêu tốn khá nhiều tài nguyên hệ thống
- Đây là một trong những lý do Uber chuyển từ PostgreSQL sang MySQL, đồng thời cũng được nhắc tới như một trong 10 điểm bất mãn về PostgreSQL mà Richard Branson nêu ra
- Việc đưa vào Heap-Only Tuples(HOT) updates và microvacuum đã làm giảm nhu cầu vacuum toàn bộ bảng
- Dù vậy, VACUUM vẫn là tác vụ ngốn nhiều tài nguyên và các bảng vẫn dễ bị Bloat
- Điều này được nhắc tới như phần PostgreSQL mà đội ngũ OtterTune ghét nhất
- Bất chấp các giới hạn đó, nhiều tổ chức và nhà phát triển vẫn tiếp tục dùng PostgreSQL nhờ độ ổn định, khả năng mở rộng và cộng đồng mạnh
- OtterTune thừa nhận vấn đề nhưng vẫn quyết định tiếp tục sử dụng PostgreSQL
Các tính năng cốt lõi của OrioleDB
- OrioleDB là engine PostgreSQL mới được phát triển với mục tiêu chính là bảo vệ bảng khỏi Bloat và loại bỏ nhu cầu bảo trì định kỳ như VACUUM
- Điều này được thực hiện bằng undo log ở mức hàng·mức khối và tự động gộp trang
- Undo log ở mức hàng·khối cung cấp khả năng kiểm soát chi tiết hơn để xử lý thay đổi dữ liệu hiệu quả
- Tính năng tự động gộp trang liên tục hợp nhất dữ liệu bị phân mảnh trong nền
- Cách hoạt động của từng kỹ thuật
- Undo log mức hàng cho phép cập nhật in-place
- Undo log mức khối loại bỏ khỏi kho lưu trữ chính các tuple đã bị xóa nhưng vẫn còn hiển thị với một số giao dịch, nhờ đó tạo không gian cho tuple mới
- Tự động gộp các trang thưa giúp bảo vệ bảng và chỉ mục khỏi Bloat ngay cả sau nhiều lần xóa
- Kết quả là giảm can thiệp thủ công, giảm tiêu thụ tài nguyên và giảm mức độ dễ bị Bloat của bảng
Benchmark
- Benchmark tổng hợp được cấu hình bằng script khởi tạo tạo 1 bảng và 5 chỉ mục
- Tạo bảng
testvới khóa chínhidvà các cộtvalue1~value4(float8),ts(timestamp) - Tạo chỉ mục lần lượt trên
value1,value2,value3,value4,ts
- Tạo bảng
- Script pgbench có dạng upsert với cập nhật thưa trên một chỉ mục khi xảy ra xung đột
- Thực hiện INSERT với
idngẫu nhiên trong phạm vi 1~10,000,000, và khi xung đột thì chỉ cập nhậtts - Kiểu cập nhật thưa này gây ra Bloat chỉ mục trong bảng PostgreSQL heap thông thường
- Thực hiện INSERT với
- Benchmark này cho thấy các ưu điểm trong thiết kế của OrioleDB
- Nhờ undo log và cập nhật in-place, OrioleDB chỉ cập nhật một chỉ mục có giá trị thay đổi, trong khi engine heap của PostgreSQL khiến việc cập nhật một trường chỉ mục duy nhất vô hiệu hóa HOT, dẫn tới mọi chỉ mục đều bị cập nhật
- Tự động gộp trang giúp bảo vệ chỉ mục thưa khỏi Bloat, và các trang thưa sẽ được gộp tự động
- WAL ở mức hàng chiếm ít không gian hơn rất nhiều so với WAL ở mức khối, giúp giảm IOPS khi ghi WAL
- Các con số mà OrioleDB mang lại từ tổng hợp các cải tiến
- TPS cao hơn 5 lần trên mỗi giao dịch
- Tải CPU thấp hơn 2.3 lần trên mỗi giao dịch
- IOPS thấp hơn 22 lần trên mỗi giao dịch
- Không có Bloat ở bảng và chỉ mục
Ý nghĩa của OrioleDB
- Với OrioleDB, cộng đồng PostgreSQL bước vào một thời kỳ mới nơi VACUUM trở thành chuyện của quá khứ
- Giải pháp này đưa ra câu trả lời cho một trong những bài toán lâu đời nhất của PostgreSQL, đồng thời mang lại hiệu quả cao hơn và giảm gánh nặng bảo trì cho người dùng
1 bình luận
Ý kiến trên Hacker News
Dù vậy vẫn có vài điểm khiến tôi băn khoăn. Xử lý theo từng hàng tạo ra vấn đề căn chỉnh khi ghi và căn chỉnh fsync, còn trang thì dễ căn chỉnh hơn so với các hàng có kích thước tùy ý
PostgreSQL rất, thậm chí có thể nói là cực kỳ, bảo thủ về an toàn dữ liệu; điều này chủ yếu đạt được bằng fsync đúng thời điểm, và tác động của nó lan ra toàn bộ stack I/O, bao gồm cả firmware SSD, khiến hệ thống chậm đi
MVCC rất tốt cho truy cập đồng thời, nhưng tài liệu Oriole không nói các biểu đồ được tạo ra trong điều kiện đồng thời nào
Tiêu đề và phần mở đầu của tài liệu Oriole tập trung vào giải quyết VACUUM, nhưng dường như chưa chứng minh rằng biểu đồ “sóng vuông” thấy trong PostgreSQL thực sự phần lớn là do VACUUM. Một benchmark khác của Percona (https://www.percona.com/blog/evaluating-checkpointing-in-postgresql/) không cho thấy mẫu sóng vuông rõ rệt như vậy
Chắc các tác giả cũng biết những vấn đề này, nên có thể họ sẽ viết một phần tổng quan về cách họ tiếp cận
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
Vì PostgreSQL bảo thủ về an toàn dữ liệu, mục tiêu đầu tiên là trở thành một extension thuần túy. Để trở thành một phần của PostgreSQL thì cần được thời gian kiểm chứng
Đúng là thiếu thông tin về concurrency, và chúng tôi đã thêm loại VM cùng thông tin concurrency vào bài blog
Mẫu sóng vuông đúng là do checkpoint gây ra. Nguyên nhân cải thiện ở đây thật ra không phải là VACUUM, mà là cách chỉ sửa các index liên quan, cùng với WAL theo từng hàng giúp giảm tổng I/O
Trông rất hay, nhưng tôi tò mò họ định theo kịp chu kỳ phát hành của dự án mẹ như thế nào
Họ đã fork một dự án mã nguồn mở lớn rồi đặt lên đó những thay đổi đáng kể; nếu họ không cho rằng các thay đổi đó sẽ được upstream chấp nhận, tôi không rõ đây có phải ý tưởng hay không
Cần làm rõ đây là một món đồ chơi làm cho vui, hay là một dự án nghiêm túc sẽ được duy trì về sau
Nếu là dự án nghiêm túc, họ nên nêu rõ kế hoạch để nó không trở thành phần mềm bị bỏ rơi trong tương lai, kế hoạch hấp thụ các bản phát hành tương lai của dự án gốc, hoặc kế hoạch tách hẳn thành một dự án riêng
Vì là extension nên có vẻ vẫn có thể nhận các thay đổi upstream của PostgreSQL, nhưng nếu chuyện đó dễ thì ngay từ đầu nó đã trở thành một phần của dự án PostgreSQL rồi, nên có vẻ không đơn giản
“Đúng vậy. Nhưng vẫn còn một chặng đường dài. Hiện OrioleDB là một extension được cung cấp cùng với patch cho PostgreSQL core. Mục tiêu trung hạn là biến OrioleDB thành một extension thuần túy, còn mục tiêu dài hạn là đưa OrioleDB trở thành một phần của PostgreSQL core”
Lần sau có lẽ tốt hơn là đừng vào cuộc gay gắt như vậy ngay từ đầu
https://news.ycombinator.com/item?id=36742001
Chỉ riêng việc thấy một ví dụ đã sửa được cũng chắc chắn là hữu ích. Nếu là công ty có nguồn lực để duy trì một fork của Postgres thì tốt; nếu không, họ có thể đánh giá liệu khó khăn khi dùng fork này có đáng so với lợi ích hiệu năng hay không
Mong là nên thôi kiểu nói “Uber đã chuyển từ Postgres sang MySQL”. Uber thực chất đã chuyển từ Postgres dùng như cơ sở dữ liệu quan hệ sang một cơ sở dữ liệu phi quan hệ gần như tự xây dựng, dùng MySQL như kho khóa-giá trị phân tán, nên đây không phải tình huống có thể áp dụng nguyên xi cho phần lớn người dùng Postgres
Dù sao thì thiết kế MVCC kiểu chuyển dữ liệu cũ sang undo log/segment như vậy cũng được dùng trong Oracle DB, nên bản thân cách hoạt động đã được kiểm chứng
Khó khăn chung là tốc độ đọc các phiên bản dữ liệu cũ sẽ chậm hơn. Phải tìm trong log, và đôi khi dữ liệu bị xóa khỏi log trước khi giao dịch kết thúc, gây ra lỗi Snapshot Too Old khét tiếng
Tôi không thấy bài viết nói khi nào các hàng bị xóa khỏi undo log. Nếu chúng bị xóa khi không còn cần nữa thì có lẽ vẫn cần lượng công việc quản lý tương tự, nên không rõ cải thiện đến từ đâu; còn nếu là một circular buffer có thể cạn dung lượng như Oracle thì dưới tải ghi cao, các giao dịch chạy lâu sẽ bắt đầu thất bại, khá khó chịu
Còn tùy “công việc quản lý” chính xác nghĩa là gì. Nếu xét khối lượng công việc, các bản ghi undo cũ có thể được xóa hàng loạt với chi phí rất thấp, kiểu unlink file, nên có cải thiện. Không cần quét VACUUM
Nếu xét mức dùng dung lượng, đúng là cùng số phiên bản sẽ chiếm cùng lượng không gian. Nhưng đặt các phiên bản hàng cũ trong một kho riêng có thể ngăn sự xuống cấp dài hạn của kho chính. OrioleDB cũng triển khai tự động gộp các trang thưa
OrioleDB triển khai circular buffer trong bộ nhớ cho undo log. Nếu circular buffer không chứa hết mọi bản ghi undo, nó sẽ đẩy các bản ghi cũ nhất ra storage. Hiện tại không đặt giới hạn kích thước undo log, và giữ bản ghi chừng nào còn có giao dịch nào có thể cần đến, nên không có lỗi “Snapshot Too Old”
Tuy nhiên có thể cân nhắc triển khai lỗi kiểu Oracle như một tùy chọn để có thể giới hạn kích thước undo. Cũng nên xem tài liệu kiến trúc trên GitHub
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
Tất nhiên MySQL tránh VACUUM bằng cách nhượng bộ khá nhiều về mặt concurrency
Tiêu đề thì làm tôi kỳ vọng, nhưng ấn tượng đầu tiên lại khá bức bối. README chính trên GitHub[1] có cảm giác như lời quảng bá kiểu doanh nghiệp
Những gì tôi biết được cho đến giờ chỉ là OrioleDB là một storage engine mới cho PostgreSQL, PostgreSQL là cơ sở dữ liệu “được yêu thích nhất”, OrioleDB là một extension được xây trên các extension khác, và OrioleDB mở ra cánh cửa lên cloud
Sau khi các trò lừa tiền mã hóa và Web 3.0 đã đi qua, tôi tưởng khi công bố một dự án mở rộng và cải thiện thứ quan trọng như PostgreSQL thì người ta sẽ tránh cách tiếp cận như thế
[1] https://github.com/orioledb/orioledb
Tôi nghĩ khó viết rõ hơn thế. Table access method là tính năng được đưa vào PostgreSQL để hỗ trợ các cơ chế lưu trữ thay thế như zheap hoặc storage dạng cột
Việc nhắc đến điều này là quan trọng. Có khá nhiều fork PostgreSQL gắn thêm hệ thống lưu trữ dữ liệu thay thế, còn OrioleDB được thiết kế để hoạt động như một extension trên PostgreSQL không fork. Hiện chưa đạt đến mức đó, nhưng định hướng là vậy
Nếu quen với PostgreSQL thì README trông khá rõ ràng
Thiết kế của Oriole có vẻ đòi hỏi các chỉ mục nhận biết giao dịch và loại bỏ entry theo điểm, và việc này có chi phí riêng
Ví dụ, thứ tương đương GiST cho spatial index có lẽ sẽ khó bảo trì vì đặc tính không thể biết chính xác vị trí của từng index tuple, còn GIN cho full-text search indexing có thể phình rất lớn do khả năng nén posting tree kém đi
Ngoài ra, trong tình huống dùng index-organized table, tôi khó hình dung sẽ triển khai thứ tương đương BRIN thế nào. BRIN cho phép nhanh chóng loại trừ các đoạn lớn của bảng vật lý khỏi kết quả truy vấn nếu không có dữ liệu đáng quan tâm. Có thể partition theo phạm vi khóa chính, nhưng mật độ giá trị của khóa chính có thể thay đổi rất nhiều theo thời gian và theo khoảng giá trị
Tôi tò mò họ định triển khai các phương thức chỉ mục phức tạp hơn nhưng rất hữu ích này ra sao
Cũng có vấn đề có thể phát sinh khi quy tắc sắp xếp, tức collation, thay đổi. Heap và VACUUM của Postgres không biết thứ tự sắp xếp, nên trong nhiều trường hợp có thể sửa hỏng bằng cách xóa rồi chèn lại các hàng nằm sai vị trí sau khi đổi collation, và cuối cùng VACUUM sẽ dọn các tuple bị hỏng
Với Oriole, có vẻ khó làm vậy vì không thể tìm tuple gốc cần loại bỏ bằng point lookup, và để sửa các trường hợp hỏng chỉ mục đã biết có khả năng phải rebuild toàn bộ index, khiến gánh nặng bảo trì có vẻ khá lớn
Với chức năng tương tự GIN, vẫn có thể nén posting list. Một lựa chọn khả dĩ là gắn undo record với toàn bộ posting list, chứ không phải với một mục posting list riêng lẻ
Vì dùng index-organized table nên có lẽ khó tạo một đối ứng trực tiếp của BRIN. Tuy vậy có thể làm những điều thú vị với union key trong các trang nội bộ của khóa chính
Vấn đề collation là đúng và nghiêm trọng. Trước khi GA, có lẽ phải cố định mọi chỉ mục có nhận biết collation vào một phiên bản collation libicu cụ thể
Lập luận của bài viết khá thuyết phục và benchmark dường như cũng hậu thuẫn cho các tuyên bố về hiệu năng, nhưng tôi chưa hiểu rõ sự phân biệt giữa storage engine mới được đề xuất, tức OrioleDB, và bản thân PostgreSQL
Ngoài động cơ thương mại và lý do muốn kiếm tiền từ những đổi mới được đề cập trong bài, có lý do nào khiến cải tiến này phải được marketing như một cơ sở dữ liệu mới tên OrioleDB thay vì đóng góp ngược lên upstream không?
Nhưng các thay đổi của OrioleDB quá lớn để đưa vào một cách tiệm tiến. Vì vậy hiện đang so sánh OrioleDB với engine PostgreSQL hiện tại, bao gồm không chỉ heap mà cả nhiều subsystem khác
Để biết chi tiết, xem đặc biệt các slide 9–11 tại https://www.socallinuxexpo.org/sites/default/files/presentations/solving-postgres-wicked-problems.pdf
Nếu là một engine extension thì tôi tò mò liệu nó có ảnh hưởng gì khi dùng cùng các extension khác không. Ví dụ timescaledb[0] hoạt động trên các bảng con
Tôi tò mò cách làm như thế này sẽ có hiệu quả ra sao
create table xyz(...) using orioledb;select create_hypertable(xyz, ts);[0] https://github.com/timescale/timescaledb
Liệu OrioleDB có quan tâm đến việc cam kết một định dạng on-disk ổn định để không còn cần quy trình nâng cấp giữa các major version của Postgres không? Có vẻ đây là một cơ hội để giải quyết vấn đề đó
Những thứ như thêm type mới và các hàm hỗ trợ của chúng phải được chèn vào thông qua một quy trình nâng cấp nào đó. Cũng có những thay đổi catalog khác làm thay đổi bố cục cột của các bảng catalog; trong trường hợp này cũng cần một quy trình để cập nhật dữ liệu đã lưu giữa các phiên bản
Nếu không có quy trình nâng cấp thì không thể thay đổi catalog. Vì vậy chỉ các bản nâng cấp minor version của PostgreSQL mới có thể thực hiện chỉ bằng cách thay binary, và có thể rollback an toàn mà không gặp vấn đề
Nếu việc nâng cấp chỉ bị giới hạn ở các thay đổi API nội bộ, planner và executor thì quá trình phát triển sẽ bị hạn chế nghiêm trọng. Tôi nghĩ OrioleDB khó có thể loại bỏ nhu cầu về quy trình nâng cấp này
Phần lớn công việc nâng cấp cluster đến từ việc ghi lại các bảng catalog. Hơn nữa với đa số workload, pg_upgrade rất nhanh, nên tôi cũng không rõ lợi ích lớn sẽ xuất hiện ở đâu
Tôi thấy hơi băn khoăn với cách diễn đạt “giảm 2,3 lần overhead CPU trên mỗi transaction”. Postgres có mức sử dụng CPU dao động từ 5% đến 65%, còn Oriole thì luôn ở mức 90%
Khả năng dự đoán được là tốt, nhưng kéo cả vùng thấp lên thêm tới 85% thì chẳng phải cũng khá đáng lo sao?
Tin tốt là nếu scale-up theo chiều dọc bằng CPU mạnh hơn, bạn sẽ nhận được nhiều hiệu năng hơn từ Oriole. Ngược lại, Postgres sẽ không tiếp tục tăng hiệu năng theo cùng cách đó
Nếu nhìn từ góc độ hệ điều hành desktop thì có thể dùng thời gian nhàn rỗi của server Postgres vào việc khác, nhưng trên server thì thường người ta muốn máy làm một việc và được tối ưu cho việc đó
Nếu hạ TPS xuống thì CPU cũng sẽ giảm theo tỷ lệ, và ở đây có vẻ họ muốn cho thấy nó có thể lên cao đến mức nào
Nếu giới hạn hệ thống ở CPU 60% thì các con số tổng thể có thể khác, nhưng nếu ở cùng mức sử dụng mà TPS gấp 1,8 lần thì dù sao đó cũng là một chiến thắng. Trông giống một con số khá tốt hơn là chiêu trò marketing
CPU server đắt tiền có giá X đô la mỗi đơn vị, mà bạn chỉ dùng được 60% và thực tế chỉ có thể dùng đến mức đó, thì coi như đang đốt 0,4X đô la mỗi đơn vị
Nếu có thể đưa workload theo chiều dọc để làm bão hòa tới 90% một máy, thì nhìn chung sẽ dễ áp dụng QoS và các kỹ thuật cô lập để duy trì mức bão hòa thấp hơn cùng hiệu năng theo tỷ lệ. Chiều ngược lại thì không đúng. Nếu bạn chỉ dùng được 60% độ bão hòa của toàn máy và phải scale-out, thì để vượt quá 90% bạn cần thiết kế lại, và đó chính là lần thiết kế lại đang xảy ra ở đây
Họ nói rằng kết quả tích lũy của các cải tiến là OrioleDB cung cấp TPS cao hơn 5 lần, tải CPU trên mỗi transaction thấp hơn 2,3 lần, IOPS trên mỗi transaction thấp hơn 22 lần, và không có hiện tượng phình to bảng lẫn index
Việc tải CPU lên xuống không phải là Postgres đang “scale”, mà là nó định kỳ vướng vào nghẽn cổ chai hiệu năng. Có lẽ là do cần chạy VACUUM, việc này rất nhạy với I/O
Vì vậy Postgres dùng I/O cho tác vụ dọn dẹp thay vì xử lý query, khiến TPS và mức sử dụng CPU cùng tụt mạnh
Trong khi đó, Oriole quản lý throughput cao hơn nhiều một cách nhất quán hơn nhiều. Khi đạp ga, bạn thích chiếc xe đều đều chạy 100mph, hay chiếc xe dù đã đạp sát sàn vẫn dao động dữ dội giữa 40–70mph?
Trong bài có một link với tiêu đề khá thú vị là “10 điều Richard Branson ghét ở PostgreSQL”. Hóa ra người viết blog đó không phải Richard mà là Rick Branson
À, hóa ra không phải người đó