2019 年 Percona 對 UUID 當作 Primary Key 的看法

前陣子的「為資料庫提案新的 UUID 格式」這邊提到了有人提案要增加新的 UUID 格式,Percona 的老大 Peter ZaitsevTwitter 上貼了「UUIDs are Popular, but Bad for Performance — Let’s Discuss」這篇在 2019 年時他們家的文章,題到了 MySQL 使用 UUID 當作 Primary Key 的事情:

要注意的是這篇文章沒有要從頭解釋 UUID 對於 Primary Key 的壞處,如果你想要先了解的話,在這篇文章的開頭給了一堆其他文章的連結,裡面就有討論過了。

這篇主要是在討論,如果硬要用 UUID 當 Primary Key 時,可以有什麼方法降低對 InnoDB 的衝擊,剛好回應最近的提案。

開頭還是先花了一些篇幅大概講一下 UUID 的種類,然後在「What is so Wrong with UUID Values?」這邊提到了字串比較的差異,如果 UUID 是到最後一碼才不同的話 (這邊是跑 df878007-80da-11e9-93dd-00163e000002 與 df878007-80da-11e9-93dd-00163e000003 與比較一億次):

1 row in set (27.67 sec)

但如果是一開始就不同的話 (這邊是選擇 df878007-80da-11e9-93dd-00163e000002ef878007-80da-11e9-93dd-00163e000003) 會快很多:

1 row in set (2.45 sec)

但如果與數字相比的話 (這邊是 2=3 這樣的條件去比):

1 row in set (0.96 sec)

可以看數字在這邊的優勢,另外也是在說明,如果你用的是 time-based ordering 的 UUID,要考慮會遇到這個可能會發生的效能問題。

再來是玩 UUID 的三種不同的儲存方式對於寫入效能的差異,分別是 CHAR(36) (32 bytes 的 hex 加上四個 -)、base64 (用 CHAR(22) 存) 與 BINARY(16),可以看出來 BINARY(16) 因為佔用空間比較小的關係,是可以高速寫入持續最久的,再來是 base64,最差的是 CHAR(36)

後面給了兩個 workaround,第一個算是定義了另外一種產生 128 bits 的方式,第二個則是想辦法把 UUID 對應到數字。

這在 MySQL 的環境裡面算是被討論的很久的主題了。(我猜在 PostgreSQL 應該也是,不過 PostgreSQL 的社群沒跟那麼久...)

把 blog 搬到 t4g.small 上

算了一下成本還可以接受 (機器 + 空間 + 流量),就把 blog 搬到 AWSt4g.small (ARM) 上,理論上頁面的速度應該會快不少,過幾天等穩定性沒問題後就來買 RI...

x86-64 轉到 ARM 上面,主要是 Percona Server 目前沒有提供 ARM binary 的 apt repository,所以就改用 MariaDB 了。

其他的倒是都差不多,目前的 Ubuntu + nginx + PHP 沒什麼問題,跑一陣子看看...

最近討論到的二分搜尋法...

應該是直接在 Hacker News 上看到的東西,有人丟出一個二分搜尋法實做,宣稱比標準實做快不少:「Binary Search: A new implementation that is up to 25% faster (github.com)」。

實做的程式碼放在 GitHub 的「scandum/binary_search」這邊,讀了 source code 後可以看到一臉就要利用現代 CPU 預測平行化的能力加速 XDDD

另外看了 Hacker News 上的討論,這種寫法會透過 CPU 預測平行化的能力善用記憶體頻寬,這應該是測起來比較快的主因。

不過這只算是個開頭,丟出一些方向讓社群可以研究,實際上還是得看看負面影響的部份,像是比較舊的 CPU 會不會有很重的 penalty (overhead),以及其他類型 CPU 上的情況...

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,是個頗值得投資的項目。

各種 Java 的版本

看到這則 tweet,提到 Java 的支援度:

主要是裡面有張圖列出了目前市場上有的選擇,可以當關鍵字來查:

目前看起來如果要 Java 8 只有三個方案,其中有過 TCK 的只有兩個,看起來用 Amazon Corretto 算是個還不錯的選擇?

計算圓周率 Pi 的公式...

Twitter 上看到這個:

利用這個公式 (Bailey–Borwein–Plouffe formula),可以直接對二進位 (四進位、八進位、十六進位、...) 直接計算出某個位數的值...

Google Play Store 將支援 Brotli 壓縮

在「Intern Impact: Brotli compression for Play Store app downloads」這邊介紹了 Google Play Store 引入 Brotli 的情況。

選擇 Brotli 除了是 Google 自家研發出來的東西以外,另外是考量到 Brotli 的壓縮與解壓縮速 (尤其是後者) 不會增加太多,卻可以多出不少壓縮率。維基百科這邊說明的是文字的部份:

Replacing deflate with brotli typically gives an increase of 20% in compression density for text files, while compression and decompression speeds are roughly unchanged.

不過實際在 Google Play Store 上測試 binary 的效果也不錯:

當然,如同之前提到的「Google 再次改善 Android 的 APK 更新,讓下載的量更小」,在去年 12 月時 Google 對於背景更新的下載 File-by-File 的更新來降低流量 (但在手機上會需要大量的 CPU 資源計算,不過因為是背景 idle 時跑而不會影響使用者,所以被採用),透過這兩個改善互相搭配繼續壓低流量。

在接下來的幾個禮拜會生效:

Brotli compression for app downloads is rolling out now, and users should start to enjoy the benefits over the coming weeks.

Google 再次改善 Android 的 APK 更新,讓下載的量更小

Google 的人再次更新了演算法,將下載的量再次減少,從本來的 47% 降到 65%:「Saving Data: Reducing the size of App Updates by 65%」。

今年七月的時候,更新演算法導入了 bsdiff,使得本來要抓整包 APK 的量,變成抓 diff 的部份,這使得下載的流量降了 47%:「Improvements for smaller app downloads on Google Play」。

Using bsdiff, we were able to reduce the size of app updates on average by 47% compared to the full APK size.

現在則改成不直接對 APK 做 diff,而是對未壓縮的檔案做,再把差異包起來,則可以降到 65%:

Today, we're excited to share a new approach that goes further — File-by-File patching. App Updates using File-by-File patching are, on average, 65% smaller than the full app, and in some cases more than 90% smaller.

主要的原因在於 APK 的壓縮使用的 DEFLATE 演算法對於變更非常敏感,改變一個字元就會讓後續整串都改變,導致差異很大而跑 diff algorithm 的效果不好:

用 File-by-File 的好處主要來自於是對未壓縮的檔案比較差異,這代表沒有變動的檔案完全不會進來攪和,而對 binary 檔案的效果也比較好 (大部份的程式碼還是一樣)。不過這對於已經有壓縮的圖片的效果就比較差了,這也是 APK 一般肥大常見的原因。

有兩件事情值得注意的,一個是 Google 的人為了使用者體驗,只有在 auto update 時才會走 File-by-File 的更新,主要原因是 File-by-File 的解開速度慢不少:

For now, we are limiting the use of this new patching technology to auto-updates only, i.e. the updates that take place in the background, usually at night when your phone is plugged into power and you're not likely to be using it. This ensures that users won't have to wait any longer than usual for an update to finish when manually updating an app.

另外一個是,這個新方式讓 Google 每天省下 6PB 的流量,如果流量都是平均打散的話,大約是 600Gbps:

The savings, compared to our previous approach, add up to 6 petabytes of user data saved per day!

這種規模改善起來很有感覺 XDDD

Facebook 備份 MySQL 資料並且確認正確性的方法

Facebook 再多花了一些篇幅數對於 MySQL 資料備份以及確認正確性的方法:「Continuous MySQL backup validation: Restoring backups」。

首先是 Continuous Restore Tier (CRT) 這塊,可以看到他們在這塊很仰賴 HDFS 當作備份的第一層基地,包括了 Full logical backups (用 mysqldump)、Differential (diff) backups 以及 Binary log (binlog) backups (stream 進 HDFS)。

另外上了 GTID,對於後續的處理會比較方便:

All of our database servers also use global transaction IDs (GTIDs), which gives us another layer of control when replaying transactions from binlog backups.

在 CRT 這塊可以看到其實是拿現成的工具堆起來,不同單位會因為規模而有不同的作法。真正的重點反而在 ORC Restore Coordinator (ORC) 這塊,可以看到 Facebook 開發了大量的程式將回復這件事情自動化處理:

在收到回復的需求後,可以看到 Peon 會從 HDFS 拉資料出來,並且用 binlog replay 回去:

Peons contain all relevant logic for retrieving backups from HDFS, loading them into their local MySQL instance, and rolling them forward to a certain point in time by replaying binlogs. Each restore job a peon works on goes through these five stages[.]

也是因為 Facebook 對 MySQL 的用量大到需要自動化這些事情,才有這些東西...

Golang 1.7

Golang 1.7 主打更小的 binary size:「Smaller Go 1.7 binaries」:

Typical programs, ranging from tiny toys to large production programs, are about 30% smaller when built with Go 1.7.

還附了一張經典的「Hello, world」程式的分析:

由於現代 CPU 的速度與 L1/L2/... cache 有緊密關係,當 binary size 變小時,常常會伴隨著 memory access 變快 (因為 hitrate 提昇),所以 binary size 也是效能指數蠻重要的一環。