自從關聯性資料庫(Relational Database)問世以來,對程式人來說,絕對是一大革命。因為關聯性資料庫提供的是一種通用性質的資料存取服務,抽象化資料的新增、修改、刪除以及查詢作業。

現今廣為程式人所採用的SQL語法,形同描述、處理資料的共通語言。而針對關聯式資料庫的種種研究,更持續地強化效能及穩定度。時至今日,透過關聯式資料庫去管理應用系統的資料,早已是應用系統中不可或缺的一環。

高階的資料處理觀點,過度關注正確性,卻可能忽略效能成本
因為高度抽象化對資料庫的存取,也漸漸地將程式人導向至以高階的抽象角度,看待資料處理這個工作。這使得程式人關注的焦點放在處理資料的結果是否正確,而忽略每個對資料庫存取的動作,所產生的效能代價是否可以接受。

因為程式人僅站在高階的資料處理觀點看待系統,使得他們在資料處理的效能上,付出的關心太少。在單元測試或小規模的測試時,效能的問題也許不會彰顯,而程式人也能順利交差。一旦系統正式上線,面臨更大的運作壓力時,這些付出龐大效能成本的處理動作,就會成為系統的效能地雷。

此外,許多的程式人,都不會額外關心在自己負責範圍以外的系統,特別是程式人普遍當做是一個大黑箱的資料庫,程式人們(包括我在內)更是不太關心如何調整,使它發揮更好的效能。

而這正是從程式人角度出發看待效能的一大盲點。程式人們倘若只努力調整應用系統的效能,但沒有連帶調整應用到的其他系統,例如作業系統、Web伺服器、以及資料庫伺服器等的效能組態,使這些參數設定能與應用系統相匹配,效能瓶頸就很有可能發生在這些環節,因而拖慢了整個系統。

資料庫參數調整是程式人不可輕忽的面向
說到了資料庫效能,程式人們通常不會忘了選用一個好的資料庫連線驅動程式。例如,對Java的應用程式而言,皆以JDBC連接資料庫。然而,JDBC驅動程式又區分為四種類型,由Type 1至Type 4,各類型的JDBC驅動程式,效能表現又不盡相同。針對資料庫選用一個高效的資料庫連線驅動程式,是打造優越效能的開始。

程式人們通常也不會忽略Connection Pool的設置。資料庫Connection Pool的作用,就和其他類型的Resource Pool相仿。之所以需要利用Connection Pool連接資料庫,主要是因為資料庫能承載(或允許承載)的同時連線數有限,而且重複建立全新的資料庫連線極為耗時,因此,透過Connection Pool管理對資料庫的連線,將大大有助於提升存取的效能。

即使對此一無所悉,仍然可以享受到Connection Pool所帶來的好處。程式人就算不懂得如何自行實作Connection Pool或利用協力廠商所開發的Connection Pool,也會因為大多數的程式人皆以既成的資料庫存取平臺(Framework)為基礎開發應用程式,而這些平臺中皆包裝了Connection Pool的機制。

只要套用現成的Connection Pool或資料存取平臺,不意謂著就沒有其他的工作。就如同HTTP Server的Server Pool設定一樣,資料庫的Connection Pool仍然有相關的管理參數必須妥善設置。

舉凡,初始時的連線數、最大的連線數、最少的閒置連線之類的參數,都必須考慮到:應用系統本身在不同的時段(尖峰、離峰)同時會發生的資料庫連線數、以及資料庫伺服器本身所具備的計算力等情況,然後加以設定。倘若開發團隊中,沒有專職負責資料庫效能參數調整的人員,程式人需要自立自強。

撰寫每條SQL述句時,必須估量未來資料量可能的規模
在設計階段所規畫設計的資料庫表格,也會深深影響資料存取的效能。例如設計時廣泛採用的正規化技巧,倘若使用過度或不正確,便會造成存取資料時的效能低落(例如得跨資料庫表格的join動作,才能取得所需的資料)。此外,未能建立適當的索引,同樣也會影響到存取的效能。

事實上,神仙難救無命人,當程式人寫下的SQL述句,本質就注定低效時,其他的部分再怎麼調整作用都不大。

之所以會寫下本質低效的SQL述句,便導因於「僅站在高階的資料處理觀點,看待處理資料的動作」。因為程式人在低階設計階段或程式開發階段撰寫SQL述句時,多半未考量每個SQL述句中所運用到的操作,可能帶來的效能成本。

程式人在撰寫每條SQL述句時,都必須依據這條SQL述句牽扯的資料規模有所假設。例如,存取使用者帳戶資料時,就必須估計可能會有的使用者數量;處理記錄部落格文章資訊的表格時,自然必須對未來系統可能會面臨的部落格文章究竟會有多少篇,要有一個大概的估計。

在這個基礎上,再去評量你的動作是否可能引發效能的問題。例如,對於一個很大的表格做Select Max(field)的動作,是不是會很慢?對兩個很大的表格執行Join、再做Group By,最後再做Order By,會不會遭遇到巨量的資料規模?利用Sub-Query搭配In語法,是不是可能成為系統中的效能陷阱呢?

程式人有責任了解自己所寫下的每條SQL述句中的操作,在不同的資料規模下所需付出的效能成本,例如前面提過的Join、Order By、Group By……。程式人必須意識到那些可能會付出昂貴成本的SQL述句,並且試著加以調整。

大多數情況下,同樣的目的,換一種寫法,就會有不同的效能表現。但有時並不容易調整,就必須回過頭尋求修改商業邏輯的方法。許多商業邏輯並非完全不可撼動,通常稍微犧牲完美性,就能換取更好的效能表現。

此外,表格鎖定或交易動作時,都有可能造成資料獨占的現象,當鎖定頻繁地發生時,自然容易大幅影響效能。

縮小結果集,有助降低頻寬與伺服器負擔
另一個從程式人觀點出發容易忽略的因素,就是應用程式與資料庫之間傳輸的資料量。「Select * from table_xxx」這樣的述句寫起來方便,但對於那種單一資料列含有相當長資料的表格來說,這種Select述句就會引發資料庫伺服器從資料庫中取出大型的結果集(Resultset),因而提高傳輸資料所需的頻寬。

但事實上,大多數的資料存取需求,都不會需要取出表格中的所有欄位,真正所需的,不過僅僅只是部分欄位罷了,因此重新檢視確實需要的資料欄位,縮小存取的結果集空間大小,有助於減少所需的傳輸頻寬,以及資料庫伺服器的負擔。

最後,適度地使用資料庫快取絕對是有很大的幫助。就和所有類型的快取一樣,針對變化不頻繁、即時性不那麼關鍵的資料,採用資料庫的快取,可以讓應用程式所需的資料,毋需送至資料庫處理即可獲得。這中間帶來的好處,包括了減少網路傳輸的負擔、減少資料庫伺服器的處理器運算、減少資料庫伺服器的磁碟存取,等於少去了一項相當耗時的操作。

是的,資料庫存取時常是許多應用系統運行時,最有可能發生瓶頸的所在。只要調整好資料庫的存取效能,系統通常不致於有太糟糕的表現。

作者簡介:
王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。

相關閱讀:
高效的系統開發要領(1)制定量化效能目標,作為調整基準
高效的系統開發要領(2)找出最關鍵的效能瓶頸
高效的系統開發要領(3)當瓶頸在運算量時,先從程式下手
高效的系統開發要領(4)當瓶頸在檔案存取時,善用快取
高效的系統開發要領(5)努力瘦身,輕鬆通過對外頻寬的瓶頸
高效的系統開發要領(6)伺服器運用頁面快取,加速效果驚人

熱門新聞

Advertisement