4 điểm bởi GN⁺ 2024-10-21 | 1 bình luận | Chia sẻ qua WhatsApp
  • PostgreSQL gần đây nổi lên như lựa chọn mặc định trên Internet, nhưng theo kinh nghiệm của Carnegie Mellon và OtterTune, cách triển khai MVCC của nó tạo ra gánh nặng vận hành lớn hơn MySQL, Oracle và Microsoft SQL Server
  • Khi cập nhật một hàng, thiết kế cốt lõi là cơ chế lưu trữ append-only tạo một phiên bản vật lý mới thay vì ghi đè hàng hiện có, cùng chuỗi O2N đi từ phiên bản cũ sang phiên bản mới
  • Cấu trúc này dẫn đến việc sao chép toàn bộ tuple, tích tụ dead tuple, cập nhật mọi chỉ mục và phụ thuộc vào autovacuum; chỉ khi có thể cập nhật HOT mới tránh được một phần chi phí
  • Trong phân tích PostgreSQL của khách hàng OtterTune, trung bình chỉ khoảng 46% lượt cập nhật dùng được tối ưu hóa HOT, và cấu hình autovacuum mặc định có thể trì hoãn việc dọn dẹp rất lâu trên các bảng lớn
  • PostgreSQL vẫn là một DBMS hấp dẫn, nhưng với workload ghi nhiều và bảng lớn, bạn phải tự quản lý dung lượng lưu trữ, I/O, bộ nhớ, bảo trì chỉ mục và vận hành vacuum

Vì sao PostgreSQL MVCC trở thành vấn đề

  • Có rất nhiều lựa chọn cơ sở dữ liệu, đến mức tính đến tháng 4/2023 DBDB đã ghi nhận 897 hệ thống, nhưng ở từng thời kỳ vẫn có một lựa chọn mặc định trên thực tế
    • Những năm 2000, MySQL, được Google và Facebook sử dụng, là lựa chọn theo thông lệ
    • Những năm 2010, MongoDB được chú ý cùng hình ảnh “webscale”
    • Trong 5 năm gần đây, PostgreSQL được ưa chuộng rộng rãi nhờ độ ổn định, bộ tính năng phong phú, khả năng mở rộng và phù hợp với workload vận hành
  • Trọng tâm của bài viết là cách PostgreSQL triển khai kiểm soát đồng thời đa phiên bản (MVCC)
    • Theo nghiên cứu của Carnegie Mellon và kinh nghiệm tối ưu Amazon RDS PostgreSQL, cách triển khai MVCC của PostgreSQL bị đánh giá là kém hơn MySQL, Oracle và Microsoft SQL Server
    • Amazon Aurora PostgreSQL cũng có cùng vấn đề mang tính cấu trúc

Mục đích của MVCC và lựa chọn thiết kế của PostgreSQL

  • MVCC là phương thức cho phép nhiều truy vấn đọc và ghi cơ sở dữ liệu đồng thời, nếu có thể, mà không cản trở lẫn nhau
    • DBMS không ghi đè hàng hiện có mà duy trì nhiều phiên bản vật lý cho mỗi hàng logic
    • Truy vấn đọc phiên bản phù hợp với nó dựa trên thứ tự phiên bản, chẳng hạn thời điểm tạo
    • Nhờ đó có thể có snapshot isolation, tức nhìn thấy snapshot của cơ sở dữ liệu tại thời điểm bắt đầu giao dịch
  • Các thao tác đọc có thể giảm tình huống bị chặn bởi lock bản ghi tường minh do thao tác ghi đang cập nhật cùng một mục
  • Một DBMS MVCC phải quyết định ba điều lớn
    • Cách lưu các cập nhật của hàng hiện có
    • Cách tìm phiên bản hàng phù hợp với truy vấn tại thời điểm thực thi
    • Cách loại bỏ các phiên bản đã hết hạn không còn hiển thị nữa
  • Do lựa chọn đầu tiên được PostgreSQL đưa ra từ thập niên 1980, hai lĩnh vực còn lại đến nay vẫn phải gánh chịu hệ quả

Lưu trữ phiên bản kiểu append-only

  • PostgreSQL được thiết kế ngay từ đầu để hỗ trợ đa phiên bản; khi cập nhật, nó không ghi đè hàng hiện có mà tạo bản sao rồi áp dụng thay đổi lên phiên bản mới
  • Có thể xem cách này là lưu trữ phiên bản kiểu append-only
    • Khi cập nhật một tuple hiện có, DBMS tìm một slot trống trong bảng cho phiên bản hàng mới
    • Sao chép nội dung của hàng phiên bản hiện tại sang phiên bản mới
    • Áp dụng thay đổi vào slot phiên bản vừa được cấp phát
  • Bảng ví dụ là bảng movies chứa thông tin phim
    • id là khóa chính và có các cột name, year, director
    • Có chỉ mục chính movies_pkey và các chỉ mục B+Tree phụ idx_name, idx_director
  • Trong cập nhật đổi năm phát hành của "Shaolin and Wu Tang" từ 1985 thành 1983, PostgreSQL sao chép tuple gốc rồi áp dụng năm đã đổi vào phiên bản mới
    • Nếu trang hiện có không còn chỗ, phiên bản mới có thể được tạo trên một trang bảng khác

Chuỗi phiên bản và cách O2N

  • Khi nhiều phiên bản vật lý biểu diễn cùng một hàng logic, DBMS phải ghi lại quan hệ giữa các phiên bản
    • DBMS MVCC tạo chuỗi phiên bản dưới dạng danh sách liên kết một chiều
    • Chuỗi chỉ nối theo một hướng để giảm chi phí lưu trữ và bảo trì
  • Thứ tự chuỗi phiên bản có hai kiểu
    • N2O: phiên bản mới nhất trỏ tới phiên bản trước đó, và đầu chuỗi luôn là phiên bản mới nhất
    • O2N: mỗi phiên bản trỏ tới phiên bản mới hơn, và đầu chuỗi là phiên bản cũ nhất
  • Hầu hết DBMS, bao gồm Oracle và MySQL, triển khai N2O
  • PostgreSQL hiếm khi dùng O2N, ngoại trừ engine In-Memory OLTP của Microsoft SQL Server
    • O2N có thể giảm nhu cầu cập nhật chỉ mục để trỏ tới phiên bản mới mỗi khi tuple bị sửa
    • Đổi lại, để tìm phiên bản mới nhất có thể phải đi theo một chuỗi phiên bản dài
  • Trường t_tcid trong header hàng PostgreSQL chứa tuple ID của phiên bản kế tiếp, hoặc tuple ID của chính nó nếu đó là phiên bản mới nhất
    • Nếu chỉ mục trỏ tới một phiên bản cũ, PostgreSQL phải lần theo chuỗi để tìm phiên bản mới hơn

Cách giảm việc duyệt chuỗi bằng chỉ mục

  • Các nhà phát triển PostgreSQL đã biết hai loại chi phí này từ rất sớm
    • Chi phí tạo bản sao mới của toàn bộ tuple cho mỗi lần cập nhật là lớn
    • Việc hầu hết truy vấn phải duyệt toàn bộ chuỗi phiên bản để tìm phiên bản mới nhất mong muốn là lãng phí
  • Để tránh duyệt chuỗi dài, PostgreSQL thêm mục chỉ mục của bảng cho từng phiên bản vật lý của hàng
    • Nếu một hàng logic có 5 phiên bản vật lý, có thể có tối đa 5 mục chỉ mục cho tuple đó
    • Nếu chỉ mục idx_name trỏ tới nhiều phiên bản của "Shaolin and Wu Tang", PostgreSQL có thể truy cập trực tiếp phiên bản mới nhất
  • Việc truy cập phiên bản mới nhất có thể nhanh hơn, nhưng chỉ mục phình to và chi phí bảo trì tăng lên

Tối ưu hóa cập nhật HOT

  • PostgreSQL dùng cập nhật HOT (heap-only tuple) để giảm tình huống các phiên bản liên quan bị rải trên nhiều trang và tạo nhiều mục chỉ mục
  • Có hai điều kiện để có thể cập nhật HOT
    • Cập nhật không sửa cột được tham chiếu bởi chỉ mục của bảng
    • Phiên bản mới có thể được lưu trên cùng trang dữ liệu với phiên bản trước vì còn đủ chỗ
  • Khi HOT được áp dụng, chỉ mục vẫn trỏ tới phiên bản cũ, và truy vấn lần theo chuỗi phiên bản để tìm phiên bản mới nhất
  • PostgreSQL cũng thực hiện tối ưu hóa cắt tỉa chuỗi phiên bản bằng cách loại bỏ các phiên bản cũ trong quá trình hoạt động bình thường

Loại bỏ dead tuple và vacuum

  • Vì PostgreSQL tạo bản sao hàng mỗi khi cập nhật, nó phải loại bỏ dead tuple, tức các phiên bản cũ
  • PostgreSQL thời kỳ đầu trong thập niên 1980 không loại bỏ dead tuple
    • Ý tưởng là giữ các phiên bản cũ để có thể chạy truy vấn time-travel xem trạng thái cơ sở dữ liệu tại một thời điểm cụ thể
    • Nhưng nếu không loại bỏ dead tuple, kích thước bảng sẽ không giảm dù có xóa dữ liệu, và chuỗi phiên bản của các tuple được cập nhật thường xuyên sẽ dài ra
  • PostgreSQL dọn dead tuple khỏi bảng thông qua quy trình vacuum
    • Vacuum quét tuần tự các trang bảng đã được sửa kể từ lần chạy trước và tìm các phiên bản hết hạn
    • Nếu một phiên bản không hiển thị với giao dịch đang hoạt động nào, nó được coi là expired
    • Vì giao dịch hiện tại không truy cập phiên bản đó và các giao dịch tương lai sẽ dùng phiên bản live mới nhất, việc tái sử dụng không gian là an toàn
  • Tùy cấu hình, PostgreSQL tự động chạy autovacuum định kỳ
    • Có thể điều chỉnh tần suất vacuum cho toàn bộ bảng bằng cấu hình toàn cục
    • Cũng có thể cấu hình autovacuum theo từng bảng
    • Người dùng có thể chạy thủ công bằng lệnh SQL VACUUM

Vấn đề 1: Sao chép toàn bộ tuple

  • Trong MVCC append-only, dù chỉ một cột của tuple thay đổi, toàn bộ các cột vẫn được sao chép sang phiên bản mới
  • Cách này làm tăng mạnh trùng lặp dữ liệu và nhu cầu lưu trữ
    • PostgreSQL có thể cần nhiều bộ nhớ và dung lượng đĩa hơn các DBMS khác để lưu cùng một cơ sở dữ liệu
    • Kết quả là truy vấn có thể chậm hơn và chi phí cloud tăng lên
  • MySQL và Oracle lưu delta đã nén giữa phiên bản mới và phiên bản hiện tại
    • Trong bảng có 1000 cột, nếu chỉ một cột thay đổi, chúng chỉ lưu bản ghi delta cho một cột đã thay đổi đó
    • PostgreSQL tạo một phiên bản mới chứa một cột đã đổi và 999 cột không đổi
  • Thuộc tính TOAST của PostgreSQL được xử lý khác nên bị loại khỏi so sánh này
  • Năm 2013, EnterpriseDB khởi động dự án zheap nhằm thay engine lưu trữ append-only bằng cơ chế dựa trên phiên bản delta
    • Cập nhật chính thức cuối cùng là bài viết trạng thái năm 2021
    • Sau đó không thấy tiến triển rõ rệt

Vấn đề 2: Bloat bảng

  • Các phiên bản hết hạn của PostgreSQL, tức dead tuple, chiếm nhiều không gian hơn phiên bản delta
  • Với workload ghi nhiều, dead tuple có thể tích tụ nhanh hơn trước khi autovacuum kịp xử lý
    • Bảng có thể tiếp tục lớn lên
    • Vì dead tuple và live tuple lẫn trong cùng trang, khi thực thi truy vấn cũng phải nạp dead tuple vào bộ nhớ
    • Bloat không được kiểm soát khiến các lần quét bảng tiêu thụ thêm IOPS và bộ nhớ, làm giảm hiệu năng truy vấn
    • Nếu dead tuple khiến thống kê của optimizer không chính xác, nó có thể dẫn tới kế hoạch truy vấn kém
  • Ví dụ, nếu bảng movies có 10 triệu live tuple và 40 triệu dead tuple, thì 80% là dữ liệu obsolete
    • Nếu kích thước tuple trung bình là 1KB, live tuple chiếm 10GB, còn dead tuple chiếm khoảng 40GB
    • Tổng kích thước bảng là 50GB
    • Khi quét toàn bộ bảng, PostgreSQL phải đọc 50GB từ đĩa và đưa vào bộ nhớ dù phần lớn là dữ liệu obsolete
  • PostgreSQL có cơ chế bảo vệ để tránh việc quét tuần tự làm ô nhiễm cache buffer pool, nhưng cơ chế này không loại bỏ chính chi phí I/O

Khác biệt giữa VACUUM và VACUUM FULL

  • Ngay cả khi autovacuum chạy định kỳ và theo kịp workload, autovacuum thông thường cũng không trả lại dung lượng lưu trữ cho hệ điều hành
  • VACUUM thông thường loại bỏ dead tuple và sắp xếp lại live tuple trong từng trang, nhưng không thu hồi các trang trống trên đĩa
  • Nếu PostgreSQL có thể làm trống trang cuối cùng, nó có thể cắt bỏ trang đó, nhưng các trang khác vẫn nằm trên đĩa
    • Dù loại bỏ 40GB dead tuple khỏi bảng 50GB, PostgreSQL vẫn có thể giữ 50GB dung lượng được cấp phát nhìn từ hệ điều hành hoặc RDS
  • Để thực sự trả lại không gian không dùng tới, cần ghi lại toàn bộ bảng vào không gian mới bằng VACUUM FULL hoặc pg_repack
    • Cả hai thao tác đều tốn nhiều tài nguyên và mất nhiều thời gian
    • Trên cơ sở dữ liệu production, chúng có thể ảnh hưởng lớn đến hiệu năng truy vấn
  • VACUUM FULL loại bỏ dead tuple trên từng trang, nén và chuyển các live tuple còn lại sang trang mới, rồi xóa các trang không cần thiết

Vấn đề 3: Bảo trì chỉ mục phụ

  • Khi PostgreSQL cập nhật một tuple, nó phải cập nhật tất cả chỉ mục của bảng đó
  • Lý do là cả chỉ mục chính lẫn chỉ mục phụ đều lưu vị trí vật lý chính xác của phiên bản
    • Nếu không phải cập nhật HOT, trong đó phiên bản mới được lưu trên cùng trang với phiên bản trước, thì việc này cần thiết cho mọi lần cập nhật
  • Trong ví dụ cập nhật, PostgreSQL tạo phiên bản mới trên Table Page #2, rồi chèn các mục trỏ tới phiên bản mới vào movies_pkey, idx_director, idx_name
  • Cấu trúc buộc phải sửa mọi chỉ mục tạo ra nhiều chi phí hiệu năng
    • Truy vấn cập nhật chậm hơn
    • Phát sinh I/O bổ sung để duyệt từng chỉ mục và chèn mục mới
    • Phát sinh tranh chấp lock/latch trong các cấu trúc nội bộ như chỉ mục và bảng trang của buffer pool
    • Phải trả chi phí bảo trì cả cho những chỉ mục mà truy vấn thực tế không sử dụng
    • Với DBMS tính phí theo IOPS như Amazon Aurora, các lần đọc và ghi bổ sung trở thành vấn đề
  • Trong phân tích cơ sở dữ liệu PostgreSQL của khách hàng OtterTune, trung bình khoảng 46% lượt cập nhật dùng tối ưu hóa HOT
    • Hơn 50% lượt cập nhật còn lại phải chịu chi phí bảo trì chỉ mục
  • Bài viết năm 2016 của Uber về việc chuyển từ Postgres sang MySQL là một ví dụ tiêu biểu cho vấn đề này
    • Với workload ghi nhiều trên các bảng có nhiều chỉ mục phụ, họ gặp vấn đề hiệu năng lớn
  • Oracle và MySQL không gặp cùng vấn đề vì chỉ mục phụ không lưu địa chỉ vật lý của phiên bản mới
    • Chỉ mục phụ lưu định danh logic như tuple ID hoặc khóa chính
    • DBMS dùng định danh logic này để tìm địa chỉ vật lý của phiên bản hiện tại
    • Việc đọc chỉ mục phụ có thể chậm hơn, nhưng các lợi ích khác của cách triển khai MVCC giúp giảm overhead

Vấn đề 4: Độ khó khi vận hành autovacuum

  • Hiệu năng PostgreSQL phụ thuộc lớn vào hiệu quả của autovacuum, tức việc loại bỏ dữ liệu obsolete và cho phép tái sử dụng không gian
  • RDS, Aurora và Aurora Serverless đều là biến thể PostgreSQL nên có cùng vấn đề autovacuum
  • Autovacuum phức tạp nên khó chạy ở trạng thái tối ưu
    • Cấu hình mặc định không phù hợp với mọi bảng
    • Vấn đề đặc biệt lớn ở các bảng lớn
  • Giá trị mặc định của autovacuum_vacuum_scale_factor là 20%
    • Với bảng 100 triệu tuple, phải có ít nhất 20 triệu tuple được cập nhật thì autovacuum mới được kích hoạt
    • Vì vậy nhiều dead tuple có thể nằm trong bảng rất lâu, gây chi phí I/O và bộ nhớ
  • Long-running transaction có thể chặn autovacuum
    • Nếu việc dọn các expired version bị trì hoãn, dead tuple và thống kê cũ sẽ tích tụ
    • Có thể hình thành vòng luẩn quẩn: vấn đề hiệu năng tạo ra thêm long-running transaction, rồi các giao dịch đó lại chặn autovacuum
    • Trong trường hợp như vậy, con người có thể phải kết thúc long-running transaction thủ công

Trường hợp khách hàng OtterTune

  • Trong một cơ sở dữ liệu PostgreSQL Amazon RDS, số lượng dead tuple biến động theo dạng răng cưa trong 2 tuần
    • Autovacuum thực hiện một đợt dọn lớn khoảng mỗi ngày một lần
    • Ngày 14/2, DBMS dọn 3,2 triệu dead tuple
    • Trên toàn bộ biểu đồ, số dead tuple có xu hướng tăng, cho thấy trạng thái bất thường khi autovacuum không theo kịp
  • Trong một instance PostgreSQL RDS của khách hàng OtterTune, sau một đợt bulk insertion, thống kê cũ đã gây ra long-running query
    • Truy vấn này chặn việc cập nhật thống kê của autovacuum
    • Kết quả là càng xuất hiện thêm long-running query
    • Health check tự động của OtterTune đã xác định vấn đề, nhưng quản trị viên phải kết thúc truy vấn thủ công và chạy ANALYZE sau bulk insertion
    • Thời gian chạy của long query đó giảm từ 52 phút xuống 34 giây

Kết luận thực tiễn

  • Trong thiết kế DBMS luôn có những lựa chọn khó, và mỗi lựa chọn khiến hiệu năng khác nhau theo từng workload
  • Với workload ghi rất nhiều cụ thể của Uber, khuếch đại ghi chỉ mục do MVCC của PostgreSQL là lý do chuyển sang MySQL
  • Cách triển khai MVCC của PostgreSQL được đánh giá là cách không nên noi theo nếu xây một DBMS MVCC mới
    • Vấn đề cốt lõi là sự kết hợp giữa lưu trữ append-only và autovacuum
    • Thiết kế này là di sản của thập niên 1980, trước khi các mẫu hệ thống log-structured trở nên phổ biến từ thập niên 1990 trở đi
  • PostgreSQL vẫn là DBMS được ưa chuộng, nhưng phải vận hành trong khi chấp nhận các điểm yếu của MVCC
  • Cách xử lý là tự bỏ nhiều thời gian và công sức để tinh chỉnh

1 bình luận

 
GN⁺ 2024-10-21
Các ý kiến trên Hacker News
  • Dù tôi nghĩ mình khá hiểu bên trong Postgres, bài viết này vẫn rất hay và tôi học được nhiều điều
    Một trong những điểm yếu căn bản có vẻ là Postgres đã chọn cách O2N thay vì N2O để theo dõi phiên bản hàng. Chuyển sang N2O không giải quyết mọi vấn đề, ví dụ vấn đề lưu bản sao toàn bộ hàng vẫn còn, nhưng nhìn theo quy tắc 80/20 thì có thể giảm được phần lớn nhược điểm của cách triển khai hiện tại
    Vì phần lớn giao dịch sẽ muốn phiên bản hàng mới nhất, nếu dùng thứ tự N2O thì chỉ cần lần theo danh sách liên kết khi cần phiên bản cũ, và có khả năng không cần lưu từng phiên bản hàng trong chỉ mục

    • Nên xem bài giảng YouTube History of Databases của Andy (CMU Databases / Spring 2020)
      Cả buổi học đầu tiên được dạy trên đường phố Amsterdam vì không vào được khách sạn; nhân vật này cũng thú vị và khả năng giải thích cơ chế bên trong thì rất ấn tượng
  • Ưu điểm lớn là khi tải công việc chủ yếu gồm INSERT rồi sau đó xóa bảng, không cần thêm không gian lưu trữ
    Thông thường cũng không cần chia nhỏ giao dịch chèn. Vì về thực tế không có giới hạn về kích thước dữ liệu tạo ra hay tổng số hàng đã thay đổi. Có giới hạn về số câu lệnh trong một giao dịch, nhưng nếu không thay đổi bảng quá thường xuyên thì có thể đi đường vòng bằng COPY FROM
    Từ góc nhìn DBA, không cần quản lý không gian rollback/undo tách biệt với lưu trữ bảng. Tùy ứng dụng, nhưng thiết kế PostgreSQL không phải là kẻ thua trên mọi phương diện. Ý là nó không giống như sắp xếp nổi bọt

    • Khi làm phát triển game đầu những năm 2000, tôi học được rằng sắp xếp nổi bọt cũng không phải là kẻ thua trên mọi phương diện
      Nó có hiệu năng tốt khi danh sách gần như đã được sắp xếp, và trong render 3D, đó là trường hợp khi sắp xếp đối tượng theo khoảng cách tới camera. Nếu chỉ di chuyển hoặc xoay camera một chút, khi sắp xếp lại dựa trên thứ tự của khung hình trước, sắp xếp nổi bọt hoạt động rất tốt
      Để tránh trường hợp xấu nhất, chỉ cần đếm số lần so sánh thất bại ở lượt quét cuối và số lượt quét đã thực hiện cho đến nay; nếu vượt ngưỡng thì chuyển sang thuật toán sắp xếp khác
    • Sắp xếp nổi bọt rất tuyệt trên phần cứng hoặc với tập gần như đã sắp xếp
    • Bài viết nói thẳng rằng thiết kế MVCC của pg mang phong cách thập niên 90 và giờ không ai làm như vậy nữa
      Vì là công nghệ đã cũ hơn 30 năm, dù không phải là kẻ thua trên mọi phương diện, tôi cho rằng nó thua ở những khía cạnh quan trọng nhất
  • Tôi cho rằng bài viết này sai, đặc biệt ở đoạn sau
    “Trong thập niên 2000, quan niệm phổ biến chọn MySQL vì các ngôi sao công nghệ đang lên như Google và Facebook dùng MySQL. Trong thập niên 2010, đó là MongoDB, thứ trở thành ‘webscale’ nhờ các lần ghi không bền vững. Trong 5 năm qua, PostgreSQL đã trở thành DBMS được Internet yêu thích. Điều đó có lý do chính đáng! Nó đáng tin cậy, giàu tính năng, có khả năng mở rộng và phù hợp với hầu hết tải công việc vận hành.”
    Các kỹ sư giỏi chọn Postgres không phải vì ngụy biện viện dẫn số đông, mà vì an toàn dữ liệu, ACID, sự tương đồng với Oracle, MVCC, tuân thủ chuẩn SQL, đội ngũ Postgres, cộng đồng tuyệt vời và hữu ích, các kiểu dữ liệu, hiệu năng cao, và sự linh hoạt của BSD
    Đây cũng là những lý do ATT chọn Postgres vào đầu những năm 2000, và các Oracle DBA cũng chấp nhận việc chuyển đổi rất dễ dàng. Trong khi MySQL trải qua quá trình chuyển đổi gập ghềnh, PG tiếp tục mạnh lên và cải thiện. Tôi nghĩ Bruce Momjian đã đóng vai trò lớn trong thành công này, và cộng đồng thật sự xuất sắc

    • Tương tự, vào năm 2005 tôi đã đổi sở thích từ MySQL sang PostgreSQL
      Vì tôi muốn dùng database view để tạo một lớp tương thích “live” giữa schema cơ sở dữ liệu AS400 cũ và một ứng dụng Rails hiện đại
      Sau đó, sự an toàn dữ liệu, DDL trong giao dịch và những thứ tương tự khiến tôi ngày càng thích nó hơn
  • “MySQL và Oracle lưu delta đã nén giữa phiên bản mới và phiên bản hiện tại (có thể nghĩ như git diff).”
    Git nổi tiếng là không lưu diff, mà lưu đối tượng mới và toàn bộ đối tượng trước đó, giống cách Postgres dùng ở đây, không phải sao?

    • Câu được trích không nói Git lưu diff, mà nói thứ MySQL và Oracle lưu giống git diff
    • Đúng vậy. Mỗi phiên bản của tệp là một blob riêng
      Dù có nén bằng packing để clone nhanh hơn, dạng thô mà Git xử lý là các blob như vậy
    • Đối tượng so sánh không phải là cách Git lưu trữ, mà là phép ví von rằng cách MySQL và Oracle lưu trữ giống định dạng git diff
      Kho Git cũng có nén, và cách nén đó phần nào dựa trên diff, nhưng không dựa trên lịch sử commit như người ta có thể ngây thơ nghĩ
    • Dù đã nói rằng người khác gọi là “git diffs”, Git thực sự dùng delta như một tối ưu hóa cấp thấp trong pack file, tương tự phép so sánh với MySQL
      Trong truy vấn SQL, diff cũng không được trả về nguyên dạng
    • Git diff được tạo tức thì, nhưng diff vẫn là diff
  • “Nhu cầu PostgreSQL phải sửa mọi chỉ mục của bảng cho mỗi lần cập nhật kéo theo nhiều tác động về hiệu năng. Hiển nhiên hệ thống phải làm nhiều việc hơn, nên truy vấn update chậm đi.”
    Tôi đã thắc mắc về khuếch đại ghi này. Đúng là MySQL không cần cập nhật chỉ mục theo cách đó, nhưng replication của MySQL phụ thuộc vào binlog, và mọi thay đổi phải được ghi thêm ngoài chính cơ sở dữ liệu (như InnoDB redo log, v.v.)
    Vì vậy MySQL dùng trong cụm có vẻ có một kiểu khuếch đại ghi khác. PostgreSQL tái sử dụng WAL cho replication nên không có sự khuếch đại đó
    Hơn nữa, ở phía nhận, MySQL trước tiên ghi binlog đến vào relay log, rồi relay log được các luồng applier tiêu thụ, tạo thêm các lần ghi InnoDB và, theo cấu hình mặc định, thêm binlog nữa

  • Không thể thảo luận chủ đề này nếu bỏ qua đĩa
    SSD ghi theo từng trang 4KB. Nghĩa là dù chỉ cập nhật 1 bit, đĩa cũng đọc 4KB, đổi bit đó rồi ghi lại trang 4KB vào một slot mới. Vì vậy, mức phạt của việc sao chép phụ thuộc vào loại đĩa

    • Thú vị thật. Không biết điều này được phản ánh thế nào trong cách tính phí của AWS
      AWS tính phí cố định theo MBps của IO, nhưng không rõ có quy tắc làm tròn lên mức 4KB gần nhất hay không, hay là họ không theo dõi lượng request mà theo dõi dung lượng ghi của chính ổ đĩa rồi tính phí lượng IO thực tế ở tầng triển khai lưu trữ
    • Trang của Postgres là 8KB, nên điểm đó không có nhiều ý nghĩa
  • “Oracle và MySQL không gặp vấn đề này trong triển khai MVCC, vì các chỉ mục phụ không lưu địa chỉ vật lý của phiên bản mới. Thay vào đó, chúng lưu một định danh logic (ví dụ: tuple ID, khóa chính), và DBMS dùng định danh này để tìm địa chỉ vật lý của phiên bản hiện tại. Điều này có thể làm việc đọc chỉ mục phụ chậm hơn, nhưng các DBMS này có những ưu điểm khác trong triển khai MVCC giúp giảm overhead.”
    Có một hành vi thú vị tôi quan sát được ở MySQL. Đó là một database khoảng 500GB, schema gần với hướng tài liệu hơn là quan hệ; SELECT id WHERE something; UPDATE what WHERE id=id nhanh hơn vài bậc độ lớn so với UPDATE what WHERE something
    Tôi đoán lý do của hành vi này có lẽ nằm ở đây. Tuy nhiên, workload thông thường không làm như vậy; nó chỉ chậm trong các DML tạm thời dùng để sửa sai lệch

    • SELECT là thao tác chỉ đọc nên có thể thực thi song song
      Trong khi đó UPDATE thực sự ghi dữ liệu và cũng có thể khóa bảng. UPDATE id=id cho phép khóa ở cấp hàng. Tuy nhiên cũng có rủi ro bỏ sót các bản ghi mới được chèn vào giữa SELECT và UPDATE
    • Tôi đang vận hành vài instance Postgres trên 2TB thiên về đọc, và chúng cũng khá gần với hướng tài liệu
      Tôi đồng ý rằng cập nhật hàng loạt có thể quá chậm. Cuối cùng thường phải xử lý dần theo từng batch hoặc thậm chí dùng COPY
  • “Trong thập niên 2010, đó là MongoDB. Vì các ghi không bền vững đã khiến nó trở thành ‘webscale’.”
    Hơi lệch chủ đề, nhưng từ đầu đến cuối đó là marketing: https://news.ycombinator.com/item?id=15124306

    • MongoDB được thiết kế như một DB DIY do các cựu kỹ sư DoubleClick tiện tay tạo ra vì không có DB nào đáp ứng được các yêu cầu cần cho dịch vụ khác
      Đến phiên bản 4.2.8 (2020), nó đã khá vững chắc và được biết là không còn dirty write: https://en.wikipedia.org/wiki/MongoDB#Technical_criticisms
  • Điều tôi ghét nhất là đến năm 2024 vẫn phải đặt connection pooler (ví dụ: pgbouncer) ở phía trước thì mới dùng ổn

  • OrioleDB đã cố giải quyết vấn đề này bằng một storage engine mới: https://github.com/orioledb/orioledb

    • Đang ở trong đội Supabase
      Oriole hiện đã gia nhập Supabase, và Alexander cùng đội của anh ấy đang làm full-time. Patch set ở đây: https://www.orioledb.com/docs#patch-set
      Dự kiến cuối năm nay cũng sẽ có thể dùng thử trên nền tảng Supabase