Bài đăng trên blog này đã được xuất bản trên Hortonworks.com trước khi hợp nhất với Cloudera. Một số liên kết, tài nguyên hoặc tham chiếu có thể không còn chính xác.
Bài đăng trên blog này ban đầu xuất hiện ở đây và được sao chép lại toàn bộ tại đây.
HBase là một cơ sở dữ liệu phân tán được xây dựng dựa trên các khái niệm cốt lõi của một nhật ký ghi có thứ tự và một cây hợp nhất có cấu trúc nhật ký. Như với bất kỳ cơ sở dữ liệu nào, I / O được tối ưu hóa là một mối quan tâm quan trọng đối với HBase. Khi có thể, ưu tiên là không thực hiện bất kỳ I / O nào. Điều này có nghĩa là việc sử dụng bộ nhớ và cấu trúc bộ nhớ đệm là vô cùng quan trọng. Vì vậy, HBase duy trì hai cấu trúc bộ nhớ cache:“bộ nhớ lưu trữ” và “bộ nhớ đệm khối”. Bộ nhớ lưu trữ, được triển khai dưới dạng MemStore
, tích lũy các chỉnh sửa dữ liệu khi chúng nhận được, lưu chúng vào bộ nhớ (1). Bộ đệm ẩn khối, một triển khai của BlockCache
giao diện, giữ cho các khối dữ liệu thường trú trong bộ nhớ sau khi chúng được đọc.
MemStore
rất quan trọng để truy cập các chỉnh sửa gần đây. Không có MemStore
, việc truy cập dữ liệu đó khi nó được ghi vào nhật ký ghi sẽ yêu cầu các mục nhập đọc và giải mã hóa trở lại từ tệp đó, ít nhất là O(n)
hoạt động. Thay vào đó, MemStore
duy trì cấu trúc danh sách bỏ qua, có O(log n)
chi phí truy cập và không yêu cầu I / O đĩa. MemStore
Tuy nhiên, chỉ chứa một phần nhỏ dữ liệu được lưu trữ trong HBase.
Dịch vụ đọc từ BlockCache
là cơ chế chính mà qua đó HBase có thể phân phát các lần đọc ngẫu nhiên với độ trễ mili giây. Khi khối dữ liệu được đọc từ HDFS, khối dữ liệu đó sẽ được lưu vào bộ nhớ đệm trong BlockCache
. Các lần đọc tiếp theo của dữ liệu lân cận - dữ liệu từ cùng một khối - không phải chịu hình phạt I / O khi truy xuất lại dữ liệu đó từ đĩa (2). Nó là BlockCache
đó sẽ là trọng tâm còn lại của bài đăng này.
Các khối vào bộ nhớ cache
Trước khi hiểu BlockCache
, nó giúp hiểu chính xác “khối” HBase là gì. Trong ngữ cảnh HBase, một khối là một đơn vị I / O duy nhất. Khi ghi dữ liệu ra HFile, khối là đơn vị nhỏ nhất của dữ liệu được ghi. Tương tự như vậy, một khối duy nhất là lượng dữ liệu nhỏ nhất mà HBase có thể đọc lại từ một HFile. Hãy cẩn thận để không nhầm lẫn khối HBase với khối HDFS hoặc với các khối của hệ thống tệp bên dưới - tất cả chúng đều khác nhau (3).
Khối HBase có 4 loại:DATA
, META
, INDEX
và BLOOM
.
DATA
khối lưu trữ dữ liệu người dùng. Khi BLOCKSIZE
được chỉ định cho một họ cột, nó là một gợi ý cho loại khối này. Phiền bạn, đó chỉ là một gợi ý. Trong khi xả MemStore
, HBase sẽ cố gắng hết sức để tôn trọng tôn chỉ này. Sau mỗi Cell
được viết, người viết sẽ kiểm tra xem số tiền được ghi có phải là> =target BLOCKSIZE
không . Nếu vậy, nó sẽ đóng khối hiện tại và bắt đầu khối tiếp theo (4).
INDEX
và BLOOM
các khối phục vụ cùng một mục tiêu; cả hai đều được sử dụng để tăng tốc đường dẫn đọc. INDEX
các khối cung cấp chỉ mục trên Cell
được chứa trong DATA
các khối. BLOOM
các khối chứa một bộ lọc nở trên cùng một dữ liệu. Chỉ mục cho phép người đọc nhanh chóng biết vị trí của một Cell
nên được lưu trữ. Bộ lọc cho người đọc biết khi nào một Cell
chắc chắn không có trong dữ liệu.
Cuối cùng, META
khối lưu trữ thông tin về chính HFile và thông tin lặt vặt khác - siêu dữ liệu, như bạn có thể mong đợi. Tổng quan toàn diện hơn về các định dạng HFile và vai trò của các loại khối khác nhau được cung cấp trong Apache HBase I / O - HFile.
HBase BlockCache và các triển khai của nó
Có một BlockCache
duy nhất ví dụ trong máy chủ khu vực, có nghĩa là tất cả dữ liệu từ tất cả các khu vực được máy chủ đó lưu trữ đều chia sẻ cùng một nhóm bộ nhớ cache (5). BlockCache
được khởi tạo khi khởi động máy chủ khu vực và được giữ lại trong toàn bộ thời gian của quá trình. Theo truyền thống, HBase chỉ cung cấp một BlockCache
duy nhất triển khai:LruBlockCache
. Bản phát hành 0.92 đã giới thiệu giải pháp thay thế đầu tiên trong HBASE-4027:SlabCache
. HBase 0.96 đã giới thiệu một tùy chọn khác thông qua HBASE-7404, được gọi là BucketCache
.
Sự khác biệt chính giữa LruBlockCache
đã thử và đúng và những lựa chọn thay thế này là cách chúng quản lý bộ nhớ. Cụ thể, LruBlockCache
là một cấu trúc dữ liệu nằm hoàn toàn trên JVM heap, trong khi hai cấu trúc kia có thể tận dụng bộ nhớ từ bên ngoài của JVM heap. Đây là một điểm khác biệt quan trọng vì bộ nhớ heap JVM được quản lý bởi JVM Garbage Collector, trong khi những bộ nhớ khác thì không. Trong các trường hợp của SlabCache
và BucketCache
, ý tưởng là giảm áp lực GC mà quy trình máy chủ khu vực gặp phải bằng cách giảm số lượng đối tượng được giữ lại trên heap.
LruBlockCache
Đây là cách triển khai mặc định. Các khối dữ liệu được lưu trong bộ nhớ cache trong JVM heap bằng cách sử dụng triển khai này. Nó được chia thành ba khu vực:truy cập một lần, đa truy cập và trong bộ nhớ. Các khu vực có kích thước là 25%, 50%, 25% của tổng số BlockCache
kích thước, tương ứng (6). Một khối ban đầu được đọc từ HDFS được đưa vào vùng truy cập đơn. Các truy cập liên tiếp thúc đẩy khối đó vào vùng đa truy cập. Vùng trong bộ nhớ được dành riêng cho các khối được tải từ các họ cột được gắn cờ là IN_MEMORY
. Bất kể khu vực nào, các khối cũ bị loại bỏ để nhường chỗ cho các khối mới bằng cách sử dụng thuật toán Ít được sử dụng gần đây, do đó là “Lru” trong “LruBlockCache”.
SlabCache
Việc triển khai này phân bổ các vùng bộ nhớ bên ngoài heap JVM bằng cách sử dụng DirectByteBuffer
S. Các khu vực này cung cấp phần thân của BlockCache
này . Khu vực chính xác mà một khối cụ thể sẽ được đặt dựa trên kích thước của khối. Theo mặc định, hai khu vực được phân bổ, chiếm lần lượt 80% và 20% tổng kích thước bộ đệm ẩn ngoài heap đã định cấu hình. Cái trước được sử dụng để lưu vào bộ đệm các khối có kích thước xấp xỉ kích thước khối đích (7). Cái sau chứa các khối có kích thước xấp xỉ gấp đôi kích thước khối mục tiêu. Một khối được đặt vào khu vực nhỏ nhất mà nó có thể phù hợp. Nếu bộ nhớ cache gặp một khối lớn hơn có thể vừa với một trong hai khu vực, khối đó sẽ không được lưu vào bộ nhớ đệm. Giống như LruBlockCache
, việc loại bỏ khối được quản lý bằng thuật toán LRU.
BucketCache
Việc triển khai này có thể được định cấu hình để hoạt động ở một trong ba chế độ khác nhau:heap
, offheap
và file
. Bất kể chế độ hoạt động, BucketCache
quản lý các vùng bộ nhớ được gọi là “thùng” để chứa các khối được lưu trong bộ nhớ cache. Mỗi nhóm được tạo với một kích thước khối mục tiêu. heap
việc triển khai tạo ra các nhóm đó trên JVM heap; offheap
triển khai sử dụng DirectByteByffers
để quản lý các nhóm bên ngoài JVM heap; file
chế độ mong đợi một đường dẫn đến một tệp trên hệ thống tệp trong đó các nhóm được tạo. file
chế độ này được thiết kế để sử dụng với kho lưu trữ sao lưu có độ trễ thấp - hệ thống tệp trong bộ nhớ hoặc có thể là tệp nằm trên bộ lưu trữ SSD (8). Bất kể chế độ nào, BucketCache
tạo ra 14 cái xô có kích thước khác nhau. Nó sử dụng tần suất truy cập khối để thông báo việc sử dụng, giống như LruBlockCache
và có cùng phân tích truy cập một lần, đa truy cập và trong bộ nhớ là 25%, 50%, 25%. Cũng giống như bộ nhớ đệm mặc định, việc loại bỏ khối được quản lý bằng thuật toán LRU.
Bộ nhớ đệm nhiều cấp độ
Cả SlabCache
và BucketCache
được thiết kế để sử dụng như một phần của chiến lược bộ nhớ đệm nhiều cấp. Do đó, một số phần trong tổng số BlockCache
kích thước được phân bổ cho một LruBlockCache
ví dụ. Phiên bản này hoạt động như bộ đệm ẩn cấp đầu tiên, “L1”, trong khi phiên bản bộ đệm khác được coi là bộ đệm ẩn cấp thứ hai, “L2”. Tuy nhiên, sự tương tác giữa LruBlockCache
và SlabCache
khác với LruBlockCache
như thế nào và BucketCache
tương tác.
SlabCache
chiến lược, được gọi là DoubleBlockCache
, là luôn lưu trữ các khối trong bộ đệm L1 và L2. Hai cấp bộ đệm hoạt động độc lập:cả hai đều được kiểm tra khi truy xuất một khối và mỗi loại bỏ khối mà không quan tâm đến khối kia. BucketCache
chiến lược, được gọi là CombinedBlockCache
, sử dụng bộ đệm L1 dành riêng cho khối Bloom và Index. Các khối dữ liệu được gửi trực tiếp đến bộ đệm L2. Trong trường hợp loại bỏ khối L1, thay vì bị loại bỏ hoàn toàn, khối đó sẽ bị hạ cấp xuống bộ nhớ cache L2.
Chọn cái nào?
Có hai lý do để xem xét bật một trong những BlockCache
thay thế triển khai. Đầu tiên chỉ đơn giản là dung lượng RAM bạn có thể dành cho máy chủ khu vực. Sự khôn ngoan của cộng đồng nhận ra giới hạn trên của heap JVM, theo như máy chủ khu vực có liên quan, nằm trong khoảng từ 14GB đến 31GB (9). Giới hạn chính xác thường phụ thuộc vào sự kết hợp của cấu hình phần cứng, cấu hình cụm, hình dạng của bảng dữ liệu và các mẫu truy cập ứng dụng. Bạn sẽ biết mình đã bước vào vùng nguy hiểm khi GC tạm dừng và RegionTooBusyException
s bắt đầu làm đầy nhật ký của bạn.
Thời điểm khác để xem xét một bộ nhớ cache thay thế là khi độ trễ phản hồi thực sự các vấn đề. Giữ khoảng 8-12GB cho phép bộ thu CMS chạy rất trơn tru (10), điều này có thể đo lường tác động đến phân vị thứ 99 của thời gian phản hồi. Với hạn chế này, các lựa chọn duy nhất là khám phá một bộ thu gom rác thay thế hoặc thực hiện một trong các triển khai off-heap này để quay vòng.
Tùy chọn thứ hai này chính xác là những gì tôi đã làm. Trong bài đăng tiếp theo, tôi sẽ chia sẻ một số kết quả thử nghiệm phi khoa học nhưng đầy đủ thông tin, nơi tôi so sánh thời gian phản hồi cho các BlockCache
khác nhau triển khai.
Như mọi khi, hãy theo dõi và tiếp tục với HBase!
1:MemStore
tích lũy các chỉnh sửa dữ liệu khi chúng nhận được, lưu chúng vào bộ nhớ. Điều này phục vụ hai mục đích:nó tăng tổng lượng dữ liệu được ghi vào đĩa trong một thao tác đơn lẻ và nó giữ lại những thay đổi gần đây trong bộ nhớ để truy cập tiếp theo dưới dạng đọc có độ trễ thấp. Điều thứ nhất là quan trọng vì nó giữ cho các khối ghi HBase gần như đồng bộ với kích thước khối HDFS, điều chỉnh các mẫu truy cập HBase với bộ lưu trữ HDFS bên dưới. Sau đó là tự giải thích, tạo điều kiện thuận lợi cho các yêu cầu đọc đối với dữ liệu được viết gần đây. Cần chỉ ra rằng cấu trúc này không liên quan đến độ bền của dữ liệu. Các chỉnh sửa cũng được ghi vào nhật ký ghi theo thứ tự, HLog
, liên quan đến hoạt động nối thêm HDFS ở một khoảng thời gian có thể định cấu hình, thường là ngay lập tức.
2:Đọc lại dữ liệu từ hệ thống tệp cục bộ là trường hợp tốt nhất. HDFS xét cho cùng là một hệ thống tệp phân tán, vì vậy trường hợp xấu nhất yêu cầu đọc khối đó qua mạng. HBase cố gắng hết sức để duy trì địa phương dữ liệu. Hai bài viết này cung cấp một cái nhìn sâu sắc về vị trí dữ liệu có ý nghĩa như thế nào đối với HBase và cách nó được quản lý.
3:Các khối hệ thống tệp, HDFS và HBase đều khác nhau nhưng có liên quan với nhau. Hệ thống con I / O hiện đại là nhiều lớp trừu tượng nằm trên lớp trừu tượng. Cốt lõi của sự trừu tượng đó là khái niệm về một đơn vị dữ liệu duy nhất, được gọi là “khối”. Do đó, cả ba lớp lưu trữ này đều xác định khối riêng của chúng, mỗi khối có kích thước riêng. Nói chung, kích thước khối lớn hơn có nghĩa là thông lượng truy cập tuần tự tăng lên. Kích thước khối nhỏ hơn tạo điều kiện truy cập ngẫu nhiên nhanh hơn.
4:Đặt BLOCKSIZE
kiểm tra sau khi dữ liệu được ghi có hai phân nhánh. Một Cell
duy nhất là đơn vị dữ liệu nhỏ nhất được ghi vào DATA
khối. Nó cũng có nghĩa là một Cell
không thể kéo dài nhiều khối.
5:Điều này khác với MemStore
, trong đó có một phiên bản riêng cho mọi khu vực do máy chủ khu vực lưu trữ.
6:Cho đến rất gần đây, các phân vùng bộ nhớ này đã được định nghĩa tĩnh; không có cách nào để ghi đè phân chia 25/50/25. Ví dụ:một phân khúc nhất định, khu vực đa truy cập, có thể phát triển lớn hơn mức phân bổ 50% của nó miễn là các khu vực khác được sử dụng chưa đầy đủ. Việc sử dụng gia tăng trong các khu vực khác sẽ loại bỏ các mục nhập khỏi khu vực đa truy cập cho đến khi đạt được số dư 25/50/25. Nhà điều hành không thể thay đổi các kích thước mặc định này. HBASE-10263, vận chuyển trong HBase 0.98.0, giới thiệu các thông số cấu hình cho các kích thước này. Hành vi linh hoạt được giữ lại.
7:Công việc kinh doanh “xấp xỉ” là cho phép một số phòng lung tung trong các kích thước khối. Kích thước khối HBase là một mục tiêu hoặc gợi ý thô, không phải là một ràng buộc được thực thi nghiêm ngặt. Kích thước chính xác của bất kỳ khối dữ liệu cụ thể nào sẽ phụ thuộc vào kích thước khối đích và kích thước của Cell
các giá trị có trong đó. Gợi ý kích thước khối được chỉ định làm kích thước khối mặc định là 64kb.
8:Sử dụng BucketCache
trong tệp file
chế độ với một cửa hàng hỗ trợ liên tục có một lợi ích khác:tính bền bỉ. Khi khởi động, nó sẽ tìm kiếm dữ liệu hiện có trong bộ nhớ cache và xác minh tính hợp lệ của nó.
9:Theo tôi hiểu, có hai thành phần đưa ra giới hạn trên cho phạm vi này. Đầu tiên là giới hạn về khả năng địa chỉ đối tượng JVM. JVM có thể tham chiếu đến một đối tượng trên heap với địa chỉ tương đối 32 bit thay vì địa chỉ gốc 64 bit đầy đủ. Tối ưu hóa này chỉ có thể thực hiện được nếu tổng kích thước heap nhỏ hơn 32GB. Xem phần Lỗi được nén để biết thêm chi tiết. Thứ hai là khả năng của bộ thu gom rác để theo kịp với số lượng đối tượng khuấy trong hệ thống. Từ những gì tôi có thể nói, ba nguồn của đối tượng churn là MemStore
, BlockCache
và các hoạt động mạng. Đầu tiên được giảm thiểu bởi MemSlab
, được bật theo mặc định. Thứ hai bị ảnh hưởng bởi kích thước tập dữ liệu của bạn so với kích thước của bộ nhớ cache. Điều thứ ba không thể được thực hiện miễn là HBase sử dụng ngăn xếp mạng dựa trên bản sao dữ liệu.
10:Cũng giống như 8, điều này được giả định là "phần cứng hiện đại". Các tương tác ở đây khá phức tạp và vượt ra ngoài phạm vi của một bài đăng trên blog.