Một trong các bài toán khiến mình khá đau đầu khi lên plan để chuyển một hệ thống từ physical server lên cloud đó là bài toán giới hạn I/O và chi phí cho database. Nó khiến mình mất khá nhiều thời gian và thắc mắc, bài này là bài lược dịch từ một bài blog của Percona, có nói chi tiết các cách giải quyết, các issues có thể gặp phải và cách giải quyết các issues đó. Mình dịch lại, hi vọng nếu ai đó gặp phải có thể tìm được cách giải quyết và dễ dàng migrate hệ thống lên cloud.

1. Đặt vấn đề

Hệ thống sử dụng MySQL (hoặc PostgeSQL), sử dụng 1 siêu physical server với cấu hình và thông số về data tượng trưng như sau:

  • Dell PowerEdge R720 (2-socket, 2U rack server).
  • CPU Xeon E5-2660V4 (14 cores, 28 threads) 2.20 Ghz.
  • 16GB * 12 ~ 192 GB RAM.
  • 2 * 4TB Samsung SSD 860 Pro (consumer SSD) -> RAID 1.
    • Sequential Read Speed ~ Up to 560 MB/sec.
    • Sequential Write Speed ~ Up to 530 MB/sec.
    • Random read (4KB, QD32) ~ Up to 100,000 IOPS.
    • Random write (4KB, QD32) ~ Up to 90,000 IOPS.
    • Random read (4KB, QD1) ~ Up to 11,000 IOPS.
    • Random write (4KB, QD1) ~ Up to 43,000 IOPS.
    • Reliablility (MTBF) ~ 1.5 milion hours.
    • ….
  • Kích thước dữ liệu thô giả định là ~ 2 TB (chưa bao gồm binlog …).

Đặc điểm của một server phục vụ database là cấu hình cái gì cũng sẽ cần mạnh:

  • Cần memory lớn, dùng để cache (innodb_buffer_pool_size) dữ liệu đọc từ đĩa (giảm thời gian đọc đĩa), tuy nhiên dữ liệu sẽ luôn lớn hơn rất nhiều so với memory (2TB với gần 200GB RAM) nên không thể fit hết data trên memory được, memory to chỉ giúp giảm thiểu việc đọc đĩa.
  • CPU mạnh, nhiều core giúp xử lý nhanh và đồng thời tốt.
  • Đĩa càng nhanh càng tốt, vì data vẫn nằm chính trên đĩa, mà execute time ở đĩa thì rất chậm so với các bộ nhớ khác nên đó là lí do cần dùng SSD. Tuy nhiên nếu có điều kiện nên sử dụng các dòng SSD DataCenter hoặc các đĩa xài NVMe Interface (ở trên xài Consumer SSD), tham khảo thêm tại wikipedia về IOPS của các loại đĩa.

Một số lưu ý khác trong quá trình setup:

  • Không xài swap, do swapping đọc đĩa sẽ chậm.
  • Tách biệt các vùng OS, data ra các đĩa/partition riêng. Ví dụ nên dùng 1 cặp đĩa nhỏ hơn để setup OS, cặp đĩa data nên để riêng để tránh bị ảnh hưởng IOPS khi OS đọc/ghi dữ liệu (log).
  • Nếu có thể, nên tách riêng vùng lưu trữ binlog ra partition riêng (tương tự postgres khi tách WAL).
  • Cân nhắc ZFS nếu có nhiều file nhỏ hoặc các filesystem khác phù hợp hơn.

Vấn đề:

Vấn đề chính là kích thước dữ liệu và IOPS của đĩa. Ở physical server như link wiki phía trên và trạng thái của đĩa Samsung SSD 860Pro thì IOPS rất lớn nên khi chuyển lên cloud thì cũng phải lựa chọn đĩa có cấu hình tương tự. Nói chung hầu hết các vấn đề chậm của database là do tốc độ của đĩa.

Nói qua về một số loại đĩa trên AWS và GCP

AWS

  • EBS: Đây là loại đĩa thường thấy trên AWS, về bản chất nó là một dạng network disk (nhưng ko giống EFS, loại network disk có thể mount đồng thời vào nhiều server cùng lúc, EBS chỉ mount vào 1 instance tại 1 thời điểm), đặc điểm là persistent nhưng có giới hạn về tốc độ. EBS lại chia làm một số loại nhỏ, nhưng ở đây ta chỉ nói về gp2io1:
    • gp2 là EBS giá rẻ, IOPS phụ thuộc vào kích thước của đĩa (muốn tăng IOPS phải resize disk), thường xài cho OS và các nhu cầu chung chung khác. Kích thước từ 1GB - 16TB, Max IOPS/volume là 16,000, max throughput/volume 250 MB/s, max IOPS/instance là 80,000, max throughput/instance 1,750 MB/s. Ngoài ra gp2 có một khái niệm là IO credit và baseline performance, nói 1 cách dễ hiểu là chỉ xài đc 100% performance trong 1 khoảng thời gian và credit có được phụ thuộc vào thời gian sử dụng. Ví dụ đĩa 4TB sẽ có IOPS là 12.288 và giá là ~ $406 tại region us-east-1.
    • io1 là loại EBS mà IOPS không phụ thuộc vào kích thước đĩa, có thể mua riêng IOPS. Thường xài khi cần nhiều IOPS ví dụ các ứng dụng database. Kích thước từ 4 GB - 16TB. Max IOPS/volume là 64,000, max throughput/volume 1,000 MB/s, max IOPS/instance là 80.000, max throughput/instance 1750 MB/s. Ví dụ với 4TB dữ liệu + 12,288 IOPS sẽ có throughput là 1000 MB/s, thì sẽ có giá là $1441 tại region us-east-1 -> một sự chênh lệnh rất lớn so với gp2. Và giả sử cần IOPS tầm 20,000 (1 ước tính chắc ăn và so với physical server) thì ta sẽ tốn gần $2000/tháng chỉ cho mỗi đĩa cứng 😑.
  • Instance storage: cung cấp temporary block-level storage. Storage sẽ được đặt trên local disk mà được physical attach vào host computer (đơn giản là 1 physical được chia làm nhiều EC2, thì instance storage là vùng storage lấy ra từ chính local disk đặt trên con physical đó và mount vào EC2 - khác với EBS là network disk, mount từ 1 physical khác và instance storage thì không thể deattach/attach vào instance khác). Đặc điểm chính là có tốc độ cao hơn nhưng dữ liệu không persistent (nghĩa là sẽ mất dữ liệu nếu instance bị stop hoặc terminate hoặc đĩa bị fail). Có 2 interface là SATA và NVMe tương tự như physical disk.

GCP về cơ bản cũng tương tự như AWS.

  • Persistent Disk, tương tự EBS.
  • Local SSD, tương tự Instance storage.

Nói thêm về dữ liệu trên Instance Storage/LocalSSD:

  • Dữ liệu của instance storage/LocalSSD chỉ persistent trong vòng đời liên kết với instance chứa nó, Nếu reboot (vô hình hoặc cố ý) thì dữ liệu sẽ không bị mất.
  • Dữ liệu sẽ bị mất trong các trường hợp sau:
    • Ổ đĩa bên dưới bị lỗi.
    • Instance stop.
    • Instance bị terminate.
  • Với AWS, sẽ không có một cách chính thống nào để change instance type mà giữ lại được dữ liệu trên instance storage.

Nếu bạn phải làm việc với instance storage/LocalSSD thì bắt buộc phải đọc kỹ các link phía dưới nếu không muốn bị mất sạch dữ liệu.

Vậy tóm tắt lại 2 vấn đề chính:

  • Làm sao nếu cần IOPS vượt quá giới hạn của io1 mà dữ liệu vẫn đảm bảo an toàn.
  • Chi phí hợp lý (nếu $2.000/month thì một năm sẽ tốn $24.000 chỉ cho mỗi một đĩa).

2. Giải pháp

Phần này chủ yếu dịch từ percona (có lược bỏ 1 số phần đã nói ở trên), các issue đều tương tự phần trên, chỉ có một điểm khác biệt là data có kích thước là 10TB+.

Giải pháp có thể tóm gọn như sau:

  • Dùng local disk (instance storage) NHƯNG lưu ý instance storage không persistence vì:
    • Nhanh vì nó là local disk -> giải quyết chuyện vượt giới hạn của io1.
    • Rẻ hơn EBS nhiều -> rẻ hơn io1 -> giải quyết bài toán chi phí.
  • Vì dữ liệu không an toàn và ta cần EBS snapshot nên sẽ dùng vài instance replication (chi tiết ở dưới).

2.1 So sánh chi phí

Sử dụng công cụ tính chi phí của AWS, cost ước tính là cost đăng ký 1 năm RIs và sử dụng đĩa với 14TB (lưu 10TB dữ liệu + binary log). Chi tiết như sau:

r4.4xlarge, 122GB RAM, 16 vCPUs + EBS, 14TB volume

Amazon EC2 Service (US East (N. Virginia)) $ 1890.56 / month
Compute: $ 490.56
EBS Volumes: $1400.00

Local storage price estimate: i3.4xlarge, 122GB RAM, 16 vCPUs, 3800 GiB disk (2 x 1900 NVMe SSD)

Amazon EC2 Service (US East (N. Virginia)) $ 627.21 / month
Compute: $625.61

i3.8xlarge, 244GB RAM, 32 vCPUs, 7600 GiB disk (4 x 1900 NVMe SSD)

Amazon EC2 Service (US East (N. Virginia)) $1252.82 / month
Compute: $ 1251.22

Như trên ta thấy nếu sử dụng i3.8xlarge ta được:

  • x2 RAM.
  • x2 virtual CPUs
  • Đĩa nhanh hơn.
  • 10 Gb network

Nhưng vẫn rẻ hơn 1.5 lần so với r4.4xlarge instance trên cùng.

2.2 How to migrate from EBS to local storage

Ở đây ta có một vài vấn đề khác cần giải quyết:

  • Total data size là 10TB và i3.8xlarge có 7,600GB đĩa. Để giải quyết vấn đề này thì câu trả lời là compression.
  • Local storage thì ephemeral, không thể chấp nhận chuyện mất dữ liệu -> dùng replication để đồng bộ dữ liệu sang một slave khác (slave này cũng xài instance storage) -> để scale read và tăng độ an toàn cho dữ liệu.
  • Cần EBS snapshot để backup -> dùng thêm 1 slave khác + EBS để persistence dữ liệu (nhưng xài EBS thì sẽ mắc) nên ta có thể dùng đĩa với IOPS tương đối và chỉ dùng slave này cho backup hoặc phục vụ một lượng nhỏ traffic (ví dụ analytic …). -> có thể dùng EBS snapshot cho instance này.

Về compression data, với i3.x8large thì chỉ cần 2x compression, cơ bản có 2 cách:

InnoDB compression

  • InnoDB row compresssion (row_format=compressed) hoặc InnoDB page compression.
  • Yêu cầu là filesystem phải hỗ trợ sparse filehole punching, các filesystem hiện đại hiện này đều hỗ trợ như ext3, ext4 … hay NTFS. Hiểu một cách đơn giản thì nó hoạt động như cách mà ta cấp đĩa cứng cho máy ảo, init 10GB nhưng nếu thực sự chỉ dùng 2GB thì file image sẽ chỉ có kích thước 2GB -> tối ưu việc lưu trữ. Bản chất đây là yêu cầu của compression chứ không phải của filesystem.
  • Tuy nhiên InooDB compression có thể chậm hơn so với không compression và và nhược điểm là chỉ hỗ trợ compress ibd file còn các file binary log, frm … thì không hỗ trợ.

ZFS compression

  • Đây là compression trên level filesystem, ZFS compression sẽ compress tất cả các file bao gồm cả binary log, frm …
  • Hữu ích nếu nếu dùng nhiều tablespace hoặc khi sử dụng schema per customer, table per customer

Một số thông tin về zfs compression như sau:

> du -sh --apparent-size /mysqldata/mysql/data
8.6T	/mysqldata/mysql/data
> du -sh /mysqldata/mysql/data
3.2T	/mysqldata/mysql/data

=> lưu ý đây là dữ liệu thật mà tác giả đang vận hành chứ không phải generated data để test.

Với tỉ lệ nén là 2.42x cho table và 3.75x cho binary log, giúp giảm từ 8.6T -> 3.2T sẽ vừa đủ trên i3.8xlarge instance.:

> zfs get all | grep -i compress
...
mysqldata/mysql/data  compressratio         2.42x                  -
mysqldata/mysql/data  compression           gzip                   inherited from mysqldata/mysql
mysqldata/mysql/data  refcompressratio      2.42x                  -
mysqldata/mysql/log   compressratio         3.75x                  -
mysqldata/mysql/log   compression           gzip                   inherited from mysqldata/mysql
mysqldata/mysql/log   refcompressratio      3.75x    

=> Tóm lại thì ZFS cung cấp một tỉ lệ nén khá tốt và do nén ở level filesystem nên có thể nén tất cả mọi thứ trên filesystem, giúp tiết kiệm một số tiền đáng kể. Tuy nhiên thì compression không miễn phí ZFS scan sẽ chậm hơn ở một số workload, sử dụng local NVMe storage để bù lại hiệu suất.

Ngoài ra bạn có thể đọc thêm về một số performance testing dành cho ZFS trên linux ở bài viết About ZFS Performance.

MyRock

Ngoài 2 cách trên để nén dữ liệu thì một lựa chọn khác là sử dụng MyRocks storage engine, đây là storage engine do Facebook phát triển nhằm giải quyết bài toán kích thước lưu trữ và hiệu năng ghi dữ liệu.

Replication & local volumes: Như nói ở trên thì ta sẽ cần vài server replication để đảm bảo độ an toàn của dữ liệu, mô hình sẽ như sau:

master - local storage (AZ 1, i.e. 1a)
-> slave1 - local storage (AZ 2, i.e. 1b)
-> slave2 - local storage with delayed replication (AZ 3, i.e. 1b)
-> slave3 - ebs storage (AZ 3, i.e. 1c)
   (other replication slaves if needed with local storage - optional)

master-slave

Một số lưu ý:

  • Đặt các replication ở các availability zone khác nhau.
  • Các local storage thì không bền (not durable) nghĩa là nếu ta stop instancestart instance lại thì dữ liệu sẽ mất. TUY NHIÊN reboot dù vô tình hay cố ý (instance reboots intentionally or unintentionally) là ngoại lệ, bạn có thể reboot instance và local storage vẫn sẽ được giữ lại.
  • Failover cho master bằng slave khi master chết có thể giải quyết bằng MHA hoặc Orchestrator.
  • Có thể clone slave bằng cách dùng xtrabackup hoặc zfs snapshot, với xtrabackup có thể đọc một số bài viết khác về clone slave trên blog của mình.
  • Nên xài delayed replication với độ trễ sync dữ liệu từ 30 phút - 60 phút để tránh human-error.

3. Tổng kết

  • Sử dụng io1 cho IOPS tốt nhưng siêu mắc.
  • Sử dụng local storage và i3 instance giúp tiết kiệm tiền nhưng phải aware các giải pháp về an toàn dữ liệu và độ tiện dụng, operation.
  • Có thể sử dụng MyRock, nhưng đổi storage engine là một bigger task yêu cầu nhiều thời gian và kiểm thử.
  • Có thể đổi qua sử dụng AWS Aurora nhưng chi phí cũng sẽ rất mắc và cũng là một bigger task.