Nén dữ liệu trong Apache Ignite. Kinh nghiệm của Sber

Nén dữ liệu trong Apache Ignite. Kinh nghiệm của SberKhi làm việc với khối lượng dữ liệu lớn, vấn đề thiếu dung lượng ổ đĩa đôi khi có thể phát sinh. Một cách để giải quyết vấn đề này là nén, nhờ đó, trên cùng một thiết bị, bạn có thể tăng dung lượng lưu trữ. Trong bài viết này, chúng ta sẽ xem xét cách hoạt động của tính năng nén dữ liệu trong Apache Ignite. Bài viết này sẽ chỉ mô tả các phương pháp nén đĩa được triển khai trong sản phẩm. Các phương pháp nén dữ liệu khác (qua mạng, trong bộ nhớ), dù được triển khai hay không, sẽ vẫn nằm ngoài phạm vi.

Vì vậy, khi bật chế độ lưu giữ lâu dài, do có sự thay đổi về dữ liệu trong bộ đệm, Ignite bắt đầu ghi vào đĩa:

  1. Nội dung của bộ nhớ đệm
  2. Nhật ký viết trước (sau đây gọi tắt là WAL)

Đã có một cơ chế nén WAL từ khá lâu, được gọi là nén WAL. Apache Ignite 2.8 được phát hành gần đây đã giới thiệu thêm hai cơ chế cho phép bạn nén dữ liệu trên đĩa: nén trang đĩa để nén nội dung của bộ đệm và nén ảnh chụp nhanh trang WAL để nén một số mục WAL. Thông tin chi tiết về cả ba cơ chế này dưới đây.

Nén trang đĩa

Làm thế nào nó hoạt động

Đầu tiên, chúng ta hãy xem xét ngắn gọn cách Ignite lưu trữ dữ liệu. Bộ nhớ trang được sử dụng để lưu trữ. Kích thước trang được đặt ở đầu nút và không thể thay đổi ở các giai đoạn sau; ngoài ra, kích thước trang phải là lũy thừa của hai và bội số của kích thước khối hệ thống tệp. Các trang được tải vào RAM từ đĩa khi cần; kích thước dữ liệu trên đĩa có thể vượt quá dung lượng RAM được phân bổ. Nếu không có đủ dung lượng trong RAM để tải một trang từ đĩa, các trang cũ, không còn được sử dụng sẽ bị xóa khỏi RAM.

Dữ liệu được lưu trữ trên đĩa theo dạng sau: một tệp riêng biệt được tạo cho mỗi phân vùng của từng nhóm bộ đệm; trong tệp này, các trang xuất hiện lần lượt theo thứ tự chỉ mục tăng dần. Mã định danh trang đầy đủ chứa mã định danh nhóm bộ đệm, số phân vùng và chỉ mục trang trong tệp. Do đó, bằng cách sử dụng mã định danh toàn trang, chúng ta có thể xác định duy nhất tệp và phần bù trong tệp cho mỗi trang. Bạn có thể đọc thêm về bộ nhớ phân trang trong bài viết Apache Ignite Wiki: Đốt cháy cửa hàng liên tục - dưới mui xe.

Cơ chế nén trang đĩa, như bạn có thể đoán từ tên, hoạt động ở cấp độ trang. Khi cơ chế này được bật, dữ liệu trong RAM sẽ được xử lý nguyên trạng mà không cần nén, nhưng khi các trang được lưu từ RAM vào đĩa, chúng sẽ bị nén.

Nhưng việc nén từng trang riêng lẻ không phải là giải pháp cho vấn đề; bạn cần bằng cách nào đó giảm kích thước của tệp dữ liệu thu được. Nếu kích thước trang không còn cố định, chúng ta không thể ghi lần lượt các trang vào tệp vì điều này có thể tạo ra một số vấn đề:

  • Sử dụng chỉ mục trang, chúng tôi sẽ không thể tính toán phần bù mà nó nằm trong tệp.
  • Không rõ phải làm gì với các trang không nằm ở cuối tệp và thay đổi kích thước của chúng. Nếu kích thước trang giảm, không gian được giải phóng sẽ biến mất. Nếu kích thước trang tăng lên, bạn cần tìm một vị trí mới trong tệp cho nó.
  • Nếu một trang di chuyển theo một số byte không phải là bội số của kích thước khối hệ thống tệp thì việc đọc hoặc ghi trang đó sẽ yêu cầu chạm vào thêm một khối hệ thống tệp, điều này có thể dẫn đến suy giảm hiệu suất.

Để tránh giải quyết những vấn đề này ở cấp độ riêng của nó, tính năng nén trang đĩa trong Apache Ignite sử dụng cơ chế hệ thống tệp được gọi là tệp thưa thớt. Tệp thưa thớt là tệp trong đó một số vùng chứa XNUMX có thể được đánh dấu là "lỗ hổng". Trong trường hợp này, sẽ không có khối hệ thống tệp nào được phân bổ để lưu trữ các lỗ hổng này, giúp tiết kiệm dung lượng ổ đĩa.

Điều hợp lý là để giải phóng khối hệ thống tệp, kích thước của lỗ phải lớn hơn hoặc bằng khối hệ thống tệp, điều này đặt ra một giới hạn bổ sung đối với kích thước trang và Apache Ignite: để việc nén có bất kỳ tác dụng nào, kích thước trang phải lớn hơn kích thước của khối hệ thống tệp. Nếu kích thước trang bằng kích thước khối thì chúng ta sẽ không bao giờ có thể giải phóng một khối đơn lẻ, vì để giải phóng một khối đơn lẻ, trang nén phải chiếm 0 byte. Nếu kích thước trang bằng kích thước của 2 hoặc 4 khối, chúng tôi sẽ có thể giải phóng ít nhất một khối nếu trang của chúng tôi được nén tương ứng ít nhất 50% hoặc 75%.

Do đó, mô tả cuối cùng về cách thức hoạt động của cơ chế: Khi ghi một trang vào đĩa, một nỗ lực được thực hiện để nén trang đó. Nếu kích thước của trang nén cho phép giải phóng một hoặc nhiều khối hệ thống tệp thì trang đó sẽ được ghi ở dạng nén và một “lỗ” được tạo thay cho các khối được giải phóng (một lệnh gọi hệ thống được thực hiện fallocate() với cờ đục lỗ). Nếu kích thước của trang nén không cho phép giải phóng các khối thì trang đó sẽ được lưu nguyên trạng, không bị nén. Tất cả độ lệch trang được tính theo cách tương tự như không nén, bằng cách nhân chỉ mục trang với kích thước trang. Không cần phải tự mình di chuyển các trang. Độ lệch trang, giống như khi không nén, nằm trong ranh giới của các khối hệ thống tệp.

Nén dữ liệu trong Apache Ignite. Kinh nghiệm của Sber

Trong quá trình triển khai hiện tại, Ignite chỉ có thể hoạt động với các tệp thưa thớt trong HĐH Linux; do đó, tính năng nén trang đĩa chỉ có thể được bật khi sử dụng Ignite trên hệ điều hành này.

Các thuật toán nén có thể được sử dụng để nén trang đĩa: ZSTD, LZ4, Snappy. Ngoài ra, còn có một chế độ hoạt động (SKIP_GARBAGE), trong đó chỉ không gian chưa sử dụng trên trang được loại bỏ mà không áp dụng nén đối với dữ liệu còn lại, giúp giảm tải cho CPU so với các thuật toán được liệt kê trước đó.

Tác động hiệu suất

Thật không may, tôi đã không tiến hành đo lường hiệu suất thực tế trên khán đài thực, vì chúng tôi không có kế hoạch sử dụng cơ chế này trong sản xuất, nhưng về mặt lý thuyết, chúng tôi có thể suy đoán xem chúng tôi sẽ thua ở đâu và sẽ thắng ở đâu.

Để làm điều này, chúng ta cần nhớ cách các trang được đọc và viết khi được truy cập:

  • Khi thực hiện thao tác đọc, đầu tiên nó sẽ được tìm kiếm trong RAM; nếu tìm kiếm không thành công, trang sẽ được tải vào RAM từ đĩa theo cùng một luồng thực hiện thao tác đọc.
  • Khi thao tác ghi được thực hiện, trang trong RAM bị đánh dấu là bẩn nhưng trang đó không được lưu vật lý vào đĩa ngay lập tức bởi luồng thực hiện thao tác ghi. Tất cả các trang bẩn sẽ được lưu vào đĩa sau này trong quy trình điểm kiểm tra trong các luồng riêng biệt.

Vì vậy, tác động đến hoạt động đọc là:

  • Tích cực (IO đĩa), do số lượng khối hệ thống tệp đọc giảm.
  • Tiêu cực (CPU), do hệ điều hành yêu cầu tải bổ sung để làm việc với các tệp thưa thớt. Cũng có thể các thao tác IO bổ sung sẽ ngầm xuất hiện ở đây để lưu cấu trúc tệp thưa phức tạp hơn (tiếc là tôi không quen với tất cả các chi tiết về cách thức hoạt động của tệp thưa).
  • Tiêu cực (CPU), do cần phải giải nén trang.
  • Không có tác động đến hoạt động ghi.
  • Tác động đến quá trình checkpoint (mọi thứ ở đây đều tương tự như thao tác đọc):
  • Tích cực (IO đĩa), do số lượng khối hệ thống tệp được ghi giảm.
  • Âm tính (CPU, có thể là IO đĩa), do làm việc với các tệp thưa thớt.
  • Tiêu cực (CPU), do nhu cầu nén trang.

Bên nào của cân sẽ làm nghiêng cân? Tất cả điều này phụ thuộc rất nhiều vào môi trường, nhưng tôi có xu hướng tin rằng việc nén trang đĩa rất có thể sẽ dẫn đến suy giảm hiệu suất trên hầu hết các hệ thống. Hơn nữa, các thử nghiệm trên các DBMS khác sử dụng cách tiếp cận tương tự với các tệp thưa thớt cho thấy hiệu suất giảm khi bật tính năng nén.

Cách kích hoạt và cấu hình

Như đã đề cập ở trên, phiên bản tối thiểu của Apache Ignite hỗ trợ nén trang đĩa là 2.8 và chỉ hỗ trợ hệ điều hành Linux. Kích hoạt và cấu hình như sau:

  • Phải có mô-đun nén-đánh lửa trong đường dẫn lớp. Theo mặc định, nó nằm trong bản phân phối Apache Ignite trong thư mục libs/tuỳ chọn và không được bao gồm trong đường dẫn lớp. Bạn có thể chỉ cần di chuyển thư mục lên một cấp tới libs và sau đó khi bạn chạy nó qua Ignition.sh, nó sẽ tự động được bật.
  • Persistence phải được kích hoạt (Enabled via DataRegionConfiguration.setPersistenceEnabled(true)).
  • Kích thước trang phải lớn hơn kích thước khối hệ thống tệp (bạn có thể đặt nó bằng cách sử dụng DataStorageConfiguration.setPageSize() ).
  • Đối với mỗi bộ đệm có dữ liệu cần được nén, bạn phải định cấu hình phương thức nén và (tùy chọn) mức độ nén (các phương thức CacheConfiguration.setDiskPageCompression() , CacheConfiguration.setDiskPageCompressionLevel()).

nén WAL

Làm thế nào nó hoạt động

WAL là gì và tại sao nó lại cần thiết? Rất ngắn gọn: đây là nhật ký chứa tất cả các sự kiện cuối cùng làm thay đổi việc lưu trữ trang. Nó chủ yếu cần thiết để có thể phục hồi trong trường hợp bị ngã. Bất kỳ thao tác nào, trước khi trao quyền kiểm soát cho người dùng, trước tiên phải ghi lại một sự kiện trong WAL, để trong trường hợp bị lỗi, nó có thể được phát lại trong nhật ký và khôi phục tất cả các thao tác mà người dùng đã nhận được phản hồi thành công, ngay cả khi các thao tác này không có thời gian để phản ánh trong bộ lưu trữ trang trên đĩa (đã ở trên. Nó đã được mô tả rằng việc ghi thực tế vào kho lưu trữ trang được thực hiện trong một quy trình gọi là "điểm kiểm tra" với một số độ trễ bởi các luồng riêng biệt).

Các mục trong WAL được chia thành logic và vật lý. Bản thân các giá trị Boolean là khóa và giá trị. Vật lý - phản ánh những thay đổi đối với các trang trong cửa hàng trang. Mặc dù các bản ghi logic có thể hữu ích trong một số trường hợp khác, nhưng bản ghi vật lý chỉ cần thiết để phục hồi trong trường hợp xảy ra sự cố và các bản ghi chỉ cần thiết kể từ điểm kiểm tra thành công cuối cùng. Ở đây chúng tôi sẽ không đi vào chi tiết và giải thích lý do tại sao nó hoạt động theo cách này, nhưng những ai quan tâm có thể tham khảo bài viết đã được đề cập trên Apache Ignite Wiki: Đốt cháy cửa hàng liên tục - dưới mui xe.

Thường có một số bản ghi vật lý cho mỗi bản ghi logic. Ví dụ, một thao tác đưa vào bộ đệm sẽ ảnh hưởng đến một số trang trong bộ nhớ trang (một trang có chính dữ liệu đó, các trang có chỉ mục, các trang có danh sách trống). Trong một số thử nghiệm tổng hợp, tôi nhận thấy các bản ghi vật lý chiếm tới 90% tệp WAL. Tuy nhiên, chúng chỉ cần thiết trong thời gian rất ngắn (theo mặc định, khoảng cách giữa các điểm kiểm tra là 3 phút). Sẽ là hợp lý nếu loại bỏ dữ liệu này sau khi mất đi tính liên quan của nó. Đây chính xác là những gì cơ chế nén WAL thực hiện: nó loại bỏ các bản ghi vật lý và nén các bản ghi logic còn lại bằng zip, trong khi kích thước tệp giảm rất đáng kể (đôi khi hàng chục lần).

Về mặt vật lý, WAL bao gồm một số phân đoạn (10 theo mặc định) có kích thước cố định (64MB theo mặc định), được ghi đè theo kiểu vòng tròn. Ngay sau khi phân đoạn hiện tại được lấp đầy, phân đoạn tiếp theo sẽ được chỉ định là phân đoạn hiện tại và phân đoạn đã lấp đầy sẽ được sao chép vào kho lưu trữ bằng một luồng riêng biệt. Tính năng nén WAL đã hoạt động với các phân đoạn lưu trữ. Ngoài ra, dưới dạng một luồng riêng biệt, nó giám sát việc thực thi điểm kiểm tra và bắt đầu nén trong các phân đoạn lưu trữ mà bản ghi vật lý không còn cần thiết nữa.

Nén dữ liệu trong Apache Ignite. Kinh nghiệm của Sber

Tác động hiệu suất

Vì quá trình nén WAL chạy dưới dạng một luồng riêng biệt nên sẽ không có tác động trực tiếp đến các hoạt động đang được thực hiện. Nhưng nó vẫn đặt thêm tải nền lên CPU (nén) và đĩa (đọc từng phân đoạn WAL từ kho lưu trữ và ghi các phân đoạn đã nén), vì vậy nếu hệ thống đang chạy ở công suất tối đa, nó cũng sẽ dẫn đến suy giảm hiệu suất.

Cách kích hoạt và cấu hình

Bạn có thể kích hoạt tính năng nén WAL bằng thuộc tính WalCompactionEnabled в DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). Ngoài ra, bằng cách sử dụng phương thức DataStorageConfiguration.setWalCompactionLevel(), bạn có thể đặt mức nén nếu không hài lòng với giá trị mặc định (BEST_SPEED).

Nén ảnh chụp nhanh trang WAL

Làm thế nào nó hoạt động

Chúng tôi đã phát hiện ra rằng các bản ghi WAL được chia thành logic và vật lý. Đối với mỗi thay đổi đối với mỗi trang, một bản ghi WAL vật lý sẽ được tạo trong bộ nhớ trang. Ngược lại, các bản ghi vật lý cũng được chia thành 2 loại phụ: bản ghi chụp nhanh trang và bản ghi delta. Mỗi khi chúng tôi thay đổi nội dung nào đó trên một trang và chuyển nó từ trạng thái sạch sang trạng thái bẩn, một bản sao hoàn chỉnh của trang này sẽ được lưu trữ trong WAL (bản ghi ảnh chụp nhanh trang). Ngay cả khi chúng tôi chỉ thay đổi một byte trong WAL, bản ghi sẽ lớn hơn kích thước trang một chút. Nếu chúng tôi thay đổi điều gì đó trên một trang vốn đã bẩn, một bản ghi delta sẽ được hình thành trong WAL, bản ghi này chỉ phản ánh những thay đổi so với trạng thái trước đó của trang chứ không phản ánh toàn bộ trang. Vì việc đặt lại trạng thái của các trang từ bẩn sang sạch được thực hiện trong quá trình điểm kiểm tra, nên ngay sau khi bắt đầu điểm kiểm tra, hầu hết tất cả các bản ghi vật lý sẽ chỉ bao gồm ảnh chụp nhanh của các trang (vì tất cả các trang ngay sau khi bắt đầu điểm kiểm tra đều sạch) , sau đó khi chúng ta đến điểm kiểm tra tiếp theo, phần bản ghi delta bắt đầu tăng lên và đặt lại vào đầu điểm kiểm tra tiếp theo. Các phép đo trong một số thử nghiệm tổng hợp cho thấy tỷ lệ ảnh chụp nhanh trang trong tổng khối lượng bản ghi vật lý đạt 90%.

Ý tưởng nén ảnh chụp nhanh trang WAL là nén ảnh chụp nhanh trang bằng công cụ nén trang được tạo sẵn (xem nén trang đĩa). Đồng thời, trong WAL, các bản ghi được lưu tuần tự ở chế độ chỉ nối thêm và không cần liên kết các bản ghi với ranh giới của các khối hệ thống tệp, vì vậy ở đây, không giống như cơ chế nén trang đĩa, chúng ta không cần các tệp thưa thớt ở tất cả; theo đó, cơ chế này sẽ hoạt động không chỉ trên hệ điều hành Linux. Ngoài ra, việc chúng tôi có thể nén trang đến mức nào không còn quan trọng nữa. Ngay cả khi chúng tôi giải phóng 1 byte, đây đã là một kết quả tích cực và chúng tôi có thể lưu dữ liệu nén trong WAL, không giống như nén trang đĩa, nơi chúng tôi chỉ lưu trang nén nếu chúng tôi giải phóng nhiều hơn 1 khối hệ thống tệp.

Các trang là dữ liệu có khả năng nén cao, tỷ trọng của chúng trong tổng khối lượng WAL rất cao, do đó, không cần thay đổi định dạng tệp WAL, chúng ta có thể giảm đáng kể kích thước của nó. Việc nén, bao gồm cả các bản ghi logic, sẽ yêu cầu thay đổi định dạng và mất khả năng tương thích, chẳng hạn như đối với người tiêu dùng bên ngoài, những người có thể quan tâm đến các bản ghi logic, nhưng sẽ không dẫn đến giảm đáng kể kích thước tệp.

Giống như nén trang đĩa, nén ảnh chụp nhanh trang WAL có thể sử dụng thuật toán nén ZSTD, LZ4, Snappy, cũng như chế độ SKIP_GARBAGE.

Tác động hiệu suất

Không khó để nhận thấy rằng việc trực tiếp bật tính năng nén ảnh chụp nhanh trang WAL chỉ ảnh hưởng đến các luồng ghi dữ liệu vào bộ nhớ trang, tức là các luồng thay đổi dữ liệu trong bộ đệm. Việc đọc các bản ghi vật lý từ WAL chỉ xảy ra một lần, tại thời điểm nút được nâng lên sau một cú ngã (và chỉ khi nó rơi trong một điểm kiểm tra).

Điều này ảnh hưởng đến các luồng thay đổi dữ liệu theo cách sau: chúng tôi nhận được hiệu ứng tiêu cực (CPU) do cần phải nén trang mỗi lần trước khi ghi vào đĩa và hiệu ứng tích cực (đĩa IO) do giảm số lượng dữ liệu được viết. Theo đó, mọi thứ ở đây đều đơn giản: nếu hiệu suất hệ thống bị giới hạn bởi CPU, chúng tôi sẽ bị suy giảm nhẹ, nếu bị giới hạn bởi I/O đĩa, chúng tôi sẽ tăng lên.

Một cách gián tiếp, việc giảm kích thước WAL cũng ảnh hưởng (tích cực) các luồng chuyển các phân đoạn WAL vào luồng lưu trữ và nén WAL.

Các thử nghiệm hiệu suất thực tế trong môi trường của chúng tôi sử dụng dữ liệu tổng hợp cho thấy sự gia tăng nhẹ (thông lượng tăng 10%-15%, độ trễ giảm 10%-15%).

Cách kích hoạt và cấu hình

Phiên bản Apache Ignite tối thiểu: 2.8. Kích hoạt và cấu hình như sau:

  • Phải có mô-đun nén-đánh lửa trong đường dẫn lớp. Theo mặc định, nó nằm trong bản phân phối Apache Ignite trong thư mục libs/tuỳ chọn và không được bao gồm trong đường dẫn lớp. Bạn có thể chỉ cần di chuyển thư mục lên một cấp tới libs và sau đó khi bạn chạy nó qua Ignition.sh, nó sẽ tự động được bật.
  • Persistence phải được kích hoạt (Enabled via DataRegionConfiguration.setPersistenceEnabled(true)).
  • Chế độ nén phải được thiết lập bằng phương pháp DataStorageConfiguration.setWalPageCompression(), tính năng nén bị tắt theo mặc định (chế độ TẮT).
  • Tùy chọn, bạn có thể đặt mức nén bằng phương pháp DataStorageConfiguration.setWalPageCompression(), hãy xem javadoc để biết phương thức có giá trị hợp lệ cho từng chế độ.

Kết luận

Các cơ chế nén dữ liệu được xem xét trong Apache Ignite có thể được sử dụng độc lập với nhau, nhưng bất kỳ sự kết hợp nào giữa chúng cũng đều được chấp nhận. Hiểu cách chúng hoạt động sẽ cho phép bạn xác định mức độ phù hợp của chúng với nhiệm vụ của bạn trong môi trường của bạn và bạn sẽ phải hy sinh những gì khi sử dụng chúng. Nén trang đĩa được thiết kế để nén bộ nhớ chính và có thể cho tỷ lệ nén trung bình. Nén ảnh chụp nhanh trang WAL sẽ mang lại mức độ nén trung bình cho các tệp WAL và thậm chí rất có thể sẽ cải thiện hiệu suất. Việc nén WAL sẽ không có tác động tích cực đến hiệu suất nhưng sẽ giảm kích thước của tệp WAL nhiều nhất có thể bằng cách xóa các bản ghi vật lý.

Nguồn: www.habr.com

Thêm một lời nhận xét