HBase
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> HBase

Điều chỉnh Bộ sưu tập rác Java cho HBase

Bài đăng của khách này từ kiến ​​trúc sư hiệu suất Intel Java Eric Kaczmarek (ban đầu được xuất bản tại đây) khám phá cách điều chỉnh bộ thu thập rác Java (GC) cho Apache HBase tập trung vào 100% lượt đọc YCSB.

Apache HBase là một dự án mã nguồn mở Apache cung cấp khả năng lưu trữ dữ liệu NoSQL. Thường được sử dụng cùng với HDFS, HBase được sử dụng rộng rãi trên toàn thế giới. Người dùng nổi tiếng bao gồm Facebook, Twitter, Yahoo, v.v. Từ quan điểm của nhà phát triển, HBase là “cơ sở dữ liệu phân tán, được tạo phiên bản, không quan hệ được mô phỏng theo Bigtable của Google, một hệ thống lưu trữ phân tán cho dữ liệu có cấu trúc”. HBase có thể dễ dàng xử lý thông lượng rất cao bằng cách mở rộng quy mô (tức là triển khai trên một máy chủ lớn hơn) hoặc mở rộng quy mô (tức là triển khai trên nhiều máy chủ hơn).

Theo quan điểm của người dùng, độ trễ cho mỗi truy vấn đơn lẻ rất quan trọng. Khi chúng tôi làm việc với người dùng để kiểm tra, điều chỉnh và tối ưu hóa khối lượng công việc của HBase, chúng tôi gặp phải một số lượng đáng kể hiện thực sự muốn độ trễ hoạt động theo phân vị thứ 99. Điều đó có nghĩa là một chuyến đi khứ hồi, từ yêu cầu của khách hàng đến phản hồi lại cho khách hàng, tất cả trong vòng 100 mili giây.

Một số yếu tố góp phần làm thay đổi độ trễ. Một trong những kẻ xâm nhập có độ trễ không thể đoán trước và tàn khốc nhất là chương trình “dừng thế giới” của Máy ảo Java (JVM’s) tạm dừng để thu gom rác (dọn dẹp bộ nhớ).

Để giải quyết vấn đề đó, chúng tôi đã thử một số thử nghiệm bằng cách sử dụng Oracle jdk7u21 và jdk7u60 G1 (Garbage 1st). Hệ thống máy chủ chúng tôi sử dụng dựa trên bộ xử lý Intel Xeon Ivy-bridge EP với Siêu phân luồng (40 bộ xử lý logic). Nó có RAM 256GB DDR3-1600 và ba ổ SSD 400GB làm bộ nhớ cục bộ. Thiết lập nhỏ này chứa một chủ và một phụ, được định cấu hình trên một nút duy nhất với tải được chia tỷ lệ thích hợp. Chúng tôi đã sử dụng HBase phiên bản 0.98.1 và hệ thống tệp cục bộ để lưu trữ HFile. Bảng kiểm tra HBase được định cấu hình thành 400 triệu hàng và nó có kích thước 580GB. Chúng tôi đã sử dụng chiến lược heap HBase mặc định:40% cho blockcache, 40% cho memstore. YCSB được sử dụng để thúc đẩy 600 luồng công việc gửi yêu cầu đến máy chủ HBase.

Biểu đồ sau cho thấy jdk7u21 đang chạy 100% đọc trong một giờ bằng cách sử dụng -XX:+UseG1GC -Xms100g -Xmx100g -XX:MaxGCPauseMillis=100 . Chúng tôi đã chỉ định bộ thu gom rác để sử dụng, kích thước đống và thời gian tạm dừng thu gom rác (GC) mong muốn "dừng cả thế giới".

Hình 1:Biến động dữ dội trong thời gian Tạm dừng GC

Trong trường hợp này, chúng tôi nhận được rất nhiều lần dừng GC. Thời gian tạm dừng GC có phạm vi từ 7 mili giây đến 5 giây đầy đủ sau mức tăng đột biến ban đầu lên tới 17,5 giây.

Biểu đồ sau hiển thị thêm chi tiết, trong trạng thái ổn định:

Hình 2:Chi tiết tạm dừng GC, trong trạng thái ổn định

Hình 2 cho chúng ta biết thời gian tạm dừng GC thực sự có ba nhóm khác nhau:(1) từ 1 đến 1,5 giây; (2) từ 0,007 giây đến 0,5 giây; (3) tăng đột biến từ 1,5 giây đến 5 giây. Điều này rất lạ, vì vậy chúng tôi đã thử nghiệm jdk7u60 được phát hành gần đây nhất để xem liệu dữ liệu có khác gì không:

Chúng tôi đã chạy các bài kiểm tra đọc 100% giống nhau bằng cách sử dụng chính xác các thông số JVM giống nhau:-XX:+UseG1GC -Xms100g -Xmx100g -XX:MaxGCPauseMillis=100 .

Hình 3:Việc xử lý các mức tăng đột biến về thời gian tạm dừng được cải thiện đáng kể

Jdk7u60 đã cải thiện đáng kể khả năng của G1 trong việc xử lý các mức tăng thời gian tạm dừng sau khi tăng đột biến ban đầu trong giai đoạn ổn định. Jdk7u60 đã tạo ra 1029 GC trẻ và hỗn hợp trong một giờ chạy. GC xảy ra khoảng 3,5 giây một lần. Jdk7u21 tạo ra 286 GC với mỗi GC xảy ra khoảng 12,6 giây một lần. Jdk7u60 có thể quản lý thời gian tạm dừng từ 0,302 đến 1 giây mà không có đột biến lớn.

Hình 4 dưới đây cho chúng ta cái nhìn sâu hơn về 150 GC tạm dừng trong trạng thái ổn định:

Hình 4:Tốt hơn, nhưng chưa đủ tốt

Trong trạng thái ổn định, jdk7u60 có thể giữ thời gian tạm dừng trung bình khoảng 369 mili giây. Nó tốt hơn nhiều so với jdk7u21, nhưng nó vẫn không đáp ứng yêu cầu 100 mili giây của chúng tôi do –Xx:MaxGCPauseMillis=100 đưa ra .

Để xác định xem chúng tôi có thể làm gì khác để có thời gian tạm dừng 100 triệu giây, chúng tôi cần hiểu thêm về hoạt động của trình quản lý bộ nhớ của JVM và bộ thu gom rác G1 (Garbage First). Các số liệu sau đây cho thấy G1 hoạt động như thế nào trên bộ sưu tập Young Gen.

Hình 5:Trang trình bày từ bản trình bày JavaOne 2012 của Charlie Hunt và Monica Beckwith:“Điều chỉnh hiệu suất bộ thu gom rác G1”

Khi JVM khởi động, dựa trên các tham số khởi chạy JVM, nó yêu cầu hệ điều hành phân bổ một đoạn bộ nhớ liên tục lớn để lưu trữ JVM’s heap. Đoạn bộ nhớ đó được JVM phân vùng thành các vùng.

Hình 6:Trang trình bày từ bản trình bày JavaOne 2012 của Charlie Hunt và Monica Beckwith:“Điều chỉnh hiệu suất bộ thu gom rác G1”

Như Hình 6 cho thấy, mọi đối tượng mà chương trình Java phân bổ bằng cách sử dụng API Java trước tiên sẽ đến không gian Eden trong Thế hệ trẻ ở bên trái. Sau một thời gian, Eden trở nên đầy đủ và một GC thế hệ trẻ được kích hoạt. Các đối tượng vẫn được tham chiếu (tức là "sống") được sao chép vào không gian Survivor. Khi các đối tượng tồn tại một số GC trong Thế hệ trẻ, chúng sẽ được thăng cấp vào không gian Thế hệ cũ.

Khi Young GC xảy ra, các chuỗi của ứng dụng Java bị dừng lại để đánh dấu và sao chép các đối tượng trực tiếp một cách an toàn. Các điểm dừng này là điểm dừng GC khét tiếng “dừng lại trên toàn thế giới”, khiến các ứng dụng không phản hồi cho đến khi hết thời gian tạm dừng.

Hình 7:Trang trình bày từ bản trình bày JavaOne 2012 của Charlie Hunt và Monica Beckwith:“Điều chỉnh hiệu suất bộ thu gom rác G1”

Thế hệ cũ cũng có thể trở nên đông đúc. Ở một cấp độ nhất định — được kiểm soát bởi -XX:InitiatingHeapOccupancyPercent=? trong đó giá trị mặc định là 45% tổng số heap — một GC hỗn hợp được kích hoạt. Nó thu thập cả gen Trẻ và gen Cũ. Việc tạm dừng GC hỗn hợp được kiểm soát bởi thời gian thế hệ Trẻ mất để làm sạch khi GC hỗn hợp xảy ra.

Vì vậy, chúng ta có thể thấy trong G1, việc tạm dừng GC “dừng thế giới” bị chi phối bởi tốc độ G1 có thể đánh dấu và sao chép các đối tượng trực tiếp ra khỏi không gian Eden. Với suy nghĩ này, chúng tôi sẽ phân tích cách mô hình phân bổ bộ nhớ HBase sẽ giúp chúng tôi điều chỉnh G1 GC để có được khoảng dừng mong muốn trong 100 mili giây.

Trong HBase, có hai cấu trúc trong bộ nhớ sử dụng hầu hết đống của nó:BlockCache , các khối tệp HBase vào bộ nhớ đệm cho các thao tác đọc và bộ nhớ đệm của Memstore các bản cập nhật mới nhất.

Hình 8:Trong HBase, hai cấu trúc trong bộ nhớ sử dụng hầu hết đống của nó.

Việc triển khai mặc định BlockCache của HBase là LruBlockCache , chỉ đơn giản là sử dụng một mảng byte lớn để lưu trữ tất cả các khối HBase. Khi các khối "bị loại bỏ", tham chiếu đến đối tượng Java của khối đó sẽ bị xóa, cho phép GC định vị lại bộ nhớ.

Các đối tượng mới tạo thành LruBlockCacheMemstore đi đến không gian Eden của Thế hệ trẻ trước. Nếu chúng sống đủ lâu (tức là nếu chúng không bị đuổi khỏi LruBlockCache hoặc bị loại bỏ khỏi Memstore), sau đó sau một số thế hệ GC trẻ, chúng chuyển sang Thế hệ cũ của đống Java. Khi dung lượng trống của Thế hệ cũ ít hơn threshOld nhất định (InitiatingHeapOccupancyPercent để bắt đầu), GC hỗn hợp khởi động và xóa một số vật thể đã chết trong thế hệ Cũ, sao chép các vật thể sống từ thế hệ Trẻ và tính toán lại vườn địa đàng của thế hệ Trẻ và HeapOccupancyPercent của thế hệ cũ. . Cuối cùng, khi HeapOccupancyPercent đạt đến một cấp độ nhất định, FULL GC xảy ra, điều này khiến cho “thế giới dừng lại” rất lớn GC tạm dừng để dọn dẹp tất cả các vật thể đã chết bên trong Thế hệ cũ.

Sau khi nghiên cứu nhật ký GC được tạo bởi “-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy “, Chúng tôi nhận thấy HeapOccupancyPercent không bao giờ phát triển đủ lớn để tạo ra GC đầy đủ trong quá trình đọc HBase 100%. Các lần tạm dừng GC mà chúng tôi thấy bị chi phối bởi các lần tạm dừng thuộc thế hệ Young “dừng lại thế giới” và quá trình xử lý tham chiếu ngày càng tăng theo thời gian.

Sau khi hoàn thành phân tích đó, chúng tôi đã thực hiện ba nhóm thay đổi trong cài đặt G1 GC mặc định:

  1. Sử dụng -XX:+ParallelRefProcEnabled Khi cờ này được bật, GC sử dụng nhiều luồng để xử lý các tham chiếu ngày càng tăng trong GC trẻ và hỗn hợp. Với cờ này cho HBase, thời gian nhận lại GC giảm 75% và thời gian tạm dừng GC tổng thể giảm 30%.
  2. Set -XX:-ResizePLAB and -XX:ParallelGCThreads=8+(logical processors-8)(5/8) Bộ đệm Phân bổ Cục bộ Quảng cáo (PLAB) được sử dụng trong bộ sưu tập Trẻ. Nhiều chủ đề được sử dụng. Mỗi luồng có thể cần phân bổ không gian cho các đối tượng được sao chép trong không gian Sống sót hoặc Cũ. PLAB được yêu cầu để tránh sự cạnh tranh của các luồng đối với cấu trúc dữ liệu dùng chung quản lý bộ nhớ trống. Mỗi luồng GC có một PLAB cho không gian Sinh tồn và một cho không gian cũ. Chúng tôi muốn ngừng thay đổi kích thước PLAB để tránh chi phí truyền thông lớn giữa các luồng GC, cũng như các biến thể trong mỗi GC. Chúng tôi muốn sửa số lượng luồng GC thành kích thước được tính bằng 8+ (bộ xử lý logic-8) ( 5/8). Công thức này đã được Oracle đề xuất gần đây.
  3. Thay đổi -XX:G1NewSizePercent mặc định từ 5 đến 1 cho 100GB heap Dựa trên kết quả đầu ra từ -XX:+PrintGCDetails and -XX:+PrintAdaptiveSizePolicy , chúng tôi nhận thấy lý do khiến G1 không đáp ứng được thời gian tạm dừng 100GC mong muốn của chúng tôi là thời gian xử lý Eden. Nói cách khác, G1 mất trung bình 369 mili giây để làm trống 5GB Eden trong các thử nghiệm của chúng tôi. Sau đó, chúng tôi đã thay đổi kích thước Eden bằng cách sử dụng -XX:G1NewSizePercent=
    gắn cờ từ 5 xuống 1. Với thay đổi này, chúng tôi đã thấy thời gian tạm dừng của GC giảm xuống còn 100 mili giây.

Từ thử nghiệm này, chúng tôi phát hiện ra rằng tốc độ của G1 để làm sạch Eden là khoảng 1GB mỗi 100 mili giây hoặc 10GB mỗi giây đối với thiết lập HBase mà chúng tôi đã sử dụng.

Dựa trên tốc độ đó, chúng ta có thể đặt -XX:G1NewSizePercent=
vì vậy kích thước Eden có thể được giữ khoảng 1GB. Ví dụ:

  • heap 32GB, -XX:G1NewSizePercent=3
  • 64 GB heap, - XX:G1NewSizePercent=2
  • 100GB trở lên heap, -XX:G1NewSizePercent=1
  • Vì vậy, các tùy chọn dòng lệnh cuối cùng của chúng tôi cho HRegionserver là:
    • -XX:+UseG1GC
    • -Xms100g -Xmx100g (Kích thước đống được sử dụng trong các thử nghiệm của chúng tôi)
    • -XX:MaxGCPauseMillis=100 (Thời gian tạm dừng GC mong muốn trong các thử nghiệm)
    • - XX:+ParallelRefProcEnabled
    • -XX:-ResizePLAB
    • -XX:ParallelGCThreads= 8+(40-8)(5/8)=28
    • -XX:G1NewSizePercent=1

Đây là biểu đồ thời gian tạm dừng của GC để chạy hoạt động đọc 100% trong 1 giờ:

Hình 9:Các gai lắng ban đầu cao nhất đã giảm hơn một nửa.

Trong biểu đồ này, ngay cả các mức tăng đột biến ban đầu cao nhất cũng giảm từ 3,792 giây xuống 1,684 giây. Lần tăng đột biến ban đầu ít hơn 1 giây. Sau khi giải quyết xong, GC có thể giữ thời gian tạm dừng khoảng 100 mili giây.

Biểu đồ bên dưới so sánh jdk7u60 chạy có và không điều chỉnh, trong trạng thái ổn định:

Hình 10:jdk7u60 chạy có và không điều chỉnh, trong trạng thái ổn định.

Điều chỉnh GC đơn giản mà chúng tôi mô tả ở trên mang lại thời gian tạm dừng GC lý tưởng, khoảng 100 mili giây, với độ lệch chuẩn trung bình 106 mili giây và 7 mili giây.

Tóm tắt

HBase là một ứng dụng quan trọng về thời gian phản hồi, yêu cầu thời gian tạm dừng của GC phải có thể dự đoán và quản lý được. Với Oracle jdk7u60, dựa trên thông tin GC được báo cáo bởi -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy , chúng tôi có thể điều chỉnh thời gian tạm dừng GC xuống 100 mili giây mong muốn.

Eric Kaczmarek là một kiến ​​trúc sư hiệu suất Java trong Nhóm Giải pháp Phần mềm của Intel. Ông dẫn đầu nỗ lực tại Intel để kích hoạt và tối ưu hóa các khung Dữ liệu lớn (Hadoop, HBase, Spark, Cassandra) cho các nền tảng của Intel.

Phần mềm và khối lượng công việc được sử dụng trong các bài kiểm tra hiệu suất có thể chỉ được tối ưu hóa cho hiệu suất trên bộ vi xử lý Intel. Các bài kiểm tra hiệu suất, chẳng hạn như SYSmark và MobileMark, được đo bằng cách sử dụng các hệ thống máy tính, thành phần, phần mềm, hoạt động và chức năng cụ thể. Bất kỳ thay đổi nào đối với bất kỳ yếu tố nào trong số đó đều có thể khiến kết quả thay đổi. Bạn nên tham khảo thông tin khác và các bài kiểm tra hiệu suất để giúp bạn đánh giá đầy đủ các giao dịch mua dự kiến ​​của mình, bao gồm cả hiệu suất của sản phẩm đó khi kết hợp với các sản phẩm khác.

Số lượng bộ xử lý Intel không phải là thước đo hiệu năng. Số hiệu bộ xử lý phân biệt các tính năng trong mỗi họ bộ xử lý. Không qua các họ bộ xử lý khác nhau. Truy cập:http://www.intel.com/products/processor_number.

Bản quyền 2014 Intel Corp. Intel, logo Intel và Xeon là các nhãn hiệu của Intel Corporation tại Hoa Kỳ và / hoặc các quốc gia khác.


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Mã hóa xóa HDFS trong Big Data Hadoop

  2. Bảo mật cơ sở dữ liệu hoạt động - Phần 1

  3. Apache HBase Nên và Không nên

  4. HBase BlockCache 101

  5. nên HBase của bạn bị hỏng