更大的硬體不見得更快,但通常能承受更多負載。

更大的硬體比較像更寬的高速公路,而不是更快的車:車道多了,你並不能(也不被允許)開得更快。這就是為什麼加硬體不會自動改善慢的 SQL 查詢。

1990 年代已經結束了#

當年單核 CPU 的運算能力快速攀升,多數回應時間問題換台新硬體就消失了——純粹靠 CPU 變強,就像新車款每年都比舊款快一倍。但單核 CPU 的效能在 21 世紀最初幾年撞上了牆,這條軸線幾乎不再進步。

廠商為了繼續做出更強的 CPU,只好轉向多核策略。多核允許多個任務並行,但只有一個任務時並不會更快——效能不只有一個維度。

水平擴展(加更多伺服器)也有類似的限制:更多伺服器能處理更多請求,卻不會改善單一查詢的回應時間。要讓搜尋更快,你需要一棵有效率的搜尋樹——即使在 CouchDB、MongoDB 這類非關聯式系統中也一樣。

草率索引的代價,硬體補不回來#

適當的索引,目標是完整發揮 B-tree 索引的對數擴展性。可惜索引往往做得非常草率,「資料量的影響」一節的圖表已讓這個效果無所遁形。

圖 3.5:依資料量的回應時間——草率索引與適當索引的落差

草率索引與適當索引之間的回應時間差距令人瞠目,幾乎不可能靠加硬體來補償。就算你真的靠硬體把回應時間壓下來,那是否為此問題的最佳解,仍然大有疑問。

許多所謂的 NoSQL 系統仍宣稱能以水平擴展解決所有效能問題。但這種擴展性大多僅限於寫入操作,且是靠所謂的最終一致性(eventual consistency)模型達成的。SQL 資料庫採用嚴格一致性模型,寫入因而較慢,但這未必意味著吞吐量差

更多硬體反而可能更慢#

更多硬體通常不會改善回應時間,事實上還可能讓系統更慢——因為額外的複雜度會累積更多延遲:

  • 應用程式與資料庫跑在同一台電腦時,網路延遲不成問題;但正式環境通常把它們裝在各自的專用硬體上。
  • 安全政策甚至可能要求在應用伺服器與資料庫之間架設防火牆——往往讓網路延遲加倍

架構越複雜,累積的延遲越多,回應也越慢。這往往導致一個反直覺的觀察:昂貴的正式硬體,竟然比開發用的廉價桌機還慢。

另一個關鍵延遲:磁碟尋道時間#

旋轉式硬碟(HDD)需要相當長的時間移動機械部件才能讀到目標資料——通常是幾毫秒。走訪一棵四層的 B-tree 會遇上四次這種延遲,合計數十毫秒。

對電腦而言這是半個永恆,但只發生一次時仍遠低於人的感知門檻。問題是:單一段 SQL 敘述就很容易觸發數百甚至數千次磁碟尋道——尤其是用 join 結合多張表時。

即使快取大幅緩解了問題、SSD 等新技術把尋道時間降低了一個數量級,join 依然普遍背著「慢」的嫌疑。因此下一章將說明如何運用索引做出高效的資料表 join。

延伸:最終一致性與 CAP 定理

在分散式系統中維持嚴格一致性,需要所有節點之間同步協調每一次寫入。這個原則有兩個不愉快的副作用:

  1. 增加延遲、拉長回應時間。
  2. 降低整體可用性——完成一次寫入需要多個成員同時在線。

分散式 SQL 資料庫常被誤認為「共用儲存」或「主從複製」的電腦叢集。實際上分散式資料庫更像是一個與 ERP 系統整合的網路商店——往往是不同廠商的兩套產品。兩者之間的一致性仍是值得追求的目標,通常以兩階段提交(2PC, two-phase commit)協定達成:它建立橫跨多個資料庫的全域交易,提供人所熟知的「全有或全無」行為。但只有在所有參與成員都可用時,全域交易才能完成,因而降低整體可用性。

節點越多,嚴格一致性就越麻煩;超過少數幾個節點後,幾乎不可能維持。反之,放棄嚴格一致性就解決了可用性問題、消除了增加的回應時間:基本想法是在一部分節點上完成寫入後,再重新建立全域一致性。

這個做法留下一個未解問題:若兩個節點接受了相互矛盾的變更,衝突無法被預防。最終一致性是靠「處理衝突」而非「預防衝突」達成的。在這個脈絡下,一致性只意味著所有節點擁有相同的資料——未必是正確或最好的資料

Brewer 的 CAP 定理描述了一致性(Consistency)、可用性(Availability)與分區容忍性(Partition tolerance)之間的一般依賴關係。

延伸:固態硬碟(SSD)與快取

固態硬碟(SSD, Solid State Disk)是不含移動部件的大容量儲存技術,典型尋道時間比 HDD 快一個數量級。SSD 約在 2010 年前後進入企業儲存市場,但受限於高昂成本與有限壽命,當時尚未普遍用於資料庫。

不過資料庫本身會把頻繁存取的資料快取在主記憶體中。這對「每次索引存取都需要的資料」特別有用——例如索引的根節點。資料庫甚至可能把常用索引整個快取住,讓一次索引查找不觸發任何一次磁碟尋道