Google 提出的 GoogleSQL (Pipe 版本的 SQL 改良)

看到「SQL Has Problems. We Can Fix Them: Pipe Syntax In SQL」這個研究投稿,PDF 檔案在 1004848.pdf 這邊。

Google 提出了 GoogleSQL 改善本來 SQL 的可讀性問題,另外也對 SQL optimizer 更有幫助。

直接拿 PDF 裡面的例子來說明,把本來是這樣的 SQL:

SELECT c_count, COUNT(*) AS custdist
FROM
  ( SELECT c_custkey, COUNT(o_orderkey) c_count
    FROM customer
    LEFT OUTER JOIN orders ON c_custkey = o_custkey
         AND o_comment NOT LIKE '%unusual%packages%'
    GROUP BY c_custkey
  ) AS c_orders
GROUP BY c_count
ORDER BY custdist DESC, c_count DESC;

變成這樣的:

FROM customer
|> LEFT OUTER JOIN orders ON c_custkey = o_custkey
AND o_comment NOT LIKE '%unusual%packages%'
|> AGGREGATE COUNT(o_orderkey) c_count
GROUP BY c_custkey
|> AGGREGATE COUNT(*) AS custdist
GROUP BY c_count
|> ORDER BY custdist DESC, c_count DESC;

可以看到這個語法除了變好讀以外,也指示了 SQL optimizer 怎麼去過濾與組合資料。

依照 Google 的說明,GoogleSQL 已經在許多 Google 的系統上實作了:

We’ve implemented pipe syntax in GoogleSQL, the SQL dialect and implementation shared across all1 SQL systems at Google including F1, BigQuery, Spanner and Procella, and the open-source release, ZetaSQL. GoogleSQL is a shared, reusable component, enabling many systems to share the same SQL dialect. This shared component allowed implementing pipe syntax in one place and then enabling it across many products.

裡面提到的 ZetaSQL 可以在 pipe-syntax.md 這邊看到。

我記得其他家也有類似的東西,PRQL 也是在解決類似的問題,不過語法走向不太一樣。

可以看看怎麼收斂...

用 Google Calendar 當作範例,設計資料庫的結構

Hacker News 上看到「Database Design for Google Calendar: A Tutorial (databasedesignbook.com)」這個,原文在「Database Design fo Google Calendar: a tutorial」這,這邊的資料庫指的是關聯式資料庫 (RDBMS),像是常見的 MySQL 或是 PostgreSQL

而有趣的點是在 Hacker News 的討論,不過還是先帶一下原文的內容。

原文主要就是怎麼設計以及怎麼下 SQL query 會比較好,另外有些「作者經驗」說明為什麼這樣設計,不過這部份很吃場景,所以當作故事聽過去比較好,自己遇到的時候需要通盤考慮;另外有些地方提到的是 trade off,沒有最佳解。

然後回到 Hacker News 上面,在 id=41044011 這邊算給你看,幹嘛用 RDBMS 設計一堆架構:

A random event from my calendar serializes to 740 bytes of iCalendar. An extremely busy calendar containing one event every 15 minutes 9am-5pm 7 days per week only contains 11680 entries for a full year, which works out to around 8MB.

Would suggest instead of designing a schema at all, a calendar is a good example of a problem that might be far better implemented as a scan. Optimizing an iCalendar parser to traverse a range of dumped events at GB/sec-like throughputs would mean the above worst-case calendar could be scanned in single-digit milliseconds.

Since optimizing a parser is a much simpler problem to solve once than changing or adding to a bad data model after it has many users, and that the very first task involving your new data model is probably to write an iCalendar importer/exporter anyway, I think this would be a really great trade off.

因為每個 entry 也不到 1KB (740 bytes),就算是超級忙的 calendar,每週七天的 9am~5pm 都是每 15mins 一個會議的情況下,全滿也才 8MB... XDDDDDDDD

另外幫他補充說明,他這邊省略掉的是 calendar 接近於 share-nothing 架構,所以每個人直接拆開來獨立存放很容易 scale,你可以用個 scalable key-value solution 存 JSON 就好。

不過文章的原作者主要的目的是找一個大家熟悉但又有點複雜性的東西,示範 database schema 設計的考慮,我不會直接說文章原作者錯啦...

但的確一開始看到文章不會想到暴力解還蠻... 暴力的?XDDD

Amazon DocumentDB (with MongoDB compatibility) 支援壓縮

看到「Amazon DocumentDB announces improvements to document compression」這則公告提到 Amazon DocumentDB 5.0 支援壓縮了:

These compression benefits are now supported in Amazon DocumentDB 5.0 instance-based clusters in all regions where Amazon DocumentDB is available.

官方宣稱可以壓七倍:

Compressed documents in Amazon DocumentDB can be up to 7 times smaller than uncompressed documents, leading to lower storage costs, I/O costs, and improved query performance.

在「Managing collection-level document compression」這份文件裡面則是提到演算法是 LZ4

Amazon DocumentDB uses the LZ4 compression algorithm to compress documents.

因為大家都猜 DocumentDB 後面是 PostgreSQL,所以我就查了一下 PostgreSQL 這邊的資料,可以從 PostgreSQL 14.0 (2021/09/30) 的 Release Notes 看到支援 LZ4:

Add ability to use LZ4 compression on TOAST data (Dilip Kumar)

This can be set at the column level, or set as a default via server parameter default_toast_compression. The server must be compiled with --with-lz4 to support this feature. The default setting is still pglz.

而 DocumentDB 則是 2019 年一月的時候出的,大概是 DocumentDB 5.0 用到的 PostgreSQL 比較新所以可以支援了...

在 PostgreSQL 裡,當 UUID 當作 Primary Key 時要怎麼處理

繼續清 tab,在「PostgreSQL and UUID as Primary Key (maciejwalkowiak.com)」這邊看到的,原文是討論 PostgreSQL 要怎麼處理 PK 是 UUID 的情況:「PostgreSQL and UUID as primary key」。

文章開頭作者就說了,這篇不是要戰 PK 要不要用 UUID,而是已經決定要用了 (i.e. 通常不是你決定的),在接手以後要怎麼用比較好:

Considering the size of UUID it is questionable if it is a right choice, but often it is not up to us to decide.

This article does not focus on "if UUID is the right format for a key", but how to use UUID as a primary key with PostgreSQL efficiently.

首先是 PostgreSQL 從 8.3 版 (2008 年) 就支援 UUID 的資料型態了,這點從 release note 可以看到:「PostgreSQL 8.3.0」,所以要當 PK 的話沒什麼道理不考慮他。

UUID 的空間上就是 128-bit data (16 bytes),相比於 TEXT 會省蠻多的,尤其 PK 常常會被其他表格 reference 到 (像是 foreign key) 會在其他表格也省下來:

Table that uses text is 54% larger and the index size 85% larger.

第二點則是考慮到 UUID 本身的特性,以前的 UUID 因為是亂數生成的 (通常會用 UUIDv4),對寫入 B-tree 類的資料結構不是很有效率,改用 UUIDv7 (差不多是這兩年陸陸續續發展出來的規格) 會得益於與 timestamp 有關,對 B-tree 寫入的效率會好很多:

Random UUIDs are not a good fit for a B-tree indexes - and B-tree index is the only available index type for a primary key.

B-tree indexes work the best with ordered values - like auto-incremented or time sorted columns.

UUID - even though always looks similar - comes in multiple variants. Java's UUID.randomUUID() - returns UUID v4 - which is a pseudo-random value. For us the more interesting one is UUID v7 - which produces time-sorted values. It means that each time new UUID v7 is generated, a greater value it has. And that makes it a good fit for B-Tree index.

作者的測試可以看到寫入速度與 UUIDv4 相比比快不少:

BUT we can clearly see, that inserting UUID v7 is ~2x faster and inserting regular UUID v4.

總結來說,當 PK 已經決定是 UUID 後,主要就是這兩個重點可以注意的,當然 Hacker News 上更熱鬧的是兩派人馬在吵要用 integer 類的 SERIAL/BIGSERIAL 還是用 UUID,那又是另外一個話題了...

如果早個二十年前對 memory size 斤斤計較的情況下,答案鐵定是 integer 類的,但年代不同了...?

直接在 library 層將 MongoDB 用法轉換成 PostgreSQL 底層的 Pongo

看到這個「Mongo but on Postgres and with strong consistency benefits (github.com/event-driven-io)」算是另外一種用 PostgreSQL 取代 MongoDB 的嘗試,先前其他的方案是 proxy server 的方式實作 (像是 FerretDB),也就是 TCP 裡面傳的東西還是 MongoDB protocol,然後 proxy server 會轉譯成 PostgreSQL 的 SQL 語法。

這個作法的好處是不用管既有 application 是什麼程式語言開發的,另外改動比較少 (改個連線資訊 + 然後把目前還不支援的功能改寫),但缺點是多了一組 service 要維護 (如果是 HA 的話又還要設定 failover 或是 load balancer 的機制)。

Pongo 的作法則是移到 library 這邊做掉,所以就有程式語言的限制了:這個專案是用 TypeScript 開發,所以會是 JavaScript + TypeScript 生態系的方案。

不過好處就很明顯了,少了一組 service 要維護 (如果包括 HA 機制的話可能是兩組或三組),另外因為轉譯的部分在 application 端處理,沒有了 proxy server 也等於少了一個可能的 bottleneck。

這幾天上 Hacker News 後看 commit 頗熱鬧,從 Releases 頁可以看到連續出新版本。

不過... 不考慮直接用 PostgreSQL 嗎?

Peter Zaitsev 抱怨 Oracle 對 MySQL 的態度

Percona 家的老大 Peter Zaitsev 寫了一篇紋章抱怨 OracleMySQL 的態度:「Is Oracle Finally Killing MySQL?」。

其實大家都有感覺,2009 年 Oracle 買下 Sun (當時擁有 MySQL 的公司) 後的前幾年還有些動作,後面整個就停住了,這也導致了市場上可以看到明顯的變化,不少人都開始關注並且使用 PostgreSQL

隔壁 MariaDB 透過 SPAC 弄的灰頭土臉後縮編不少,自己弄的 engine 還是跟 InnoDB 有一段差距,功能上也沒有比較優秀的地方...

雲端降低管理成本也是一個讓 PostgreSQL 興起的推手,如果在地端的話,MySQL 的管理工具還是成熟不少 (畢竟就是時間累積出來的),但在雲端上面就接包的好好的了...

MySQL 8.4.0 出版

這是 LTS 版本,可以預期後續 Percona 也會準備對應的 distribution 使用。

先查了一下 MySQL 8.0 的 EoL 日期,目前是訂在 2026/04/30,大約還有兩年的時間可以準備。

回來看 8.4,從「What Is New in MySQL 8.4 since MySQL 8.0」這邊可以看到官方整理與 8.0 的差異。

InnoDB 這邊有不少預設參數調整,算是配合現在硬體愈來愈好而調整的數字。

在「MySQL Replication: tagged GTIDs」這邊有提到 replication 時用到的 GTID format 有更新,多了 tag 的欄位。看起來在 upgrade 時會相容舊格式,但要 rollback 或是 downgrade 時得注意到這邊的問題...

然後在「Replication SQL syntax」這邊有提到移除掉很多舊語法,像是 CHANGE MASTER TORESET MASTER 這種語法都被改成新的語法了,要重新背。

整體看起來沒有太多變化?

Gitea 1.22 預定會有的改變

Gitea 1.22.0 出了 RC:「Gitea 1.22.0 Release Candidate」,裡面整理出 1.22 會有的重大改變。

一個是之前提到的 UI 架構改變:「Gitea 預定淘汰掉 jQuery + Fomantic-UI + Semantic-UI,改用 Tailwind CSS」。

另外看到放掉舊版資料庫的消息,放掉 MySQL 5.7 與 PostgreSQL 10 & 11,這兩個都是 EoL 的版本。

Support for MySQL 5.7, PostgreSQL 10 and 11, and MSSQL 2008 is dropped.

其中 MSSQL 2008 的部分,維基百科的 Microsoft SQL Server 說還有支援 (補一下是 oldid=1220997610 這個版本),不過從微軟官方網站上查「Extended Security Updates for SQL Server and Windows Server」可以看到 2022 年就已經收掉了...

整體上看起來這個版本的重心就是放在 UI,其他的功能比較少一些...

UUID 的 UX

在「The UX of UUIDs (unkey.dev)」這邊看到的紋章,原文在「The UX of UUIDs」。

裡面有不少是有幫助的建議,像是第一個建議是把 UUID 裡面的 - 拿掉,這樣對於 copy 比較方便 (畢竟大多數人應該是 copy UUID,不會是念出來?)。

第二個建議是加上 prefix,這點不一定侷限在 UUID,只要是 token 上面都很適合。這個在不少系統上應該都有看過,像是 GitHub 的 token,或是 AWS 的 token 都算是這類。

文章裡面沒有提到,但這個建議也可以幫助你在 CI 上設定 regex,擋下把 secret token 寫進去的行為。

第三個提到用 base58,一方面是減少長度,另外一方面是想要避免 1IiLl0Oo 的問題,這點我覺得還好... 既然都是 copy & paste 了,我覺得拿 base62 (i.e. 大小寫加上數字) 不錯,這避免特殊字元無法選擇到,也就是文章裡面第一個建議。

第四個建議是建議重新思考 range,因為 UUID 的 128-bit range 很大,但不是所有應用都需要用到這麼大的範圍確保 collision-free (於是可以當 primary key)。

這點讓我想到 X (Twitter) 當初發表的 Snowflake ID,在 Twitter 這種規模下 64-bit range 也已經夠用。

後面的文章內容就是在推銷自家東西,我就... 跳過了。

關於 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 的設計有他當時的限制以及想法,這些是外面的人看不到的,也就不好批評對錯。