關於 GitLab 的 SQL 設計

今天「My notes on Gitlab's Postgres schema design (2022) (shekhargulati.com)」這篇上 Hacker News 首頁 (看起來因為是在 pool 的關係,在第一頁卡很久...),文章「My Notes on GitLab Postgres Schema Design」是作者在 2022 年七月的時候分析了 GitLabstructure.sql 的資料庫設計整理出來的心得 & 感想,裡面有不少東西,不過這邊想補充個背景知識 (姿勢?):

RDBMS 在系統架構裡面,相較於其他的元件,是個很難 scale out 的東西 (i.e. 加更多機器得到更多效能),所以遇過 scalability 問題的架構師,會很習慣避開在 RDBMS 上面跑各種功能,有其他方式可以做的就拆出去用容易 scale out 的工具來做,非不得已才上 RDBMS。

而就算要塞進 RDBMS 裡的資料,能省的還是要省,畢竟宣稱自動幫你處理資料庫 scale out 的技術 (像是 CockroachDBTiDB) 其實沒想像中萬能,還是需要開發者改寫以前大惡搞的 SQL query (一個 terminal 列不完那種)。

而你心裡也有底,如果 scale out 不是條好的路,那麼只好 scale up (i.e. 加大機器的 CPU & RAM),而 scale up 總是有極限,真的遇到自己被迫要處理 sharding 的時候,DBOps/DBA 與 Dev 的臉都很臭... (一堆 JOIN 要改成拉回 application 端自己湊,或是有 ProxySQL 這種東西幫你處理,但是發現 ProxySQL 去後面資料庫拉太多資料幫你組反而很慢 !@#$%)

但另外一方面,現在已經不是 2005 年 64GB RAM 的伺服器是個天價的年代... 硬體的成長已經長到在 AWS 雲端上面可以租到給 SAP 用的 24TB RAM 的機器 (u-24tb1.112xlarge),而地端找個 server 也都有 15TB RAM (POWEREDGE R940),所以很容易把所有資料都塞到記憶體裡面搞,加上 NVMe 的讀寫速度比以前 HDD disk 快多了。

記得這兩件都是現實,然後再回來看文章內容與其他的討論,用不同的現實就會有不同的想法出現。

GitLab 的設計有他當時的限制以及想法,這些是外面的人看不到的,也就不好批評對錯。

HashiCorp 內 scale 的方法

去日本前在 Hacker News 上看到「Squeeze the hell out of the system you have」這篇,用作者的名字翻了一下 LinkedIn,看起來講的是 HashiCorpSRE 事情:「Dan Slimmon」。

看的時候可以注意一下,文章裡面的觀點未必要認同,大多是他自己的看法或是想法,但裡面提到很多發生的事情,可以知道 HashiCorp 內目前搞了什麼東西。

從 LinkedIn 的資料可以看到他從 2019 就加入 HashiCorp 了,所以文章一開頭這邊講的同事應該就是 HashiCorp 的同事:

About a year ago, I raised a red flag with colleagues and managers about Postgres performance.

往下看可以看到他們有遇到 PostgreSQL 的效能問題,然後每次都是以 scale up (加大機器) 的方式解決,考慮到 HashiCorp 的產品線,我會猜應該是 Terraform Cloud 這個產品線遇到的狀況。

然後在後面提到的解法則是提到了 codebase 是 Rails,他們花了三個月的時候不斷的重複 profiling + optimizing,包括 SQL 與 PostgreSQL 的設定:

Two engineers (me and my colleague Ted – but mostly Ted) spent about 3 months working primarily on database performance issues. There was no silver bullet. We used our telemetry to identify heavy queries, dug into the (Rails) codebase to understand where they were coming from, and optimized or eliminated them. We also tuned a lot of Postgres settings.

另外一組人則是弄了 read-only replication server,把 loading 拆出去:

Two more engineers cut a path through the codebase to run certain expensive read-only queries on a replica DB. This effort bore fruit around the same time as (1), when we offloaded our single most frequent query (a SELECT triggered by polling web clients).

這兩個方法大幅降低了資料庫的 peak loading,從 90% 降到 30%:

These two efforts together reduced the maximum weekly CPU usage on the database from 90% to 30%.

可以看到都還沒用到 sharding 的技巧,目前硬體的暴力程度可以撐很久 (而且看起來是在沒有投入太多資源在 DB-related tuning 上面),快撞到的時候也還可以先用 $$ 換效能,然後投入人力開始 profiling 找問題...

Amazon S3 的新數字

Werner Vogels 寫了一篇在回憶 Amazon S3 的文章:「Building and operating a pretty big storage system called S3」,裡面有個是他這個層級比較容易取得公開權限的資料:

有標注「S3 by the numbers (as of publishing this post).」,所以是 2023 年七月現在的數字。

雖然很明顯的還是避開談總大小,但有提供目前的 S3 object 數量是 280 兆,以及 request 量是每秒 1 億次。

搭配之前公開過的數字 (出自維基百科上的「Amazon S3」條目),上次公佈是在 2021 年三月的時候宣布超過 100 兆,所以過了兩年的時間已經到 280 兆了:

Amazon Web Services introduced Amazon S3 in 2006. Amazon reported it stored more than 100 trillion objects as of March 2021, up from 10 billion objects in October 2007, 14 billion objects in January 2008, 29 billion objects in October 2008, 52 billion objects in March 2009, 64 billion objects in August 2009, 102 billion objects in March 2010, and 2 trillion objects in April 2013.

低 Downtime 將 4TB 的 PostgreSQL 9.6 資料庫升級到 13 的故事

前幾天在 Hacker News 首頁上看到的文章,講怎麼把一個 4TB 的 PostgreSQL 從 9.6 升級到 13 的故事:「How we upgraded our 4TB Postgres database (retool.com)」,原文在「How Retool upgraded our 4 TB main application PostgreSQL database」,翻了一下 LinkedIn,這篇文章的作者 Peter Johnston 在 Retool 掛的是 Security Software Engineer,另外他也有在 Hacker News 上的討論出現 (帳號是 mrbabbage),可以搜尋翻翻看他的回覆。

看完文章後發現方法的概念其實不難,主要是要找到對的工具來用。基本的想法是先生出一個 initial dump,然後架構 logical replication,接下來就是處理各種因為在 4TB data 這個 scale 下會遇到的問題。

主要用到的工具是 Citus Data 的 Warp:「Citus warp: Database migrations without the pain」,不過這個工具的限制是表格必須都有 single column primary key,所以他們為了這次轉移也有小改 database schema 配合 Warp 的要求:

We had to do a bit of finagling to coax Warp into processing our database. Warp expects all tables to have a single column primary key, so we had to convert compound primary keys into unique constraints and add scalar primary keys. Otherwise, Warp was very straightforward to use.

另外針對比較大的兩個 append-only 的表格 (分別是 2TB 與 x00 GB) 做處理,在 initial dump 的階段不對這兩個表格做 replication,而是透過自製的 Python script 搬移:

To handle the two massive tables we skipped in Warp, we wrote a Python script to bulk transfer data from the old database server to the new.

然後是 foreign key 相關的關閉與重啟,這算是 RDBMS 在大量資料的 dump & restore 的標準作業了:

As you can see from the runbook above, one of the steps we had to do was to turn off and then re-enable foreign key constraint checks.

最後執行下去,整個 downtime 只有十五分鐘:

We scheduled the maintenance window late on Saturday, October 23rd, at the lowest period of Retool cloud traffic. With the configuration described above, we were able to bring up a new database server at version 13 in around 15 minutes, subscribed to changes at our 9.6 primary with logical decoding.

另外也有提到有計畫要 sharding,之後 main database 就有機會被拆小:

We anticipate we’ll have sharded our database by the end of that support window, and be performing our next substantial version upgrades incrementally.

整個計畫的核心概念不難,主要是要怎麼順出來並且執行...

Mac 上 sprintf 的 scalability 問題

Hacker News 上看到個有趣的 scalability 問題,在 Mac 上的 sprintf() 因為有 lock 造成的 scalability 問題:「Curious lack of sprintf scaling (aras-p.info)」。

作者注意到 Mac 在多 CPU 下 sprintf() 會有 scalability 的問題,要注意到這邊的 Y 軸是對數比例:

用了 std::stringstream << 反而更慢 (作者還酸了一句「Zero cost abstractions」):

然後用了 Instruments 跑 profiling 找問題,可以看到看起來跟 locale 有關:

一般的情況下應該不會是問題,但如果是需要大量 sprintf() 組字串的人就會比較要注意了。

在「What else can we do?」這段有提到一些解法,包括了 stb_sprintf 當作替代品,以及 {fmt} 作為 iostreams 的替代品,然後另外是利用 to_chars 來解決,如果只是要把數字轉成字串。

算是蠻有趣的 bug hunting 過程,對於開發者來說,一般性的重點還是在 profiling,找到對的問題然後再往下提出解法...

GitHub 的 MySQL 架構與數字

前幾天 GitHub 有寫一篇文章提到他們的 MySQL 是怎麼 scale 的,另外裡面也有一些數字可以看:「Partitioning GitHub’s relational databases to handle scale」。

他們最主要的 database cluster 叫做 mysql1,裡面有提到 2019 年的時候這個 cluster 是 950K qps,其中 primary 有 50K qps:

In 2019, mysql1 answered 950,000 queries/s on average, 900,000 queries/s on replicas, and 50,000 queries/s on the primary.

在 2021 年的時候變成 1.125M qps,其中 75K qps 在 primary 上:

Today, in 2021, the same database tables are spread across several clusters. In two years, they saw continued growth, accelerating year-over-year. All hosts of these clusters combined answer 1,200,000 queries/s on average (1,125,000 queries/s on replicas, 75,000 queries/s on the primaries). At the same time, the average load on each host halved.

另外這幾年比較成熟的方案都拿出來用了,包括用 ProxySQL 降低連線數的壓力 (connection pool 的概念):

[W]e started using ProxySQL to reduce the number of connections opened against our primary MySQL instances.

ProxySQL is used for multiplexing client connections to MySQL primaries.

另外用 Vitess 協助 sharding 之間的轉移:

Vitess is a scaling layer on top of MySQL that helps with sharding needs. We use its vertical sharding feature to move sets of tables together in production without downtime.

這兩套應該是已經蠻成熟的了... 另外也可以發現老方法還是很好用,就算在 GitHub 這種量還是可以暴力解決很多事情。

AWS Cloud 的用法

Hacker News Daily 上看到這則,分享了 AWS (他的前東家,超過八年) 的使用經驗:

除了可以在 Twitter 上看以外,也可以用 Thread reader 直接讀整條 thread,應該也還算清楚:「This is how I use the good parts of @awscloud, while filtering out all the distracting hype.」。

這邊的經驗談主要是在 web 與 app 相關的服務這塊:

有講到 AWS 的業務其實圍繞在 scalability 上發展,但這對 startup 可能反而是扣分,因為暴力法解反而可以大幅簡化架構換得 agile (而讓 startup 存活下來)。

另外從團隊的開發成本來看,這些 scale 的技術增加了開發成本,產生了很多開發上的限制,這些觀點也有點帶到「Premature optimization is the root of all evil」在講的事情:

最後的結論可以看到一些列表:

除了 DynamoDB 的意見不同外 (這邊提到的 DDB),其他的我都可以接受...

Google 的 Cloud Spanner

GoogleCloud Spanner 這個服務拿出來賣了:「Introducing Cloud Spanner: a global database service for mission-critical applications」,以及說明的「Inside Cloud Spanner and the CAP Theorem」。

Cloud Spanner 的規劃上是希望有 RDBMS 的能力 (像是 ACID 特性),又有強大的擴充能力 (scalability) 與可用性 (availability):

Today, we’re excited to announce the public beta for Cloud Spanner, a globally distributed relational database service that lets customers have their cake and eat it too: ACID transactions and SQL semantics, without giving up horizontal scaling and high availability.

在說明裡有提到 Cloud Spanner 是做到 CAP theorem 裡面的 CP:

The purist answer is “no” because partitions can happen and in fact have happened at Google, and during some partitions, Spanner chooses C and forfeits A. It is technically a CP system.

然後把 A 拉高到使用者不會在意 downtime 的程度:

However, no system provides 100% availability, so the pragmatic question is whether or not Spanner delivers availability that is so high that most users don't worry about its outages.

當然,比較讓人爭議的是 Twitter 上 Google Cloud 官方帳號的 tweet,直接講同時解決了 CAP 三個條件:

價錢不算便宜,不過對於想要找方案的人至少有選擇...