Nếu bạn đang làm việc với cơ sở dữ liệu lớn trong Postgres , câu chuyện này nghe có vẻ quen thuộc. Khi cơ sở dữ liệu Postgres của bạn tiếp tục phát triển, hiệu suất của bạn bắt đầu giảm và bạn bắt đầu lo lắng về dung lượng lưu trữ—hay nói chính xác hơn là bạn sẽ trả bao nhiêu cho nó. Bạn yêu thích PostgreSQL, nhưng có một thứ bạn ước mình có: một cơ chế nén dữ liệu hiệu quả cao.
PostgreSQL có phần nào cơ chế nén:
Ngay cả khi nó có thể làm giảm kích thước của tập dữ liệu, TOAST (Kỹ thuật lưu trữ thuộc tính quá khổ) không phải là cơ chế nén dữ liệu truyền thống của bạn. Để hiểu TOAST là gì, chúng ta phải bắt đầu bằng cách nói về
Đơn vị lưu trữ của Postgres được gọi là trang và chúng có kích thước cố định (8 kB theo mặc định). Việc có kích thước trang cố định mang lại cho Postgres nhiều lợi thế, đó là tính đơn giản, hiệu quả và tính nhất quán trong quản lý dữ liệu, nhưng nó cũng có nhược điểm: một số giá trị dữ liệu có thể không vừa với trang đó.
Đây là lúc TOAST xuất hiện. TOAST đề cập đến cơ chế tự động mà PostgreSQL sử dụng để lưu trữ và quản lý hiệu quả các giá trị trong Postgres không vừa trong một trang. Để xử lý các giá trị như vậy, theo mặc định, Postgres TOAST sẽ nén chúng bằng thuật toán nội bộ. Nếu sau khi nén, các giá trị vẫn còn quá lớn, Postgres sẽ chuyển chúng sang một bảng riêng (gọi là bảng TOAST), để lại các con trỏ trong bảng gốc.
Như chúng ta sẽ thấy ở phần sau của bài viết này, bạn thực sự có thể sửa đổi chiến lược này với tư cách là người dùng, chẳng hạn như bằng cách yêu cầu Postgres tránh nén dữ liệu trong một cột cụ thể.
Các loại dữ liệu có thể phải tuân theo TOAST chủ yếu là các loại có độ dài thay đổi có khả năng vượt quá giới hạn kích thước của trang PostgreSQL tiêu chuẩn. Mặt khác, các loại dữ liệu có độ dài cố định, như integer
, float
hoặc timestamp
, không phải chịu TOAST vì chúng vừa khít trong một trang.
Một số ví dụ về các loại dữ liệu có thể phải tuân theo TOAST là:
json
và jsonb
Chuỗi text
lớn
varchar
và varchar(n)
(Nếu độ dài được chỉ định trong varchar(n)
đủ nhỏ thì các giá trị của cột đó có thể luôn ở dưới ngưỡng TOAST.)
bytea
lưu trữ dữ liệu nhị phân
Dữ liệu hình học như path
và polygon
và các loại PostGIS như geometry
hoặc geography
Hiểu TOAST không chỉ liên quan đến khái niệm kích thước trang mà còn liên quan đến khái niệm lưu trữ Postgres khác: bộ dữ liệu. Bộ dữ liệu là các hàng trong bảng PostgreSQL. Thông thường, cơ chế TOAST sẽ hoạt động nếu tất cả các trường trong một bộ dữ liệu có tổng kích thước xấp xỉ trên 2 kB.
Nếu bạn chú ý, bạn có thể thắc mắc: "Đợi đã, nhưng kích thước trang là khoảng 8 kB—tại sao lại có chi phí này?" Đó là bởi vì PostgreSQL muốn đảm bảo rằng nó có thể lưu trữ nhiều bộ dữ liệu trên một trang: nếu các bộ dữ liệu quá lớn, sẽ có ít bộ dữ liệu phù hợp trên mỗi trang hơn, dẫn đến tăng hoạt động I/O và giảm hiệu suất.
Postgres cũng cần giữ không gian trống để chứa dữ liệu vận hành bổ sung: mỗi trang không chỉ lưu trữ dữ liệu bộ dữ liệu mà còn lưu trữ thông tin bổ sung để quản lý dữ liệu, chẳng hạn như mã nhận dạng mục, tiêu đề và thông tin giao dịch.
Vì vậy, khi kích thước kết hợp của tất cả các trường trong một bộ dữ liệu vượt quá khoảng 2 kB (hoặc tham số ngưỡng TOAST, như chúng ta sẽ thấy sau), PostgreSQL sẽ thực hiện hành động để đảm bảo rằng dữ liệu được lưu trữ hiệu quả. TOAST xử lý việc này theo hai cách chính:
Nén. PostgreSQL có thể nén các giá trị trường lớn trong bộ dữ liệu để giảm kích thước của chúng bằng thuật toán nén mà chúng tôi sẽ đề cập ở phần sau của bài viết này. Theo mặc định, nếu quá trình nén đủ để đưa tổng kích thước của bộ dữ liệu xuống dưới ngưỡng thì dữ liệu sẽ vẫn còn trong bảng chính, mặc dù ở định dạng nén.
Lưu trữ ngoại tuyến. Nếu chỉ nén không đủ hiệu quả để giảm kích thước của các giá trị trường lớn, Postgres sẽ chuyển chúng sang một bảng TOAST riêng. Quá trình này được gọi là lưu trữ "ngoại tuyến" vì bộ dữ liệu ban đầu trong bảng chính không còn chứa các giá trị trường lớn nữa. Thay vào đó, nó chứa một "con trỏ" hoặc tham chiếu đến vị trí của dữ liệu lớn trong bảng TOAST.
Chúng tôi đang đơn giản hóa mọi thứ một chút cho bài viết này—
pglz
Chúng tôi đã đề cập rằng TOAST có thể nén các giá trị lớn trong PostgreSQL. Nhưng PostgreSQL đang sử dụng thuật toán nén nào và nó hiệu quả như thế nào?
pglz
(PostgreSQL Lempel-Ziv) là thuật toán nén nội bộ mặc định được PostgreSQL thiết kế riêng cho TOAST.
Đây là cách nó hoạt động trong điều kiện rất đơn giản:
pglz
cố gắng tránh dữ liệu lặp lại. Khi nó nhìn thấy dữ liệu lặp lại, thay vì viết lại cùng một thứ, nó chỉ quay lại nơi nó đã viết trước đó. Việc "tránh lặp lại" này giúp tiết kiệm không gian.
Khi pglz
đọc dữ liệu, nó sẽ nhớ một chút dữ liệu gần đây mà nó đã xem. Bộ nhớ gần đây này được gọi là "cửa sổ trượt".
Khi có dữ liệu mới, pglz
sẽ kiểm tra xem nó có thấy dữ liệu này gần đây không (trong cửa sổ trượt của nó). Nếu có, nó sẽ ghi một tham chiếu ngắn thay vì lặp lại dữ liệu.
Nếu dữ liệu mới hoặc không được lặp lại đủ số lần để tạo tham chiếu ngắn hơn dữ liệu thực tế, pglz
chỉ ghi dữ liệu đó ra giấy.
Khi đến lúc đọc dữ liệu nén, pglz
sử dụng các tham chiếu của nó để tìm nạp dữ liệu gốc. Quá trình này khá trực tiếp vì nó tra cứu dữ liệu được tham chiếu và đặt nó vào đúng nơi.
pglz
không cần bộ nhớ riêng cho bộ nhớ của nó (cửa sổ trượt); nó xây dựng nó khi đang di chuyển trong khi nén và thực hiện tương tự khi giải nén.
Việc triển khai này được thiết kế để mang lại sự cân bằng giữa hiệu quả nén và tốc độ trong cơ chế TOAST. Về tốc độ nén, hiệu quả của pglz
sẽ phụ thuộc phần lớn vào bản chất của dữ liệu.
Ví dụ, dữ liệu có tính lặp lại cao sẽ nén tốt hơn nhiều so với dữ liệu có entropy cao (như dữ liệu ngẫu nhiên). Bạn có thể thấy tỷ lệ nén nằm trong khoảng từ 25 đến 50 phần trăm, nhưng đây là ước tính rất chung chung—kết quả sẽ rất khác nhau dựa trên tính chất chính xác của dữ liệu.
Theo mặc định, PostgreSQL sẽ thực hiện cơ chế TOAST theo quy trình được giải thích trước đó (nén trước và lưu trữ ngoại tuyến tiếp theo, nếu nén không đủ). Tuy nhiên, có thể có những tình huống mà bạn có thể muốn tinh chỉnh hành vi này trên cơ sở từng cột. PostgreSQL cho phép bạn thực hiện điều này bằng cách sử dụng các chiến lược TOAST PLAIN
, EXTERNAL
, EXTENDED
và MAIN
.
EXTENDED
: Đây là chiến lược mặc định. Nó ngụ ý rằng dữ liệu sẽ được lưu trữ ngoài dòng trong một bảng TOAST riêng nếu nó quá lớn đối với một trang bảng thông thường. Trước khi di chuyển dữ liệu sang bảng TOAST, nó sẽ được nén để tiết kiệm dung lượng.
EXTERNAL
: Chiến lược này yêu cầu PostgreSQL lưu trữ dữ liệu cho cột này ngoài dòng nếu dữ liệu quá lớn để vừa với một trang bảng thông thường và chúng tôi đang yêu cầu PostgreSQL không nén dữ liệu—giá trị sẽ chỉ được chuyển đến Bảng TOAST nguyên trạng.
MAIN
: Chiến lược này là một nền tảng trung gian. Nó cố gắng giữ dữ liệu thẳng hàng trong bảng chính thông qua việc nén; nếu dữ liệu chắc chắn quá lớn, nó sẽ di chuyển dữ liệu sang bảng TOAST để tránh lỗi, nhưng PostgreSQL sẽ không di chuyển dữ liệu nén. Thay vào đó, nó sẽ lưu trữ giá trị trong bảng TOAST ở dạng ban đầu.
PLAIN
: Việc sử dụng PLAIN
trong một cột sẽ yêu cầu PostgreSQL luôn lưu trữ dữ liệu của cột thẳng hàng trong bảng chính, đảm bảo dữ liệu đó không bị di chuyển sang bảng TOAST ngoài dòng. Hãy lưu ý rằng nếu dữ liệu vượt quá kích thước trang, INSERT
sẽ không thành công vì dữ liệu không vừa.
Nếu bạn muốn kiểm tra các chiến lược hiện tại của một bảng cụ thể, bạn có thể chạy như sau:
\d+ your_table_name
Bạn sẽ nhận được một đầu ra như thế này:
=> \d+ example_table Table "public.example_table" Column | Data Type | Modifiers | Storage | Stats target | Description ---------+------------------+-----------+----------+--------------+------------- bar | varchar(100000) | | extended | |
Nếu bạn muốn sửa đổi cài đặt bộ nhớ, bạn có thể thực hiện bằng lệnh sau:
-- Sets EXTENDED as the TOAST strategy for bar_column ALTER TABLE example_blob ALTER COLUMN bar_column SET STORAGE EXTENDED;
Ngoài các chiến lược trên, hai tham số này cũng rất quan trọng để kiểm soát hành vi TOAST:
TOAST_TUPLE_THRESHOLD
Đây là tham số đặt ngưỡng kích thước khi các hoạt động TOASTing (nén và lưu trữ ngoại tuyến) được xem xét đối với các bộ dữ liệu quá khổ.
Như chúng tôi đã đề cập trước đây, theo mặc định, TOAST_TUPLE_THRESHOLD
được đặt ở khoảng 2 kB.
TOAST_COMPRESSION_THRESHOLD
Đây là tham số chỉ định kích thước tối thiểu của một giá trị trước khi Postgres xem xét việc nén nó trong quá trình TOASTing.
Nếu một giá trị vượt quá ngưỡng này, PostgreSQL sẽ cố gắng nén nó. Tuy nhiên, chỉ vì một giá trị vượt quá ngưỡng nén, điều đó không tự động có nghĩa là nó sẽ bị nén: chiến lược TOAST sẽ hướng dẫn PostgreSQL cách xử lý dữ liệu dựa trên việc liệu nó có được nén hay không và kích thước kết quả của nó so với bộ dữ liệu và giới hạn trang, như chúng ta sẽ thấy trong phần tiếp theo.
TOAST_TUPLE_THRESHOLD
là điểm kích hoạt. Khi kích thước của các trường dữ liệu của một bộ dữ liệu kết hợp vượt quá ngưỡng này, PostgreSQL sẽ đánh giá cách quản lý nó dựa trên chiến lược TOAST đã đặt cho các cột của nó, xem xét việc nén và lưu trữ ngoài dòng. Các hành động chính xác được thực hiện cũng sẽ phụ thuộc vào việc dữ liệu cột có vượt qua TOAST_COMPRESSION_THRESHOLD
hay không:
EXTENDED
(chiến lược mặc định): Nếu kích thước của bộ dữ liệu vượt quá TOAST_TUPLE_THRESHOLD
, trước tiên PostgreSQL sẽ cố gắng nén các cột quá khổ nếu chúng cũng vượt quá TOAST_COMPRESSION_THRESHOLD
. Nếu quá trình nén đưa kích thước bộ dữ liệu xuống dưới ngưỡng thì nó sẽ vẫn còn trong bảng chính. Nếu không, dữ liệu sẽ được chuyển sang bảng TOAST ngoại tuyến và bảng chính sẽ chứa các con trỏ tới dữ liệu bên ngoài này.
MAIN
: Nếu kích thước bộ dữ liệu vượt quá TOAST_TUPLE_THRESHOLD
, PostgreSQL sẽ cố nén các cột quá khổ (miễn là chúng vượt quá TOAST_COMPRESSION_THRESHOLD
). Nếu việc nén cho phép bộ dữ liệu vừa khít với bộ dữ liệu của bảng chính thì nó vẫn ở đó. Nếu không, dữ liệu sẽ được chuyển đến bảng TOAST ở dạng không nén.
EXTERNAL
: PostgreSQL bỏ qua quá trình nén, bất kể TOAST_COMPRESSION_THRESHOLD
. Nếu kích thước của bộ dữ liệu vượt quá TOAST_TUPLE_THRESHOLD
, các cột quá khổ sẽ được lưu trữ ngoài dòng trong bảng TOAST.
PLAIN
: Dữ liệu luôn được lưu trữ trong bảng chính. Nếu kích thước của bộ dữ liệu vượt quá kích thước trang (do có các cột rất lớn), sẽ xảy ra lỗi.
Chiến lược | Nén nếu tuple > TOAST_COMPRESSION_THRESHOLD | Lưu trữ ngoại tuyến nếu bộ > TOAST_TUPLE_THRESHOLD | Sự miêu tả |
---|---|---|---|
MỞ RỘNG | Đúng | Đúng | Chiến lược mặc định. Nén trước, sau đó kiểm tra xem có cần lưu trữ ngoại tuyến hay không. |
CHỦ YẾU | Đúng | Chỉ ở dạng không nén | Nén trước, nếu vẫn quá khổ thì chuyển sang bảng TOAST mà không nén. |
BÊN NGOÀI | KHÔNG | Đúng | Luôn chuyển sang TOAST nếu quá khổ, không nén. |
ĐƠN GIẢN | KHÔNG | KHÔNG | Dữ liệu luôn ở trong bảng chính. Nếu một bộ vượt quá kích thước trang thì sẽ xảy ra lỗi. |
Đến bây giờ, bạn có thể đã hiểu tại sao TOAST không phải là cơ chế nén dữ liệu mà bạn mong muốn có trong PostgreSQL. Các ứng dụng hiện đại ngụ ý khối lượng lớn dữ liệu được nhập hàng ngày, có nghĩa là cơ sở dữ liệu (quá mức) phát triển nhanh chóng.
Vấn đề như vậy không còn nổi bật khi Postgres yêu quý của chúng tôi được xây dựng cách đây nhiều thập kỷ, nhưng các nhà phát triển ngày nay cần các giải pháp nén để giảm dung lượng lưu trữ của tập dữ liệu của họ.
Mặc dù TOAST kết hợp tính năng nén như một trong những kỹ thuật của nó, nhưng điều quan trọng là phải hiểu rằng vai trò chính của nó không phải là cơ chế nén cơ sở dữ liệu theo nghĩa truyền thống. TOAST chủ yếu là giải pháp cho một vấn đề: quản lý các giá trị lớn trong giới hạn cấu trúc của trang Postgres.
Mặc dù cách tiếp cận này có thể giúp tiết kiệm không gian lưu trữ phần nào do nén các giá trị lớn cụ thể, nhưng mục đích chính của nó không phải là tối ưu hóa không gian lưu trữ trên bảng.
Ví dụ: nếu bạn có cơ sở dữ liệu 5 TB được tạo thành từ các bộ dữ liệu nhỏ, TOAST sẽ không giúp bạn biến 5 TB đó thành 1 TB. Mặc dù có thể điều chỉnh các tham số trong TOAST nhưng điều này sẽ không biến TOAST thành giải pháp tiết kiệm dung lượng lưu trữ tổng quát.
Và có những vấn đề cố hữu khác khi sử dụng TOAST làm cơ chế nén truyền thống trong PostgreSQL, chẳng hạn:
Việc truy cập dữ liệu TOASTed có thể tăng thêm chi phí, đặc biệt khi dữ liệu được lưu trữ ngoài luồng. Điều này trở nên rõ ràng hơn khi thường xuyên truy cập nhiều văn bản lớn hoặc các loại dữ liệu có khả năng TOAST khác.
TOAST thiếu cơ chế cấp cao, thân thiện với người dùng để đưa ra các chính sách nén. Nó không được xây dựng để tối ưu hóa chi phí lưu trữ hoặc hỗ trợ quản lý lưu trữ.
Tính năng nén của TOAST không được thiết kế để cung cấp tỷ lệ nén đặc biệt cao. Nó chỉ sử dụng một thuật toán ( pglz
) với tốc độ nén thường dao động từ 25-50%.
Bằng cách thêm chính sách nén vào các bảng lớn của bạn,
Bằng cách xác định chính sách nén dựa trên thời gian, bạn chỉ ra khi nào dữ liệu sẽ được nén. Ví dụ: bạn có thể chọn tự động nén dữ liệu cũ hơn bảy (7) ngày:
-- Compress data older than 7 days SELECT add_compression_policy('my_hypertable', INTERVAL '7 days');
Thông qua chính sách nén này, Timescale sẽ chuyển đổi bảng
Nén Gorilla cho phao
Đồng bằng của đồng bằng +
Nén từ điển toàn hàng cho các cột có một vài giá trị lặp lại (+ nén LZ ở trên cùng)
Nén mảng dựa trên LZ cho tất cả các loại khác
Thiết kế nén cột này cung cấp một giải pháp hiệu quả và có thể mở rộng cho vấn đề về tập dữ liệu lớn trong PostgreSQL. Nó cho phép bạn sử dụng ít bộ nhớ hơn để lưu trữ nhiều dữ liệu hơn mà không ảnh hưởng đến hiệu suất truy vấn của bạn (nó cải thiện hiệu suất). Và trong các phiên bản mới nhất của TimescaleDB, bạn cũng có thể INSERT
, DELETE
và UPDATE
trực tiếp trên dữ liệu nén.
Chúng tôi hy vọng bài viết này đã giúp bạn hiểu rằng mặc dù TOAST là một cơ chế được cân nhắc kỹ lưỡng để quản lý các giá trị lớn trong trang PostgreSQL nhưng nó không hiệu quả trong việc tối ưu hóa việc sử dụng bộ nhớ cơ sở dữ liệu trong lĩnh vực ứng dụng hiện đại.
Nếu bạn đang tìm kiếm giải pháp nén dữ liệu hiệu quả có thể giúp bạn tiết kiệm dung lượng lưu trữ, hãy thử Timescale. Bạn có thể dùng thử nền tảng đám mây của chúng tôi để nâng PostgreSQL lên tầm cao hiệu suất mới, làm cho nó nhanh hơn và mạnh mẽ hơn—
Viết bởi Carlota Soto .