解決不了問題,就解決提出問題的人

128 Followers
·
Follow

大量資料、大量連線、大量運算、資料多樣化

許多人在評估資料庫的能力時,都只停留在資料所佔有的空間有多少。實際上,資料庫並不是只有把資料儲存下來而已,它還有很多問題是在儲存之後需要面對的。標題已經破題了,就是四大問題的分類,除了用於評估資料庫系統設計之外,也必須用這些面向來評估你的資料應用形態。當資料庫系統設計與資料應用形態相符合時,才是最適合的選擇。

而這四大問題當然不會單獨發生,也不會是恆久不變的形態。所以在評估、上線到維護,整個系統的生命週期的過程中,都應該定期檢視資料需求的傾向,以作為資料庫調教的依據。

然而,由於不會有無限的資源,所以在成本考量下,在評估之後,勢必還是得做出某種程度的取捨,而這才是我們需要呈現的專業價值。當然我們都知道錢花得更多會越好,但你也需要知道怎樣才是花在刀口上。

本文不適合財大氣粗的朋友閱讀 😏

本文以 PostgreSQL 資料庫為例,你也可以套用在其他資料庫系統上。

Image for post
Image for post

大量資料是傳統的評估方式,使用磁碟佔用空間來計算,例如 100 TB 的資料量,或是以資料筆數,像是 100 億筆資料之類的。不過這種喊出來的數字其實沒有意義,因為你需要考量的是硬體成本和資料回應需求的差距。

硬體面向除了很直觀的存取速度之外,其實儲存裝置也很佔機房空間,而儲存年限的設定是資料政策面必須要定義的。

現代的資料庫系統,只是把資料儲存起來的話,都只看硬體上限即可。實際的使用上,簡單來看,記憶體的大小才是關鍵。在一般的條件過濾應用,所參考的索引如果能完整放進記憶體中,那才會符合一般對「可以處理」的想像。

所以,考量點其實應該是你的資料應用每次「讀取」多少資料,來評估資料庫是否可以處理「大量資料」,將會更有意義。

例如一般使用 B-tree 索引,索引比資料大的情況比比皆是。在 PostgreSQL 上你還可以選擇使用 BRIN 索引來調整索引的大小,通常用於資料倉儲方面的需求會很有效果。當然,如何設計分散式資料庫系統來達到水平擴充,也會是你需要思考的課題。

大量連線

大量連線指的是同時間(concurrent)的連線數量。還可以區分為兩個面向,一種是大量的連線,存取不同的資料;另一種則是大量的連線,存取相同的資料。兩者共通的設計上都必須要有 Connection Pool

你需要先測試一下資料庫系統的連線數與效能分數的關連,PostgreSQL 上基本會使用 pgbench 作為標準測試。我的經驗上,PostgreSQL 最佳效率點大約會落在 500~600 個連線數之間。超過這個數目的需求,就是屬於大量連線。而你必須在你的環境上實際實驗過才能確認這個臨界點。

一般大量連線的考量是儘可能服務更多的資料要求,而不是單一查詢的效率。其中 idle 的連線是最不應該出現的,有關 timeout 方面的設定需要細心地評估和溝通。在大量連線的情況下,資源的使用率很重要,必須確保每一個在資料庫裡的連線都真正在存取資料。

存取不同資料的大量連線,增加記憶體的大小是很有幫助的;而存取相同資料的大量連線,例如訂票的需求,則需要應用系統的協助,例如透過快取的設計,避免無謂的資料操作。

大量運算

大量運算一般的標的是資料分析方面的應用,假設上會是運算時間較長的運算,時間單位是以分鐘計的。和大量連線的系統設計可能會矛盾的是,你應該降低最大連線數的設定,讓長時間的運算可以擁有更多資源來處理。

你必須要很基本地理解到,再快的 CPU,一個 CPU 核心,一次也只能做一件事,越多連線數所產生的 Context Switch 只會造成更多無謂的延遲。

除了顯而易見的 CPU 和 Memory 資源高低之外,I/O 問題反而是最先浮現的。由於多數資料是來自於線上交易產生,為了平衡負載,會設計 OLTP 和 OLAP 的資料需求轉換。具體來說,就是把資料抄寫出來做資料分析之用,時常都不只抄寫一份。

在 PostgreSQL 中,有 Stream Replication 和 Logical Replication 可做運用。在這裡不談細節的運作,但原則上,你需要確保每一份抄寫都是有資料處理意義的。例如備份、異質資料源轉換、資料標準化、統計分類等等。

重要提醒,資料庫系統是有運算能力的。

許多優秀的分析軟體提供了資料庫系統所沒有的資料運算方式,所以會有大量的資料匯入匯出在批次執行很正常。但資料庫也不是什麼都沒有,設計上應該儘可能讓資料庫做完資料庫可以做的運算後,再匯出到特定的分析系統之中。由於資料傳輸的成本很高,所以在資料庫內先行運算,儘量降低需要傳輸的資料量,本身就能夠直接加速產生運算的結果。

PostgreSQL 裡提供了各式的彙總運算,請善加利用。

資料多樣化

當企業的業務量提升到穩定發展的層次時,業務樣態逐步增加的話,其資料多樣化的問題自然就會產生。所謂資料多樣化指的通常是資料型別繁多以及異質資料庫系統整合的問題。

資料型別的問題在於針對該資料型別的運算處理是否完備,否則只是儲存起來,無法檢索或相關運算,只算是做了半套。以近來許多人使用的 JSON 型別為例,能有索引支援快速檢索指定路徑內容的話,放在這樣的資料庫裡才算是可以便利地使用。

其他還有像是地理資訊資料、全文檢索資料、時間序列資料等等,甚至你需要專用資料庫系統來處理這些類型的資料。

在資料多樣化這點上,我認為關鍵在於資料流的掌握。一個方向是減少資料庫系統的種類,降低程式設計和管理的複雜度;另一個方向是建置資料類型的專屬資料庫系統,使用其中一個資料庫來串連資料流,以最佳化特定資料的處理。兩個方向並不是互斥的,可以在資料庫設計上相互搭配。

每個人都可以幫助資料庫

以上也只是蜻蜓點水般說明大致的問題分類而已,應該就能夠瞭解到這些需求也不是只靠資料庫系統就能滿足,更全面地看待這些問題是有其必要性的。

有人可能會認為全面性的瞭解似乎很困難,但反過來說,其實每一個面向的專業提升都能對資料庫的運用有所貢獻。例如在商業理解上更精確的話,或許你就不需要讓資料庫大海撈針了;程式寫得有效率的話,資料庫連線閒置的時間就更短,進而增加資源使用率。

作為軟體工程師不應該劃地自限,應該嘗試去瞭解資料需求的全貌。當你更理解資料,就可以讓資料庫表現得更好,這是只有你才能做得到的事。


符合人性、善用資料

JSON 在 PostgreSQL 中開始被支援,是市場導向,也是一種符合人性的發展。JSON 作為善用資料的方式之一,在傳統關連式資料庫的角度看來可能是有些衝突的。本文提供給還在觀望的朋友參考,直接的結論是,如果你想用就可以用,無論功能和效能都符合基本需求,和市場上流行的 Document Database 擁有同一個等級的表現。

本篇摘要整理發表於 ModernWeb’20 的演說內容。

以目前趨勢來看,JSON 已經是最受觀迎的資料格式之一了。資料庫系統可以直接支援專屬處理方式的話,當然是很方便的事。但這樣自由表現的資料結構,相對於嚴格的正規化傳統觀念,是否會有負面的影響?

我的觀念是,資料庫的理想,終究還是在「符合人性」與「善用資料」兩個面向的落實。現行的資料庫系統實作上找到了平衡點,而你得到了更多的資料處理的自由。這樣的發展當然是好的方向。

以 PostgreSQL 的功能面來看,版本 12 才支援 JSONPath,對於資料的查詢才算是比較完整。即使 9.2 就開始支援 JSON,但如果要享受更先進的功能的話,最好可以升級到 12 以上的版本。

由於 JSON Document 可能會擁有比一般欄位資料更多的內容,對於查詢語句的長度也許會成為一個問題。PostgreSQL 的系統程式和手冊說明都確定是以 1GB 為上限,一般來說應該是很夠用。但要注意的是,這指的是位元組的長度,在多國語言編碼或相關程式處理需求的情況下,實際的最大長度可能不如你想得這麼多。雖然並不常見,但如果你的查詢語句長達數百 MB 時,就需要注意這個問題。

JSON 和 JSONB 的差異是很多人關心的,簡單來說,JSONB 就是 PostgreSQL 已經預先處理好的結果,所以可以直接其他像是索引等功能的操作。但也因為是處理過的結果,和原始內容不是百分之百相等的字串,如果你的 JSON 內容表現了超過標準以外的含義時,就應該還是保留原始的資料。

詳細內容請參閱使用手冊:JSON Type

在談到效能的部份,要注意的並不是 PostgreSQL 有多好,而是 PostgreSQL 確實可以在 JSON 處理的方案中,作為一個選項的存在。以單機資料庫而言是很不錯,但在水平擴充的需求上就不會是優勢。

我認為 PostgreSQL 在 JSON 的良好支援後,和其他異質的資料庫之間,有了更多共通的處理方式,在資料處理生態系統中,能夠更有效地提供資料交換的機會。

但在磁碟空間的運用上,PostgreSQL 仍有很大的進步空間,建議搭配支援即時壓縮的檔案系統會更好。

由於在收集資料時,發現許多網友期待能夠取代其他資料庫系統。我倒不這麼認為,也覺得不會發生。每一個資料庫系統深究起來都有其獨特之處,而值得慶幸的是,我們因為擁有了更多的選擇,可以選擇更符合人性,更能善用資料的方向前行。

Image for post
Image for post
想用就用 JSON in PostgreSQL


只有實戰才能累積能力的技術

本文提供一個簡單案例作為識讀 Query Plan 的入門,理解資料庫系統的行為,並且回饋給自己擬出改善策略。Query Plan 的識讀和改善,資料形態與開發文化的掌握是很關鍵的要素,但都是只有實際的職場才能接觸到的情境,是難以取代的「真人智慧」。

有關為何會有查詢計畫,請參閱另一篇「略懂查詢處理的老規矩」。

Image for post
Image for post
  • 列出查詢計畫及實際執行的結果,找出效能瓶頸。
  • 所有資料庫使用者都必須學習的技術,自己下的查詢自己檢查。
  • 進階使用者的技術分野。
  • 只有實戰才能累積能力的技術。
  • 查詢計畫可能會因為資料分佈不同而改變。
  • 一定要在實際的資料上評估。
  • 不同資料庫的查詢邏輯不同,調教方式不能移植。

以下為入門案例,初步瞭解實際情況之後,再閱讀其他理論資訊,會更有感覺。

改善前
  1. 總執行時間為: 14329.521 (actual time)* 1 (loops) = 14329.521 ms 約 14 秒。
  2. 兩個步驟各佔約 7 秒: 7446.517 * 1 約 7.4 秒,458.859 * 15 約 6.9 秒。

依階層計算可發現瓶頸在 Bitmap Index Scan on pg060_idx1,489.915 * 15 約 7.3 秒。既然已使用索引查詢,就找出 pg060_idx1 的內容。從上一層 Bitmap Heap Scan on pg060 可知所屬的 Table:

Indexes:
"pg060_pk" PRIMARY KEY, btree (agnt_id, vrfy_wkym, sys_code)
"pg060_idx1" btree (div_no, vrfy_wkym)

其引用的 pg060_idx1 只需要參考第 2 欄位的鍵值,但依舊需要掃描完第 1 鍵值,浪費了很多時間。

Index Scan using pg059_pk on pg059 b (6.9 秒)

情境同上,不適當的複合欄位索引造成索引未能加速查詢。

改善方式

總之經過許多猜想之後,直接跳到結論是建立新的索引:

CREATE INDEX pg059_idx2 ON pg059 (vrfy_wkym);
CREATE INDEX pg060_idx2 ON pg060 (vrfy_wkym);

由查詢計畫可知,僅需要參考一個欄位即可,那就建立單欄位的索引;複合欄位索引,除非查詢條件真的非常符合,否則空間和時間效益都不大(如此案例就是如此)。

不是加了索引就會快唷!!!

你可以參考 PostgreSQL 使用手冊:多欄位索引

改善後

本案例未附上原始查詢語句,因為在分析過程中,不一定需要,關注太多反而是雜訊而沒有幫助。此例的原始語句是 View 的組合查詢,但其實我就沒有去拆解原語句,直接看最後資料庫的查詢計畫。

無論你怎麼輸入查詢語句,查詢計畫才是真的,那才資料庫實際的執行內容。

原始查詢語句用於重構資料邏輯。如果查詢計畫已無改善空間,才回到原始語句,以尋求資料邏輯的改善。(再往上就是應用邏輯或老闆邏輯了)

小練習:https://www.db-fiddle.com/f/awN5Kj5PGoBm1Q4Z5yqjUT/1
多欄位索引的練習,可以線上體驗跑跑看。

延伸問題

「你有觀察過自己的查詢(工作)計畫嗎?」

「面對不同的資料量(工作量),你的查詢計畫有不一樣嗎?」

此篇的口說內容:

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store