MyRocks/MariaDB 的 tuning 過程

看起來應該是找 Percona 的人幫忙轉移到 MyRocks 上,然後整理出來的成功案例:「The Road Story of a MyRocks/MariaDB Migration」。

看起來是跑在獨立機器上,而不是雲端的虛擬機上,所以不是想 scale up 就可以把硬體規格拉上去 (說不定記憶體插槽已經滿了之類的...):

Replicas run on bare metal servers, usually Dual Xeon E5 v3 or v4, with 192 GB to 384 GB of RAM.

這次遇到的主要的問題是發現效能跟不上。另外在文章裡面沒寫到,但可以猜到的是,他們目前不打算改架構,而是想要藉由改善資料庫的效能來解決問題:

The servers were close to their limits and were slow to catch up with replication after a maintenance period

後面可以看到不少過程,主要是重新編一份 MariaDB,讓 MyRocks 支援 Zstandard (MyRocks 支援 Zstandard,不過 MariaDB 內的 MyRocks 不知道為什麼關掉了...),這點大幅降低了空間的佔用。

另外是遇到 OOM 問題,在改用 jemalloc 解決記憶體用量的問題後就解決了 (這個在使用 InnoDB 的時候也算是標配了)。

不過在「Increased Read Load Over Time」那段還是看到了 workaround:

The read load was still rising a bit but at a much smaller pace. Instead of hours, it was days. That’s kind of expected given the workload and we were already planning for periodic manual compactions.

目前看起來 MyRocks 的強項主要是在省資源,但缺點就是有不少眉眉角角得小心處理。這樣的話,一般應該還是會先用 InnoDB,真的搞大了再考慮要不要換過去...

MariaDB 的 S3 Engine 效能測試

PerconaMariaDB 在 10.5 (目前的最新穩定版) 裡出的 S3 Engine 給出了簡單的測試報告:「MariaDB S3 Engine: Implementation and Benchmarking」。

這個 engine 顧名思義就是把資料丟到 Amazon S3 上,目前是 alpha 版本,預設是不會載入的,需要開 alpha flag 才能用:

The S3 engine is READ_ONLY so you can’t perform any write operations ( INSERT/UPDATE/DELETE ), but you can change the table structure.

另外這是從 Aria 改出來的 read-only engine,而 Aria 是從 MyISAM 改出來的:

The S3 storage engine is based on the Aria code and the main feature is that you can directly move your table from a local device to S3 using ALTER.

測出來發現在 read-only 的情境下,COUNT(*) 超快,看起來就是跟 MyISAM 體系有關,直接撈 MyISAM 內的資料,所以本地要 18 秒,但放到 S3 反而秒殺 XDDD

整體看起來還不錯?算是一種 Data warehouse 的方案,主要是要用到 row-based format 儲存的優點,遇到一些冷資料可以這樣玩。

從「Using the S3 Storage Engine」這邊的設定方式看到 s3_host_name,看起來有機會接其他家的 S3 API,或是本地的 Storage。

話說 Aria 這個引擎當初最主要的重點就在 crash-safe,在有了 crash-safe 之後,DRBD 這種 block-level replication 機制就可以硬幹上去,後來主力就在擴充其他型態了,像是 GIS 與 virtual column 的功能,不過這些功能本家在 InnoDB 上好像也都陸陸續續跟上來了,單純的 Aria engine 好像還好...

Runtime 期間的最佳化工具:Dynimize

忘記在哪邊看到「Reduce MySQL CPU Usage Through Dynamic Binary Optimization」這篇文章了,裡面其實是在描述自家產品 Dynimize 的威猛。

翻了一些資料可以發現這個產品出來一陣子了,在 2018 的時候曾經在 Percona Live 上發表過:「Accelerating MySQL with JIT Compilers」,可以看出來有點像是 PGO (Profile-guided optimization) 的行為,只是他可以直接對 binary 處理。

定價的部份會是這類產品的重點,如果價錢比加硬體貴的話就沒那麼好用了... 在 Dynimize Pricing 這邊可以看到是 per CPU 的價錢,$0.00139/hr、$1/month 或是一次性的 $24,以效能提昇的程度來看,如果在 database 這邊是 CPU bound,是個頗值得投資的項目。

RDS 支援 Storage Auto Scaling

Amazon RDS 推出了 Storage Auto Scaling:「Amazon RDS now supports Storage Auto Scaling」。

看起來傳統 RDBMS 類的都支援 (也就是非 Aurora 的這些):

Starting today, Amazon RDS for MariaDB, Amazon RDS for MySQL, Amazon RDS for PostgreSQL, Amazon RDS for SQL Server and Amazon RDS for Oracle support RDS Storage Auto Scaling.

仔細看了一下新聞稿,裡面都只有提到 scale up,沒有提到 scale down,這個功能應該是只會提昇不會下降,所以要注意突然用很多空間,再砍掉後的問題:

RDS Storage Auto Scaling automatically scales storage capacity in response to growing database workloads, with zero downtime.

RDS Storage Auto Scaling continuously monitors actual storage consumption, and scales capacity up automatically when actual utilization approaches provisioned storage capacity.

除了香港外的所有商業區域都提供:

RDS Storage Auto Scaling is available in all commercial AWS regions except in Asia Pacific (Hong Kong) and AWS GovCloud.

在 SQL 裡面避免大量刪除資料的方式

看到 Percona 的「An Overview of Sharding in PostgreSQL and How it Relates to MongoDB’s」這篇,雖然是在講 PostgreSQL 上的 sharding (以及 partition),突然想到好像沒寫過要怎麼避免大量刪除資料的操作...

一個常見的情境是,想要讓某個表格只保留這一個月的資料,所以每個月開頭都會跑一隻 cron job 負責刪掉上個月的資料,像是 DELETE FROM xxx WHERE timestamp < yyy; 這樣的指令。

這個方式無論是在 PostgreSQL 或是 MySQL 都需要很多時間與 I/O 資源,而透過 partition 將不同時間區段切開到不同的表格,再用 TRUNCATE 直接清空表格剛好可以解這樣的問題。

Percona 的文章裡說了一些 PostgreSQL 的歷史與目前的進展。

在 PostgreSQL 9 或更早以前的版本,一個常見的作法是透過 table inheritance 實做 partition,然後用再用 function 實做 INSERT

CREATE TABLE temperature (
  id BIGSERIAL PRIMARY KEY NOT NULL,
  city_id INT NOT NULL,
  timestamp TIMESTAMP NOT NULL,
  temp DECIMAL(5,2) NOT NULL
);

CREATE TABLE temperature_201901 (CHECK (timestamp >= DATE '2019-01-01' AND timestamp <= DATE '2019-01-31')) INHERITS (temperature);
CREATE TABLE temperature_201902 (CHECK (timestamp >= DATE '2019-02-01' AND timestamp <= DATE '2019-02-28')) INHERITS (temperature);
CREATE TABLE temperature_201903 (CHECK (timestamp >= DATE '2019-03-01' AND timestamp <= DATE '2019-03-31')) INHERITS (temperature);

CREATE OR REPLACE FUNCTION temperature_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
    IF ( NEW.timestamp >= DATE '2019-01-01' AND NEW.timestamp <= DATE '2019-01-31' ) THEN INSERT INTO temperature_201901 VALUES (NEW.*);
    ELSIF ( NEW.timestamp >= DATE '2019-02-01' AND NEW.timestamp <= DATE '2019-02-28' ) THEN INSERT INTO temperature_201902 VALUES (NEW.*);
    ELSIF ( NEW.timestamp >= DATE '2019-03-01' AND NEW.timestamp <= DATE '2019-03-31' ) THEN INSERT INTO temperature_201903 VALUES (NEW.*);
    ELSE RAISE EXCEPTION 'Date out of range!';
    END IF;
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

在 PostgreSQL 10 之後,就直接支援一些與 partition 相關的設計,像是這樣:

CREATE TABLE temperature (
  id BIGSERIAL NOT NULL,
  city_id INT NOT NULL,
  timestamp TIMESTAMP NOT NULL,
  temp DECIMAL(5,2) NOT NULL
) PARTITION BY RANGE (timestamp);

CREATE TABLE temperature_201901 PARTITION OF temperature FOR VALUES FROM ('2019-01-01') TO ('2019-02-01');
CREATE TABLE temperature_201902 PARTITION OF temperature FOR VALUES FROM ('2019-02-01') TO ('2019-03-01');
CREATE TABLE temperature_201903 PARTITION OF temperature FOR VALUES FROM ('2019-03-01') TO ('2019-04-01');

雖然還是有些限制,但可以看出比起以前簡單不少。

而有了 partition 後,文章的後續就在討論這跟 MongoDB 的 sharding 有什麼關係,但這就不是我關注的事情了...

在 Galera Cluster 上的 DDL 操作 (e.g. ALTER TABLE)

Percona 整理了一份關於 Galara Cluster 上 DDL 操作的一些技巧,這包括了 Percona XtraDB ClusterMariaDB 的版本:「How to Perform Compatible Schema Changes in Percona XtraDB Cluster (Advanced Alternative)?」。

在不知道這些技巧前,一般都是拿 Percona Toolkit 裡的 pt-online-schema-change 來降低影響 (可以降的非常低),所以這些技巧算是額外知識,另外在某些極端無法使用 pt-online-schema-change 的情境下也可以拿來用...

裡面的重點就是 wsrep_OSU_method 這個參數,預設的值 TOI 就是一般性的常識,所有的指令都會被傳到每一台資料庫上執行,而 RSU 則是會故意不讓 DDL 操作 (像是 ALTER TABLE) 被 replicate 到其他機器,需要由管理者自己到每台機器上執行。

利用這個設定,加上透過工具將流量導到不同後端的資料庫上,就有機會分批進行修改,而不需要透過 pt-online-schema-change 這種工具。

把 MySQL 的 binlog 功能再拆出來的 mysql-ripple

看到 Percona 的「MySQL Ripple: The First Impression of a MySQL Binlog Server」這篇提到了 Google 放出來的專案 mysql-ripple

這個軟體的情境是針對有很多 replica (slave) 時的情境,要解決每一個 replica 都會對 master server 產生壓力,算是 binlog 的 cache layer。

MySQL Ripple 抓了 binlog 下來後就可以模擬成 mysql server (但是只能提供 binlog 服務) 讓 replica 接,在 replica 很多的情境下就可以橫向擴充,而且因為軟體只支援 GTID 模式,所以比較好做 HA 架構 (相對於 filename + position 模式)。

大概可以歸納出是 write 很多 (所以 binlog 量很大),但又有大量 replica 需求的情境... 目前好像想不出來有什麼情境可以拿出來用 :o

在 MySQL 上遇到 Replication Lag 的解法

看到 Percona 的 blog 上寫了一篇 MySQL 遇到 replication lag 時要怎麼解決:「MySQL High Availability: Stale Reads and How to Fix Them」,另外在留言也有人提到 Booking.com 的解法:「How Booking.com avoids and deals with replication lag」。

在業務成長到單台 MySQL server 不夠用的情況下,最簡單的擴充方式是架設 slave server,然後把應用程式裡讀取的部份導到 slave 上 (也就是 R/W split),但因為 MySQL 的 replication 是非同步的,所以有可能會發生在 master 寫入資料後 slave 還讀不到剛剛寫的資料,也就是 replication lag。

這就大概有幾種作法,一種是當發現 lag 時就回 master 讀,但通常這都會造成 master 過載... 所以另外一種改善的作法是發現 lag 時就換其他 slave 看看,但這個方法就不保證讀的到東西,因為有可能所有的 slave 都 lag。

以前遇到的時候是拆情境,預設還是 R/W split,但敏感性的資料處理以及金流相關的資料就全部都走 master。

不過文章裡的解法更一般性,在寫入時多寫一份資料,然後在 slave 等這組資料出現。唯一的缺點就是要 GC 把多寫的資料清掉...

同樣的想法,其實可以讓 MySQL 在 commit 時直接提供給 binlog 或 GTID 的資訊,然後在 slave 等待這組 binlog 或 GTID 被執行。

看起來算是很不錯的解法,不知道各家 framework 對這些方式的支援度如何...

KPTI (Meltdown Mitigation) 對 MyISAM 的痛點

MariaDB 的「MyISAM and KPTI – Performance Implications From The Meltdown Fix」這篇看到頗驚人的數字,這篇提到了他們收到回報 (回報的 ticket 可以參考「[MDEV-15072] Massive performance impact after PTI fix - JIRA」),說 KPTI (Meltdown Mitigation) 對 MyISAM 效能影響巨大:

Recently we had a report from a user who had seen a stunning 90% performance regression after upgrading his server to a Linux kernel with KPTI (kernel page-table isolation – a remedy for the Meltdown vulnerability).

他們發現 90% 是因為 VMware 舊版本無法使用 CPU feature 加速,在新版應該可以改善不少。但即使如此,文章內還是在實體機器上看到了 40% 的效能損失:

A big deal of those 90% was caused by running in an old version of VMware which doesn’t pass the PCID and INVPCID capabilities of the CPU to the guest. But I could reproduce a regression around 40% even on bare metal.

然後後面就在推銷 MariaDB 的 Aria Storage Engine 了,不是那麼重要... 不過知道 MyISAM 在 KPTI 下這麼傷還蠻重要的,因為接下來五年應該都還是愈的到 KPTI,應該還是有人在用 MyISAM...

Amazon RDS 支援更大的硬碟空間與更多的 IOPS

Amazon RDS 的升級:「Amazon RDS Now Supports Database Storage Size up to 16TB and Faster Scaling for MySQL, MariaDB, Oracle, and PostgreSQL Engines」。

空間上限從 6TB 變成 16TB,而且可以無痛升。另外 IOPS 上限從 30K 變成 40K:

Starting today, you can create Amazon RDS database instances for MySQL, MariaDB, Oracle, and PostgreSQL database engines with up to 16TB of storage. Existing database instances can also be scaled up to 16TB storage without any downtime.

The new storage limit is an increase from 6TB and is supported for Provisioned IOPS and General Purpose SSD storage types. You can also provision up to 40,000 IOPS for Provisioned IOPS storage volumes, an increase from 30,000 IOPS.

不過隔壁的 Amazon Aurora 還是大很多啊 (64TB),而且實際上不用管劃多大,他會自己長大:

Q: What are the minimum and maximum storage limits of an Amazon Aurora database?

The minimum storage is 10GB. Based on your database usage, your Amazon Aurora storage will automatically grow, up to 64 TB, in 10GB increments with no impact to database performance. There is no need to provision storage in advance.