Cảm ơn Pedro Boado và Abel Fernandez Alfonso từ nhóm kỹ sư của Santander vì sự cộng tác của họ trên bài đăng này về cách Santander UK đang sử dụng Apache HBase làm công cụ phục vụ gần như thời gian thực để cung cấp năng lượng cho ứng dụng Spendlytics sáng tạo của mình.
Ứng dụng Spendlytics dành cho iOS được thiết kế để giúp khách hàng ghi nợ cá nhân và thẻ tín dụng của Santander luôn cập nhật chi tiêu của họ, bao gồm cả các khoản thanh toán được thực hiện qua Apple Pay. Nó sử dụng dữ liệu giao dịch thời gian thực để cho phép khách hàng phân tích chi tiêu thẻ của họ trong các khoảng thời gian (hàng tuần, hàng tháng, hàng năm), theo danh mục (du lịch, siêu thị, tiền mặt, v.v.) và theo nhà bán lẻ.
Trong bài trước của chúng tôi, chúng tôi đã mô tả cách Apache Flume và Apache Kafka được sử dụng để chuyển đổi, phong phú hóa và truyền trực tuyến các giao dịch sang Apache HBase. Bài đăng này tiếp tục bằng cách mô tả cách các giao dịch được sắp xếp trong Apache HBase để tối ưu hóa hiệu suất và cách chúng tôi sử dụng bộ đồng xử lý để cung cấp tổng hợp xu hướng mua hàng theo từng khách hàng. Santander và Cloudera đã tiếp tục (và vẫn đang tiếp tục) hành trình HBase với Spendlytics, một hành trình đã chứng kiến nhiều lần lặp lại và tối ưu hóa thiết kế lược đồ và triển khai bộ đồng xử lý. Chúng tôi hy vọng rằng những bài học kinh nghiệm này là những điểm rút ra chính từ bài đăng này.
Lược đồ 1.0
Thiết kế lược đồ HBase tốt là hiểu các mẫu truy cập dự định. Làm đúng và HBase sẽ bay; sai và bạn có thể nhận được hiệu suất dưới mức tối ưu do sự cân bằng trong thiết kế như các điểm phát sóng trong khu vực hoặc phải thực hiện quét lớn trên nhiều vùng. (Một điểm phát sóng trong bảng HBase là nơi phân phối khóa hàng không đồng đều có thể khiến phần lớn các yêu cầu được chuyển đến một vùng duy nhất, lấn át Máy chủ vùng và dẫn đến thời gian phản hồi chậm.)
Những gì chúng tôi biết về các mẫu truy cập dự định của Spendlytics và cách nó ảnh hưởng đến thiết kế giản đồ ban đầu:
- Khách hàng chỉ phân tích các giao dịch trên tài khoản của chính họ:
- Để có hiệu suất quét tuyến tính nhanh, tất cả các giao dịch của khách hàng phải được lưu trữ tuần tự.
- ID khách hàng đang tăng lên một cách đơn điệu:
- ID khách hàng tuần tự làm tăng khả năng các khách hàng mới hơn sẽ ở cùng một khu vực trong cùng một khu vực, có khả năng tạo ra một điểm nóng trong khu vực. Để tránh vấn đề này, các ID khách hàng nên được tách riêng (tiền tố) hoặc đảo ngược để phân phối đồng đều giữa các khu vực khi được sử dụng ở đầu phím hàng.
- Khách hàng có nhiều thẻ
- Để tối ưu hóa quá trình quét, các giao dịch của khách hàng nên được nhóm lại và sắp xếp theo hợp đồng thẻ, tức là ID hợp đồng phải tạo thành một phần của khóa hàng.
- Các giao dịch sẽ được truy cập toàn bộ, tức là các thuộc tính như nhà bán lẻ, người bán, vị trí, đơn vị tiền tệ và số tiền không cần phải đọc riêng
- Việc lưu trữ các thuộc tính giao dịch trong các ô riêng biệt sẽ dẫn đến một bảng rộng hơn, thưa thớt hơn, điều này sẽ làm tăng thời gian tìm kiếm. Vì các thuộc tính sẽ được truy cập cùng nhau, nên tuần tự hóa chúng cùng nhau trong một bản ghi Apache Avro. Avro nhỏ gọn và cung cấp cho chúng tôi cách biểu diễn hiệu quả với khả năng phát triển giản đồ.
- Các giao dịch được truy cập riêng lẻ, theo lô (theo thời gian, danh mục và nhà bán lẻ) và tổng hợp (theo thời gian, danh mục và nhà bán lẻ).
- Việc thêm một ID giao dịch duy nhất làm bộ định tính cột sẽ cho phép truy xuất các giao dịch riêng lẻ mà không làm tăng thêm độ phức tạp cho khóa hàng.
- Để cho phép quét nhanh các giao dịch trong các khoảng thời gian thay đổi, dấu thời gian giao dịch phải là một phần của khóa hàng.
- Việc thêm danh mục và nhà bán lẻ vào khóa hàng có thể quá chi tiết và sẽ dẫn đến một bảng rất cao và hẹp với khóa hàng phức tạp. Cao và hẹp là được vì tính nguyên tử không phải là một vấn đề, nhưng có chúng làm tiêu chuẩn cột sẽ mở rộng bảng trong khi vẫn hỗ trợ tổng hợp thứ cấp.
- Dữ liệu xu hướng phải được tính toán trước càng nhiều càng tốt để tối ưu hóa hiệu suất đọc.
- Sau này sẽ có thêm thông tin về điều này, nhưng bây giờ hãy biết rằng chúng tôi đã thêm họ cột thứ hai để lưu trữ các xu hướng.
Dựa trên những điều trên, thiết kế lược đồ ban đầu được minh họa như sau:
Xu hướng Máy tính
Khía cạnh của thiết kế ban đầu mà chúng tôi học được nhiều nhất là xu hướng tính toán. Yêu cầu là cho phép khách hàng phân tích chi tiêu của họ theo danh mục và nhà bán lẻ theo giờ. Điểm dữ liệu bao gồm giá trị giao dịch nhỏ nhất và lớn nhất, tổng giá trị giao dịch và số lượng giao dịch. Thời gian phản hồi phải từ 200 mili giây trở xuống.
Xu hướng tính toán trước sẽ cung cấp cho chúng tôi thời gian phản hồi nhanh nhất vì vậy đây là cách tiếp cận đầu tiên của chúng tôi. Xu hướng không thể làm trễ các giao dịch vì vậy chúng phải được tính toán trên đường dẫn ghi. Điều này sẽ rất tốt cho hiệu suất đọc, nhưng đã đặt ra cho chúng tôi một số thách thức:cách tốt nhất để tổ chức các xu hướng trong HBase và cách tính toán chúng một cách nhanh chóng và đáng tin cậy mà không ảnh hưởng nghiêm trọng đến hiệu suất ghi.
Chúng tôi đã thử nghiệm với các thiết kế giản đồ khác nhau và cố gắng tận dụng một số thiết kế nổi tiếng nếu có thể (chẳng hạn như giản đồ của OpenTSDB). Sau một số lần lặp lại, chúng tôi quyết định thiết kế lược đồ được minh họa ở trên. Được lưu trữ trong bảng giao dịch, trong một họ cột riêng biệt, các giá trị xu hướng được sắp xếp cùng nhau trong một hàng duy nhất, với một hàng xu hướng cho mỗi khách hàng. Bằng cách đặt cho khóa hàng cùng một tiền tố như giao dịch của khách hàng (ví dụ:
<reverse_customer_id>::<contract_id>
) nó đảm bảo rằng hàng xu hướng sẽ được sắp xếp cùng với các bản ghi giao dịch của khách hàng tương ứng. Với ranh giới khu vực xác định và chính sách phân chia khu vực tùy chỉnh được áp dụng, chúng tôi cũng có thể đảm bảo rằng hàng xu hướng sẽ luôn được đối chiếu với hồ sơ giao dịch của khách hàng, cho phép tổng hợp xu hướng hoàn toàn ở phía máy chủ trong bộ đồng xử lý.Để tính toán trước xu hướng, chúng tôi đã triển khai một bộ đồng xử lý quan sát tùy chỉnh để móc vào đường dẫn ghi. (Bộ đồng xử lý của trình quan sát tương tự như bộ kích hoạt trong RDBMS ở chỗ chúng thực thi mã người dùng trước hoặc sau khi một sự kiện cụ thể xảy ra. Ví dụ:trước hoặc sau
Put
hoặcGet
.)Trên
postPut
bộ đồng xử lý thực hiện các hành động sau:- Kiểm tra
Put
cho một thuộc tính xu hướng (cờ). Thuộc tính chỉ được đặt trên các bản ghi giao dịch mới để tránh các lệnh gọi đệ quy khi cập nhật bản ghi xu hướng. Nó cũng cho phép bỏ qua bộ đồng xử lý choPut
không yêu cầu phải cập nhật xu hướng (ví dụ: khu định cư ). - Nhận bản ghi xu hướng cho khách hàng. Bản ghi xu hướng của khách hàng được định vị với các giao dịch của họ (dựa trên tiền tố khóa hàng) để bộ đồng xử lý có thể truy xuất trực tiếp từ khu vực hiện tại. Hàng xu hướng phải được khóa để ngăn nhiều chuỗi trình xử lý RegionServer cố gắng cập nhật xu hướng song song.
- Cập nhật các điểm dữ liệu:
- Cập nhật và mở khóa hàng xu hướng.
Giải pháp được chứng minh là chính xác trong quá trình thử nghiệm và hiệu suất đọc như mong đợi đã vượt quá yêu cầu. Tuy nhiên, có một số lo ngại với cách tiếp cận này. Đầu tiên là cách xử lý thất bại:các xu hướng được lưu trữ trong một hàng riêng biệt nên không thể đảm bảo tính nguyên tử. Thứ hai là làm thế nào để xác nhận tính chính xác của các xu hướng theo thời gian; nghĩa là, chúng ta sẽ cần thực hiện một cơ chế để xác định và khắc phục bất kỳ sự không chính xác nào về xu hướng. Khi chúng tôi cũng xem xét các yêu cầu HA và thực tế là chúng tôi sẽ cần chạy hai phiên bản HBase hoạt động tích cực trong các trung tâm dữ liệu khác nhau, đây có thể là một vấn đề lớn hơn. Độ chính xác của xu hướng không chỉ có thể giảm theo thời gian mà cả hai cụm cũng có thể trôi dạt và phải được điều chỉnh tùy thuộc vào phương pháp chúng tôi sử dụng để đồng bộ hóa chúng. Cuối cùng, việc sửa lỗi hoặc thêm điểm dữ liệu mới sẽ rất khó khăn vì chúng tôi có thể phải theo dõi lại và tính toán lại tất cả các xu hướng.
Sau đó là hiệu suất viết. Đối với mỗi giao dịch mới, người quan sát phải tìm nạp bản ghi xu hướng, cập nhật 32 điểm dữ liệu và đặt bản ghi xu hướng trở lại. Mặc dù tất cả những điều này xảy ra trong giới hạn của một khu vực, chúng tôi nhận thấy rằng thông lượng đã giảm từ hơn 20.000 lần ghi mỗi giây xuống 1.000 lần ghi mỗi giây (mỗi Máy chủ vùng). Hiệu suất này có thể chấp nhận được trong ngắn hạn, nhưng sẽ không mở rộng quy mô để hỗ trợ mức tải dài hạn được dự đoán.
Chúng tôi biết rằng hiệu suất ghi là một rủi ro vì vậy chúng tôi đã có một kế hoạch dự phòng và đó là một bộ đồng xử lý điểm cuối . Bộ đồng xử lý điểm cuối tương tự như các thủ tục được lưu trữ trong RDBMS ở chỗ chúng cho phép bạn thực hiện tính toán phía máy chủ — tại Máy chủ Vùng nơi đặt dữ liệu, chứ không phải tại máy khách. Điểm cuối mở rộng hiệu quả API HBase.
Thay vì tính toán trước các xu hướng, điểm cuối sẽ tính toán chúng một cách nhanh chóng, phía máy chủ. Do đó, chúng tôi có thể loại bỏ họ cột xu hướng khỏi lược đồ và rủi ro về sự không chính xác và phân kỳ đi kèm với nó. Di chuyển ra xa người quan sát dẫn đến hiệu suất ghi tốt, nhưng liệu việc đọc có đủ nhanh không? Trong ngắn hạn, có. Với các giao dịch của khách hàng được giới hạn trong một khu vực và được sắp xếp theo thẻ và dấu thời gian, điểm cuối có thể quét và tổng hợp một cách nhanh chóng, nằm trong mục tiêu 200ms của Spendlytics. Điều này cũng có nghĩa là một yêu cầu của khách hàng (từ API Spendlytics trong trường hợp này) chỉ được chuyển đến một phiên bản Điểm cuối duy nhất (Máy chủ vùng duy nhất) và khách hàng sẽ nhận được một phản hồi duy nhất với một kết quả hoàn chỉnh — nghĩa là không có phía máy khách yêu cầu xử lý để tổng hợp kết quả từng phần từ nhiều điểm cuối, trường hợp này xảy ra nếu giao dịch của khách hàng trải dài trên nhiều khu vực.
Bài học kinh nghiệm
Spendlytics đã hoạt động từ tháng 7 năm 2015. Kể từ đó, chúng tôi đã theo dõi chặt chẽ các mẫu truy cập và xem xét các cách để tối ưu hóa hiệu suất. Chúng tôi muốn liên tục cải thiện trải nghiệm người dùng và cung cấp cho khách hàng ngày càng nhiều thông tin chi tiết hơn về chi tiêu bằng thẻ của họ. Phần còn lại của bài đăng này mô tả những bài học chúng tôi đã học được từ việc chạy Spendlytics trong sản xuất và một số tối ưu hóa đã được áp dụng.
Sau bản phát hành đầu tiên, chúng tôi đã xác định được một số điểm đau mà chúng tôi muốn tập trung cải thiện. Đầu tiên là cách lọc kết quả theo thuộc tính giao dịch. Như đã đề cập trước đây, thuộc tính giao dịch được mã hóa trong bản ghi Avro, nhưng chúng tôi nhận thấy rằng ngày càng có nhiều mẫu truy cập muốn lọc theo thuộc tính và người dùng buộc phải thực hiện việc này ở phía máy khách. Giải pháp ban đầu là triển khai một HBase
ValueFilter
tùy chỉnh đã chấp nhận các biểu thức bộ lọc phức tạp của riêng chúng tôi, ví dụ:category='SUPERMARKETS' AND amount > 100 AND (brand LIKE 'foo%' OR brand = 'bar')
Biểu thức được đánh giá cho mỗi bản ghi Avro, cho phép chúng tôi lọc kết quả phía máy chủ và giảm lượng dữ liệu được trả về máy khách (tiết kiệm băng thông mạng và xử lý phía máy khách). Bộ lọc không ảnh hưởng đến hiệu suất quét, nhưng thời gian phản hồi vẫn tốt trong mục tiêu 200ms.
Đây cuối cùng chỉ là một giải pháp tạm thời vì cần có những thay đổi khác để tối ưu hóa việc ghi. Do cách thức hoạt động của quy trình thanh toán bằng thẻ tín dụng, trước tiên, chúng tôi nhận được một thẻ được ủy quyền giao dịch từ thời điểm bán (trong thời gian gần thực tế) và sau đó một thời gian được giải quyết giao dịch từ mạng thẻ tín dụng (theo đợt). Các giao dịch này cần được điều chỉnh, về cơ bản bằng cách hợp nhất các giao dịch đã giải quyết giao dịch với được ủy quyền giao dịch đã có trong HBase, tham gia trên ID giao dịch. Là một phần của quy trình này, các thuộc tính giao dịch có thể thay đổi và các thuộc tính mới có thể được thêm vào. Điều này tỏ ra khó khăn do phải viết lại toàn bộ bản ghi Avro — ngay cả khi cập nhật các thuộc tính đơn lẻ. Vì vậy, để làm cho các thuộc tính dễ truy cập hơn cho các bản cập nhật, chúng tôi đã sắp xếp chúng thành các cột, thay thế tuần tự hóa Avro.
Chúng tôi cũng chỉ quan tâm đến tính nguyên tử ở cấp độ giao dịch, vì vậy việc bán đấu giá các giao dịch theo giờ không mang lại cho chúng tôi bất kỳ lợi thế nào. Hơn nữa, giải quyết các giao dịch hiện đã đến theo đợt chỉ có mức độ chi tiết trong ngày, điều này gây khó khăn (tốn kém) để điều chỉnh chúng với giao dịch được ủy quyền hiện có giao dịch được lưu trữ theo giờ. Để giải quyết vấn đề này, chúng tôi đã di chuyển ID giao dịch vào khóa hàng và giảm hạt dấu thời gian thành ngày, thay vì giờ. Quá trình điều chỉnh giờ đây dễ dàng hơn nhiều vì chúng tôi có thể chỉ cần tải hàng loạt các thay đổi vào HBase và để giải quyết các giá trị được ưu tiên.
Tóm lại:
- Bộ đồng xử lý của Observer có thể là một công cụ có giá trị, nhưng hãy sử dụng chúng một cách khôn ngoan.
- Đối với một số trường hợp sử dụng, việc mở rộng API HBase bằng cách sử dụng điểm cuối là một giải pháp thay thế tốt.
- Sử dụng các bộ lọc tùy chỉnh để cải thiện hiệu suất bằng cách cắt bớt kết quả ở phía máy chủ.
- Các giá trị được tuần tự hóa có ý nghĩa đối với trường hợp sử dụng phù hợp, nhưng hãy phát huy thế mạnh của HBase bằng cách ưu tiên hỗ trợ gốc cho các trường và cột.
- Việc quản lý các kết quả được tính toán trước rất khó khăn; độ trễ bổ sung do tính toán khi di chuyển có thể đáng giá.
- Các kiểu truy cập sẽ thay đổi, vì vậy, hãy nhanh nhẹn và cởi mở trong việc thực hiện các thay đổi đối với giản đồ HBase để thích ứng và luôn dẫn đầu cuộc chơi.
Lộ trình
Một tối ưu hóa mà chúng tôi hiện đang đánh giá là bộ vi xử lý lai. Ý chúng tôi muốn nói ở đây là sự kết hợp của cả bộ đồng xử lý quan sát và điểm cuối để tính toán trước xu hướng. Tuy nhiên, không giống như trước đây, chúng tôi sẽ không thực hiện việc này trên đường dẫn ghi mà ở chế độ nền bằng cách kết nối với các hoạt động nén và nén của HBase. Một người quan sát sẽ tính toán các xu hướng trong các sự kiện tuôn ra và nén dựa trên dàn xếp giao dịch có sẵn tại thời điểm đó. Sau đó, chúng tôi sẽ sử dụng một điểm cuối để kết hợp các xu hướng được tính toán trước với các tổng hợp nhanh chóng của vùng đồng bằng các giao dịch. Bằng cách tính toán trước xu hướng theo cách này, chúng tôi hy vọng sẽ tăng hiệu suất đọc mà không ảnh hưởng đến hiệu suất ghi.
Một cách tiếp cận khác mà chúng tôi đang đánh giá để tổng hợp xu hướng và để truy cập HBase nói chung, là Apache Phoenix. Phoenix là một giao diện SQL cho HBase cho phép truy cập bằng cách sử dụng các API JDBC tiêu chuẩn. Chúng tôi hy vọng rằng bằng cách sử dụng SQL và JDBC, nó sẽ đơn giản hóa việc truy cập HBase và giảm số lượng mã chúng tôi phải viết. Chúng tôi cũng có thể tận dụng các mẫu thực thi thông minh của Phoenix và được tích hợp trong bộ đồng xử lý và bộ lọc để tổng hợp nhanh chóng. Phoenix được coi là quá non nớt để sử dụng trong sản xuất khi mới thành lập Spendlytics, nhưng với các trường hợp sử dụng tương tự đã được báo cáo bởi eBay và Salesforce, bây giờ là lúc để đánh giá lại. (Gói Phoenix cho CDH có sẵn để cài đặt và đánh giá, nhưng không hỗ trợ, thông qua Cloudera Labs.)
Santander gần đây đã thông báo rằng họ là ngân hàng đầu tiên ra mắt công nghệ ngân hàng bằng giọng nói cho phép khách hàng nói chuyện với ứng dụng SmartBank của họ và hỏi về chi tiêu thẻ của họ. Nền tảng đằng sau công nghệ này là Cloudera và kiến trúc cho Spendlytics — như được mô tả trong tập hợp các bài đăng này — được coi là thiết kế kế hoạch chi tiết.
James Kinley là Kiến trúc sư giải pháp chính tại Cloudera.
Ian Buss là Kiến trúc sư giải pháp cao cấp tại Cloudera.
Pedro Boado là kỹ sư Hadoop tại Santander (Isban) Vương quốc Anh.
Abel Fernandez Alfonso là một kỹ sư Hadoop tại Santander (Isban) Vương quốc Anh.