Một hệ thống phân vùng trong PostgreSQL lần đầu tiên được thêm vào trong PostgreSQL 8.1 bởi người sáng lập 2ndQuadrant Simon Riggs . Nó dựa trên sự kế thừa quan hệ và sử dụng một kỹ thuật mới để loại trừ các bảng khỏi bị quét bởi một truy vấn, được gọi là "loại trừ ràng buộc". Mặc dù nó là một bước tiến lớn vào thời điểm đó, nhưng ngày nay nó được coi là cồng kềnh để sử dụng cũng như chậm và do đó cần phải thay thế.
Trong phiên bản 10, nó đã được thay thế nhờ những nỗ lực anh hùng của Amit Langote với "phân vùng khai báo" kiểu hiện đại. Công nghệ mới này có nghĩa là bạn không còn cần phải viết mã theo cách thủ công để định tuyến các bộ giá trị đến các phân vùng chính xác của chúng và không còn cần phải khai báo các ràng buộc chính xác cho mỗi phân vùng theo cách thủ công nữa:hệ thống đã tự động làm những việc đó cho bạn.
Đáng buồn thay, trong PostgreSQL 10, đó là tất cả những gì nó đã làm. Vì độ phức tạp tuyệt đối và thời gian hạn chế, có nhiều thứ trong quá trình triển khai PostgreSQL 10 còn thiếu. Robert Haas đã nói về vấn đề này trên tạp chí PGConf.EU của Warsaw.
Nhiều người đã làm việc để cải thiện tình hình cho PostgreSQL 11; đây là nỗ lực của tôi để kể lại. Tôi chia chúng thành ba lĩnh vực:
- Các tính năng phân vùng mới
- Hỗ trợ DDL tốt hơn cho các bảng được phân vùng
- Tối ưu hóa hiệu suất.
Tính năng phân vùng mới
Trong PostgreSQL 10, các bảng được phân vùng của bạn có thể như vậy trong RANGE và DANH SÁCH các chế độ. Đây là những công cụ mạnh mẽ để dựa trên nhiều cơ sở dữ liệu trong thế giới thực, nhưng đối với nhiều thiết kế khác, bạn cần chế độ mới được thêm vào PostgreSQL 11: HASH phân vùng . Nhiều khách hàng cần điều này và Amul Sul đã làm việc chăm chỉ để làm cho nó có thể. Đây là một ví dụ đơn giản:
CREATE TABLE clients ( client_id INTEGER, name TEXT ) PARTITION BY HASH (client_id); CREATE TABLE clients_0 PARTITION OF clients FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE clients_1 PARTITION OF clients FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE clients_2 PARTITION OF clients FOR VALUES WITH (MODULUS 3, REMAINDER 2);
Không bắt buộc phải sử dụng cùng một giá trị mô đun cho tất cả các phân vùng; điều này cho phép bạn tạo nhiều phân vùng hơn sau đó và phân phối lại các hàng từng phân vùng một, nếu cần.
Một tính năng rất hữu ích khác, được viết bởi Amit Khandekar là khả năng cho phép CẬP NHẬT để di chuyển các hàng từ phân vùng này sang phân vùng khác - nghĩa là, nếu có sự thay đổi trong các giá trị của cột phân vùng, thì hàng đó sẽ tự động được chuyển đến đúng phân vùng. Trước đây, thao tác đó sẽ gặp lỗi.
Một tính năng mới khác do Amit Langote viết và thực sự của bạn , đó có phải là CHÈN VÀO CẬP NHẬT XUNG ĐỘT có thể được áp dụng cho các bảng được phân vùng . Trước đây, lệnh này sẽ không thành công nếu nó nhắm mục tiêu đến một bảng được phân vùng. Bạn có thể làm cho nó hoạt động bằng cách biết chính xác hàng sẽ kết thúc ở phân vùng nào, nhưng điều đó không thuận tiện lắm. Tôi sẽ không trình bày chi tiết về lệnh đó, nhưng nếu bạn từng ước mình có UPSERT trong Postgres, đây là nó. Một lưu ý là CẬP NHẬT hành động không thể di chuyển hàng sang phân vùng khác.
Cuối cùng, một tính năng mới dễ thương khác trong PostgreSQL 11, lần này của Jeevan Ladhe, Beena Emerson, Ashutosh Bapat, Rahila Syed, và Robert Haas là hỗ trợ cho một phân vùng mặc định trong một bảng được phân vùng nghĩa là một phân vùng nhận tất cả các hàng không vừa với bất kỳ phân vùng thông thường nào. Tuy nhiên, mặc dù đẹp trên giấy, tính năng này không thuận tiện lắm trên cài đặt sản xuất vì một số hoạt động yêu cầu khóa nặng hơn với các phân vùng mặc định hơn là không có. Ví dụ:tạo phân vùng mới yêu cầu quét phân vùng mặc định để xác định rằng không có hàng hiện có nào phù hợp với ranh giới của phân vùng mới. Có thể trong tương lai, các yêu cầu về khóa này sẽ được hạ xuống, nhưng trong thời gian chờ đợi, tôi không nên sử dụng nó.
Hỗ trợ DDL tốt hơn
Trong PostgreSQL 10, một số DDL nhất định sẽ từ chối hoạt động khi được áp dụng cho một bảng được phân vùng và yêu cầu bạn xử lý từng phân vùng riêng lẻ. Trong PostgreSQL 11, chúng tôi đã khắc phục một số hạn chế này, như Simon Riggs đã thông báo trước đây. Đầu tiên, bây giờ bạn có thể sử dụng CREATE INDEX trên một bảng được phân vùng , một tính năng được viết bởi bạn thực sự. Điều này có thể được coi là một vấn đề giảm bớt sự tẻ nhạt:thay vì lặp lại lệnh cho mỗi phân vùng (và đảm bảo không bao giờ quên cho mỗi phân vùng mới), bạn có thể thực hiện điều đó chỉ một lần cho bảng được phân vùng chính và nó sẽ tự động áp dụng cho tất cả các phân vùng, hiện tại và tương lai.
Một điều thú vị cần lưu ý là sự phù hợp của các chỉ mục hiện có trong các phân vùng. Như bạn đã biết, tạo chỉ mục là một đề xuất chặn, vì vậy càng mất ít thời gian càng tốt. Tôi đã viết tính năng này để các chỉ mục hiện có trong phân vùng sẽ được so sánh với các chỉ mục đang được tạo và nếu có các chỉ mục phù hợp, không cần thiết phải quét phân vùng để tạo chỉ mục mới:các chỉ mục hiện có sẽ được sử dụng.
Cùng với điều này, cũng thực sự của bạn, bạn cũng có thể tạo DUY NHẤT các ràng buộc, cũng như CHÌA KHÓA CHÍNH ràng buộc . Hai lưu ý:thứ nhất, khóa phân vùng phải là một phần của khóa chính. Điều này cho phép các kiểm tra duy nhất được thực hiện cục bộ trên mỗi phân vùng, tránh các chỉ mục chung. Thứ hai, chưa thể có khóa ngoại tham chiếu đến các khóa chính này. Tôi đang làm việc trên đó cho PostgreSQL 12.
Một điều khác bạn có thể làm (nhờ cùng một người) là tạo CHO MỖI ROW kích hoạt trên một bảng được phân vùng và áp dụng nó cho tất cả các phân vùng (hiện tại và tương lai). Do tác dụng phụ, bạn có thể hoãn lại duy nhất ràng buộc đối với các bảng được phân vùng. Một lưu ý:chỉ SAU KHI trình kích hoạt được cho phép, cho đến khi chúng tôi tìm ra cách xử lý TRƯỚC kích hoạt chuyển các hàng sang một phân vùng khác.
Cuối cùng, một bảng được phân vùng có thể có NGOẠI KHÓA ràng buộc . Điều này rất tiện dụng để phân vùng các bảng dữ kiện lớn trong khi tránh các tham chiếu lủng lẳng, điều mà mọi người đều ghét. Đồng nghiệp của tôi, Gabriele Bartolini đã nắm lấy tôi vào lòng khi anh ấy biết tôi đã viết và cam kết điều này, hét lên rằng đây là một kẻ thay đổi cuộc chơi và làm sao tôi có thể vô cảm đến mức không thông báo cho anh ấy biết điều này. Tôi, tôi chỉ tiếp tục hack mã cho vui.
Hiệu suất công việc
Trước đây, các truy vấn xử lý trước để tìm ra phân vùng nào không cần quét (loại trừ ràng buộc) khá đơn giản và chậm chạp. Điều này đã được cải thiện nhờ tinh thần đồng đội đáng ngưỡng mộ được thực hiện bởi Amit Langote, David Rowley, Beena Emerson, Dilip Kumar để giới thiệu "cắt tỉa nhanh hơn" trước và "cắt tỉa thời gian chạy" dựa trên nó sau đó. Kết quả là mạnh hơn cũng như nhanh hơn nhiều ( David Rowley đã được mô tả điều này trong một bài viết trước.) Sau tất cả nỗ lực này, việc lược bớt phân vùng được áp dụng tại ba điểm trong vòng đời của một truy vấn:
- Vào thời gian lập kế hoạch truy vấn,
- Khi nhận được các tham số truy vấn,
- Tại mỗi thời điểm mà một nút truy vấn chuyển các giá trị dưới dạng tham số cho một nút khác.
Đây là một cải tiến đáng chú ý so với hệ thống ban đầu chỉ có thể được áp dụng vào thời gian lập kế hoạch truy vấn và tôi tin rằng nó sẽ làm hài lòng nhiều người.
Bạn có thể thấy tính năng này hoạt động bằng cách so sánh kết quả EXPLAIN cho một truy vấn trước và sau khi tắt enable_partition_pruning lựa chọn. Như một ví dụ rất đơn giản, hãy so sánh kế hoạch này mà không cần lược bớt:
SET enable_partition_pruning TO off; EXPLAIN (ANALYZE, COSTS off) SELECT * FROM clientes WHERE cliente_id = 1234;
QUERY PLAN ------------------------------------------------------------------------- Append (actual time=6.658..10.549 rows=1 loops=1) -> Seq Scan on clientes_1 (actual time=4.724..4.724 rows=0 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 24978 -> Seq Scan on clientes_00 (actual time=1.914..1.914 rows=0 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12644 -> Seq Scan on clientes_2 (actual time=0.017..1.021 rows=1 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12570 -> Seq Scan on clientes_3 (actual time=0.746..0.746 rows=0 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12448 -> Seq Scan on clientes_01 (actual time=0.648..0.648 rows=0 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12482 -> Seq Scan on clientes_4 (actual time=0.774..0.774 rows=0 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12400 -> Seq Scan on clientes_5 (actual time=0.717..0.717 rows=0 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12477 Planning Time: 0.375 ms Execution Time: 10.603 ms
với một cái được tạo ra với sự cắt tỉa:
EXPLAIN (ANALYZE, COSTS off) SELECT * FROM clientes WHERE cliente_id = 1234;
QUERY PLAN ---------------------------------------------------------------------- Append (actual time=0.054..2.787 rows=1 loops=1) -> Seq Scan on clientes_2 (actual time=0.052..2.785 rows=1 loops=1) Filter: (cliente_id = 1234) Rows Removed by Filter: 12570 Planning Time: 0.292 ms Execution Time: 2.822 ms
Tôi chắc chắn rằng bạn sẽ thấy điều đó hấp dẫn. Bạn có thể thấy rất nhiều ví dụ phức tạp hơn bằng cách xem xét tệp dự kiến của các bài kiểm tra hồi quy.
Một mục khác là sự ra đời của các phép nối theo phân vùng, bởi Ashutosh Bapat . Ý tưởng ở đây là nếu bạn có hai bảng được phân vùng và chúng được phân vùng theo những cách giống hệt nhau, thì khi chúng được nối với nhau, bạn có thể nối mỗi phân vùng ở một bên với phân vùng phù hợp của nó ở bên kia; điều này tốt hơn nhiều so với việc nối từng phân vùng ở bên này với mọi phân vùng ở bên kia. Thực tế là các lược đồ phân vùng cần phải phù hợp chính xác có thể làm cho điều này dường như không được sử dụng nhiều trong thế giới thực, nhưng trong thực tế, có nhiều trường hợp áp dụng điều này. Ví dụ:một bảng đơn đặt hàng và bảng đơn đặt hàng tương ứng của nó. Rất may, đã có rất nhiều việc để giảm bớt hạn chế này.
Mục cuối cùng tôi muốn đề cập là tổng hợp theo phân vùng, của Jeevan Chalke, Ashutosh Bapat, và Robert Haas . Tối ưu hóa này có nghĩa là một tập hợp bao gồm các khóa phân vùng trong GROUP BY mệnh đề có thể được thực thi bằng cách tổng hợp các hàng của từng phân vùng riêng biệt, điều này nhanh hơn nhiều.
Suy nghĩ kết thúc
Sau những phát triển quan trọng trong chu kỳ này, PostgreSQL có một câu chuyện phân vùng hấp dẫn hơn nhiều. Mặc dù vẫn còn nhiều cải tiến cần được thực hiện, đặc biệt là để cải thiện hiệu suất và tính đồng thời của các hoạt động khác nhau liên quan đến các bảng được phân vùng, chúng ta hiện đã đến thời điểm mà phân vùng khai báo đã trở thành một công cụ rất có giá trị để phục vụ nhiều trường hợp sử dụng. Tại 2ndQuadrant, chúng tôi sẽ tiếp tục đóng góp mã để cải thiện PostgreSQL trong lĩnh vực này và những lĩnh vực khác, giống như chúng tôi đã làm cho mọi bản phát hành kể từ 8.0.