Xây dựng SQL Database sử dụng key-value storage

AJ Pham
ZaloPay Engineering
6 min readDec 9, 2019

--

1. Giới thiệu

Hiện nay key-value storage (ở bài viết này mình sẽ gọi tắt là storage) hay key-value database đang được sử dụng rất phổ biến, có thể kể đến như CouchDB, FoundationDB, Redis, Berkeley DB. Hiện nay, nhiều hệ thống muốn sử dụng các cú pháp của SQL mà bên dưới vẫn tận dụng các ưu điểm của storage này để lưu trữ. Với nhu cầu đó chúng ta cần có một layer đảm nhận vai trò parser câu SQL (SQL layer) và thực thi câu SQL đó xuống tầng storage.

ZPD hay ZaloPay Database được xây dựng như một SQL layer nằm ngay trên tầng storage. Storage mình sử dụng là TiKV và dùng PD để quản lí và tương tác với TiKV.

Đây cũng là đề bài thử việc của mình tại ZaloPay, trong phạm vi của project thì mình chỉ cung cấp cách thực hiện một số câu SQL đơn giản. Ở bài viết này mình xin giới thiệu về tổng thể kiến trúc và cách hoạt động của ZPD.

2. Tổng quan hệ thống

2.1 Tính chất

Service có tính chất stateless vì tất cả dữ liệu đều được lưu trữ dưới TiKV, service chỉ đóng vai trò nhận và thực thi câu SQL xuống TiKV. Project được viết bằng ngôn ngữ Go, sử dụng protobuf để định nghĩa các gói tin, đồng thời kết hợp với framework gRPC để xây dựng protocol và service.

Service có khả năng scale bằng cách thêm nhiều instance vào trong cluster. Đồng thời nó sử dụng Consul làm discovery service và dùng tính năng election leader để chọn leader giữa các instances.

2.2 Flow hoạt động

Service cơ bản hoạt động theo sơ đồ sau:

Hiện tại ZPD cung cấp 3 API như sau:

  • ConnectDatabase: dùng để connect tới service.
  • CloseConnectionDatabase: đóng connection tới service.
  • ExecuteStatement: là API chứa các câu SQL muốn thực hiện.

Các câu SQL mà service có thể thực thi còn khá đơn giản, bao gồm:

  • Create database/table.
  • Drop database/table.
  • Show database/table.
  • Insert row.
  • Select row.
  • Delete row.

Phần tiếp theo chúng ta sẽ đi chi tiết hơn kiến trúc bên trong của service, cùng nhìn qua các layer và vai trò của nó.

3. Kiến trúc service

Kiến trúc của ZPD được chia thành các layer đảm nhận những chức năng khác nhau. Việc phân chia service thành các layer rất có lợi cho việc scale up, việc test chức năng và tận dụng lại các layer cho các mục đích sau này. Mô hình kiến trúc như sau:

3.1 Connection layer

Connection layer có vai trò quản lí các connection từ client tới service. Mỗi connection sẽ ứng với một session. Ngoài ra, connection layer còn có nhiệm vụ encodedecode các gói tin gửi/nhận.

3.2 Parser layer

Các câu SQL sẽ được Parser layer phân tích thành Abstract Syntax Tree (AST). Các bạn có thể tìm hiểu quá trình parse câu SQL thành AST ở đây. AST sẽ là input cho Core layer xây dựng thành các Executor để thực thi câu lệnh. Hiện tại mình sử dụng package sqlparser để cung cấp chức năng parse câu SQL.

3.3 Core layer

Phần Core layer chia thành 3 component:

  • Executor.
  • Consul Agent.
  • Bride API.

3.3.1 Executor

Khi nhận được AST từ Parser layer đẩy xuống, component trước tiên sẽ validate AST xem có hợp lệ hay không. Một số rule đặt ra như kiểm tra số lượng columns, kiểm tra conflict về column name, kiểu dữ liệu,…. Sau khi validate thành công, component sẽ thực hiện việc xây dựng executor tương ứng với cây AST.

Ngoài ra, Executor component còn đảm nhận vai trò mapping dữ liệu câu SQL thành dạng key-value để có thể lưu trữ được. Sau khi việc mapping hoàn tất thì executor sẽ trực tiếp gọi xuống tầng Data access layer để thực thi câu lệnh SQL.

3.3.2 Consul Agent

Đối với các câu SQL thuộc dạng DDL (Data Definition Language) nó mang tính ảnh hưởng global trong database, vì thế để dễ dàng xử lý các câu SQL như vậy trong trường hợp có nhiều instance trong cluster thì giải pháp của ZPD là chọn ra một node leader và chỉ có node leader mới có quyền thực hiện được các câu SQL DDL. Khi một yêu cầu thuộc SQL DDL được gọi tới node không phải là node leader thì node đó sẽ chuyển tiếp yêu cầu qua node leader và chờ nhận kết quả trả về. Để làm được mô hình như vậy, ZPD sử dụng Consul service hỗ trợ.

Component này đóng vài trò giao tiếp với Consul. Khi khởi động service, component sẽ đăng khi với Consul và sử dụng Consul để làm discovery service, quản lý configuration cho hệ thống.

3.3.3 Bridge API

ZPD xây dựng một Bridge API đảm nhận việc gọi API tới node leader. Bên trong ZPD chia làm hai loại API, một loại cho client sử dụng gọi là public API, loại còn lại để các ZPD gọi tới node leader hay gọi là internal API. Để có thể gọi API đến node leader, Consul Agent trước tiên gọi lên Consul service để lấy địa chỉ node leader, sau đó Bridge API sẽ gọi tới node leader.

3.4 Data access layer

Đây là layer duy nhất trong ZPD có thể giao tiếp với Storage layer để trao đổi dữ liệu. Data access layer sẽ cung cấp các API cho các tầng trên sử dụng, nó quản lý tất cả connection xuống Storage layer. Ở layer này chúng ta có thể phân quyền truy cập dữ liệu cũng như ngăn chặn các yêu cầu không hợp lệ xuống Storage layer.

Ở đây mình đã xây dựng interface, chúng ta có thể thay thế TiKV thành các loại key-value store khác như RockDB mà không ảnh hưởng tới các layer khác.

3.5 Storage layer

Để có thể giao tiếp với TiKV một cách dễ dàng, Storage layer đã sử dụng package TiKV client như là một SDK để có thể sử dụng linh hoạt các API của TiKV.

4. Đề xuất cải tiến

Chúng ta có thể sử dụng thêm NGINX để cân bằng tải. Đồng thời sử dụng Consul-template để quản lí file config của NGINX thông qua việc nhận các sự thay đổi từ Consul. Ngoài ra việc sử dụng Consul và Consul-template giúp cho việc vận hành các service rất dễ dàng như việc deploy version mới của service, quản lý configuration, health check, service discovery.

5. Kết luận

Ở bài viết này mình đã giới thiệu tổng quan của ZPD , cũng như trình bày các layer trong kiến trúc của service. Hiện tại mình đã xử lý được các câu SQL cơ bản, và tương lai sẽ hoàn thiện hơn về mặt chức năng cũng như kiến trúc bên trong. Các bạn có thể tham khảo project ở github. Mình cũng gửi lời cảm ơn đến bạn Alex Nguyen người đồng hành thực hiện project này và anh Anh Le (Andy) đã hướng dẫn nhiệt tình.

ZaloPay luôn mong muốn có thêm các thành viên mới gia nhập đội ngũ engineering, cùng giải quyết các bài toán hóc búa về high performance, fault tolerant và distributed transaction. Các bạn quan tâm về cơ hội nghề nghiệp có thể liên hệ trực tiếp qua page: https://www.facebook.com/zalopay.engineering/

--

--

AJ Pham
ZaloPay Engineering

I am a Senior Software Engineer at VNG. I often write blogs to share experiences as well as read more blog posts. linkedin.com/in/aj-pham-abc27011997