1、 前言
對於數據庫而言,在日常開發中我們主要的關注點有兩塊,一個是schema的結構設計,另一個就是索引的優化,這兩塊是影響我們最終系統結構和性能的關鍵部分,自然也是我們花費精力最多的部分;
本文主要介紹數據庫設計中的一般原則和優化手段,包括數據庫的一半范式、反范式設計、數據切分、數據路由與合並等等
2、 Schema設計的一般性原則
2.1 概述
范式理論是關系型數據庫設計的黃金法則,它提供了數據結構化的理論基礎,有效地保證了數據的一致性,應該說,關系型數據庫就是在范式的基礎上才成長起來的。
數據庫的范式有很多種,但是我們一般常用的只有第一、二、三范式和BC范式,這些范式直接在我們的數據庫schema設計中得到體現,雖然有時我們根本就沒有意識到。
2.2 第一范式和第二范式
在關系型數據結構的實體-關系模型中,是允許實體集和關系集的屬性具有某種程度的子結構的,比如多值屬性和組合屬性;而第一范式則限制了這種存在,他要求所有的字段都是不可再分的、原子的,否則就違反了第一范式;第一范式主要是為了避免數據表結構過於復雜多樣,使得上層操作的可抽象性和數據一致性遭到破壞。
第二范式簡單的說,則是要求數據庫中的每條記錄都要有其對應的主鍵id存在,這樣要求的主要目的是為了能夠滿足上層業務要求唯一標識每條記錄的需求;其實數據庫管理系統本身也有這種需求,部分數據庫的索引結構就是基於此的,只不過這不是數據范式(data normal form)應該關心的東西;
2.3 第三范式
第三范式要求在在一個實體集中,不能存在一個非主屬性可以作為該實體集中某個子集的候選主鍵,還可以表述為,不同的關系集中不能存在除了主鍵字段外的其他相同字段;這兩者是等價的。
簡單的說,第三范式主要是要求將實體集盡量拆分,將不同的業務單元屬性字段拆分到不同的表里,然后通過關系表進行關聯。
第三范式一定程度上減少了數據的冗余,降低了數據不一致的風險;通常情況下,大部分的schema都應達到第三范式的要求。
2.4 BC范式
BC范式是在第三范式的一個子集,它在第三范式之上做了更強的約束,即實體集中的任何子集都只能依賴於主鍵(注意,不是主屬性,這一點是BC范式和第三方范式的差別所在),不能存在一個非主屬性或非主屬性集可以作為某個子集的主鍵。
BC范式在定義上和第三方是差不多,他最大程度的減少了數據冗余,不過在實際應用中,二者基本是一樣的,只有在表的主鍵包含多個字段時,才會產生差異。
3、 反范式化設計
3.1 數據冗余
這里介紹一個很經典的例子。我們所常見的大部分網站都會有會員系統,用戶需要注冊會員賬號,然后登錄才可以享受進一步的功能或服務。對於這張會員表,我們不妨命名為user_info表,一般會包括會員id,會員昵稱,真實姓名,登錄密碼,性別,教育經歷、工作經歷、電話、電子郵箱、個人簡介、興趣愛好等信息,其他可能還包括跟該網站會員業務相關的一些字段(比如積分、經驗值、充值賬戶余額等),一般的schema設計是這樣的:
表名 |
user_info |
數據量 |
100w |
|
功能描述 |
主要存儲用戶的基本信息,如id、姓名、年齡、聯系方式等; |
|||
字段名(新增) |
數據類型(精度范圍) |
空/非空 |
缺省值 |
字段含義 |
id |
BIGINT(20) |
N |
用戶id |
|
name |
VARCHAR(32) |
N |
姓名 |
|
gender |
TINYINT(4) |
N |
性別 |
|
age |
INT(8) |
N |
年齡 |
|
tel |
VARCHAR(16) |
Y |
聯系電話 |
|
|
VARCHAR(64) |
Y |
電子郵箱 |
|
school |
VARCHAR(32) |
Y |
就讀學校 |
|
company |
VARCHAR(32) |
Y |
供職機構 |
|
interest |
VARCHAR(512) |
Y |
興趣愛好 |
|
gmt_create |
DATETIME |
N |
記錄創建時間 |
|
gmt_modified |
DATETIME |
N |
記錄修改時間 |
這張表結構看起來是沒什么問題的,而且在初期業務簡單,用戶數量少、訪問量低的情況下也確實都ok的;
但是隨着網站訪問量不斷增大,每天登錄的用戶越來越多,user_info表的訪問量越來越大,瓶頸慢慢出現;
我們經過進一步分析發現,在處理用戶登錄的過程中,我們需要的操作很簡單,就是根據用戶輸入的會員昵稱查詢相應記錄,進而判斷密碼是否正確;而會員登錄后,也僅會顯示用戶的昵稱或真實姓名。
整個過程前后所涉及到的字段,只有會員id、昵稱、密碼和真實姓名等3、4個,而user_info表中其他絕大部分字段,諸如教育經歷、興趣愛好等,在這個過程中是根本不需要的,但他們的體積卻比較大,每次用戶登錄都要讀取,白白浪費時間和帶寬。
那么我們為什么不把這部分字段單獨拿出來放到一張新的表里呢?我們不妨建一個login表,里邊只有會員id、會員昵稱、登錄密碼以及會員真實姓名,每次用戶登錄,都只讀取這張login表,這樣不但數據庫讀取記錄的時間會縮短,也可以將應用和數據庫之間網絡傳輸量降到最低,完全符合我們前一篇文章里所提到的將結果集縮減到最小的原則。
表名 |
login |
數據量 |
100w |
|
功能描述 |
主要存儲用戶的登錄信息; |
|||
字段名(新增) |
數據類型(精度范圍) |
空/非空 |
缺省值 |
字段含義 |
id |
BIGINT(20) |
N |
用戶id |
|
name |
VARCHAR(32) |
N |
姓名 |
|
password |
VARCHAR(128) |
N |
登錄密碼的md5值 |
但是有人不禁要問,這樣不是產生數據冗余了嗎?是的,同一份數據在user_info表和login表中各存了一份,確實有可能存在不一致的情況,應用程序中每次新增或修改記錄,都可能需要訪問兩張表,這無形中增加寫操作的成本;所以這種優化方式也不是絕對的,要根據具體的場景區別對待,比如在上述這種讀寫比例非常高的場景中,我們這樣處理就是合適的;而在一個同樣讀寫比例比較高,但是對數據一致性要求也非常高的系統中,對數據進行冗余存儲就需要花費額外的精力去處理可能存在的數據不一致情況。
3.2 去關聯化
Join是我們在數據庫操作時經常會使用的一個關鍵字,其作用就是將兩張表拼接起來,然后過濾出符合條件的記錄;但是在拼接的過程當中,是采用的是笛卡爾積的方式,其原理圖如下:
在MySQL中,Join的實現方式為Nested Loop Join ,主要以驅動表結果集作為基礎數據進行循環,有點類似編程語言中的雙層for循環嵌套;這種方式實現最最簡單,性能也基本可以接受;其他數據庫還提供Hash Join、Sort Merge Join等Join方式,都是針對不同場景有性能提升,很難簡單的說誰好誰壞。
從笛卡爾積的基本原理上來看,不管采用何種實現算法,表間join都是非常耗時耗力的操作,尤其是當其中一張表或兩張表的數據量都非常大時更是如此!
所以我們在做schema設計的時候,應該盡量避免join的出現,通過一定的字段合並和數據冗余將這種需求降到最低。
3.3 去一致性約束
在傳統應用中,數據一致性是很重要的一點,但是在很多互聯網應用對數據的一致性要求並不是很高;比如說數據庫的外鍵約束、唯一性約束等等,當記錄增刪時,數據庫都會去檢查相關的條件是否滿足,這些都是需要額外開銷的,有時候其開銷甚至會超過當前操作本身。
在數據庫層去掉這些約束並不意味着系統中就不需要,我們其實可以將這部分約束校驗提前到應用程序中;前面我們提到過,應用程序的擴展相對來說是比較容易的,所以我們何嘗不充分利用一下來為數據庫減負呢?
3.4 去SQL化
3.4.1 數據庫底層數據存儲
我們使用的關系型數據庫雖然提供了表、字段、索引、sql等種種特性,但是歸根到底,其最底層的數據存儲仍然是<k,v>格式,比如MySQL,其數據的存儲都是有由下層存儲引擎來負責的,雖然在邏輯結構上我們看到的是一張張表和其中一個個分散的字段,但是實際上每條記錄在物理存儲上都是一個整體;所謂的表結構、字段這些只是上層來維護的元數據,對底層來說,其實沒有字段的概念,就是一條條的物理記錄;
比如說我們前邊的user_info表,我們如果執行類似下邊的一條sql:
select user_id,user_name from user_info where age =8;
雖然我只想返回user_id和user_name兩個字段,但是存儲引擎還是要將所有滿足的age=8的記錄取出來,然后在分別掃描每條記錄,取出我們需要的兩個字段數據返回;
所以我們可以看到,所謂的關系數據庫只不過是在<k,v>系統的上層做了進一步的封裝,比如說權限驗證、sql解析、二級索引等等;
3.4.2 MySQL數據庫的層次結構
MySQL雖然是個開源數據庫,體積也遠小於DB2、Oracle這些商業數據庫,但是一個RDBMS必備的結構MySQL卻一樣也不少,正所謂“麻雀雖小,五臟俱全”。
下邊我們就來看下MySQL到底有怎樣的層次結構;
MySQL從上到下,可以分為三層,第一部分為客戶端,中間部分為數據庫管理系統(DBMS),最底層為存儲引擎層;MySQL使用獨立的存儲引擎,可以方便切換,這一點和其他數據庫有所不同;
3.4.3 哪些是可以不要的
前面給出了MySQL數據庫的層次結構,其他數據庫結構也是類似的;那么不妨從上到下好好想一想,整個體系中哪些東西其實不是我們必需的?
首先,Client端肯定是需要的,我們總要連接數據庫的,那么來看第二層DBMS層,其中的連接管理是需要的,不管什么樣的數據庫總要處理客戶端連接;如果一個數據庫在安全網絡環境中,並且只有我們自己在用的話,那么就不需要用戶權限驗證了,這一層可以去掉;為了減輕數據庫負擔,我們也不使用sql,需要的邏輯可以直接使用API在應用層實現,那么這一層也可以省掉;而對於訪問控制模塊,因為數據庫為我們自己所獨享,或者都是可信任的使用者,所以這一層也不那么重要了,或者說是可以進一步簡化的;
我們再來往下看存儲引擎層。索引的話可以很好的提高數據的查詢效率,這一點不管在什么類型的存儲系統中都適用,那么這個功能我們需要保留,但是如果我們只想要個<k,v>存儲的話,那就可以只保留主鍵索引好了;事務管理呢,最多我們不用好了,不麻煩數據庫了;鎖的話即使我們自己使用也會有很多的並發訪問控制,那么需要保留;
經過這樣的精簡之后,我們的數據庫還剩以下這些東西;
3.4.4 NoSQL存儲系統
前面我們提到了去關聯化、去一致性約束以及數據冗余等等;3.1中我們討論了關系型數據庫的本質,其實就是在<k,v>存儲之上又封裝和增添了諸多的功能,而3.3中我們又對關系型數據庫進行了裁剪,去掉了一些我們不必需的;經過所有的這么步驟,剩下的部分(圖1-1所示),基本上就是現在NoSQL存儲了;
所以,NoSQL不是什么高級的東東,而是關系型數據庫做了退化,回歸到了其基礎本源;而分布式的特性,也並不是NoSQL獨有的,關系型數據之所以難有分布式的架構,本質就是因為其選擇了“向上生長”,上層的復雜特性制約了其“橫向”生長的能力;而NoSQL只不過是在<k,v>之上,選擇了另外一個生長方向而已。
由於去掉了上層的“高級”特性,NoSQL系統的性能有了比較大的提升,同時由於橫向生長,其存儲能力也有了很大的增強;
所以當我們的系統受制於關系型數據庫的性能時,不妨放棄schema模式,來嘗試一下“自由”的NoSQL數據庫。
4、 數據擴展
4.1 Scale Up和Scale Out
Scale up主要是指增強數據庫的單機處理能力,比如說提高CPU、內存、硬盤、網卡等硬件配置;其實scaleup這個概念包括我們后便將要提到的scale out,不光對於數據庫,對於大部分的軟件系統都適用的,只不過具體的實施方案會有所差異;
因為scale up主要是增加機器硬件配置,相對來說比較簡單,也不需要遷移數據,對於技術人員來說沒有新的東西,所以對於中小型系統來說非常合適;
scale out是指橫向的擴展,就是增加數據庫實例或節點來增加整體的處理能力,這里邊還包括兩種方式,一種常見的數據復制,比如MySQL的Replication,Oracle rac等;另一種橫向擴展的方式是進行數據切分,也就是說把本應該放在一台機器上的數據切成幾部分,分別放在不同的節點上(並不是相同數據的備份),這樣訪問的壓力就會分散到多個節點。
scale out的優點是成本低,如果整個系統都使用PC Server的話,可以用很低的成本來支撐海量的數據和高並發;而且一般來說,這種擴展是線性的,即有多少機器,就能支撐多少的數據和多大的訪問量,但通常這需要有個比較好的數據系統架構或中間件系統來支持;
以淘寶的交易庫來說,原來使用的都是IOE(I指的是IBM的小型機,O指Oracle數據庫,E即EMC的高端存儲),采用主備雙機方式,用Orcle和高端存儲來支撐每天的巨大訪問量,但是整個系統的成本也非常高(據說一套下來要2000多萬),而經過去IOE以后,通過使用MySQL和廉價的PC Server(線上16主16備,雙11的時候擴展為32主32備),通過數據切分和Replication機制,不僅整個數據庫的在線處理能力提高了4倍,成本也降為原來的1 / 8不到,同時數據的安全性和容災能力也得到了保證;
但是數據庫技術不是本文關注的重點,下邊將主要介紹和我們日常開發聯系更為緊密的數據切分知識。
4.2 數據切分
4.2.1 為什么要切分數據
對於這個問題,可能有人會覺得的是廢話:你前邊不是已經說過了嗎,干嘛還問?沒錯,擴展系統,支持更大訪問和並發和替換商業數據庫,降低成本這兩個確實我們進行數據切分的主要原因;但這樣來說太籠統了
對於數據庫來講,不管是商業的還是開源的,其單庫和單表的承載能力都是有限的;在通常的業務場景下(寫操作和讀操作比較均衡),普通的pc server上,MySQL數據庫單表數據記錄的承載能力在千萬級(數據量在TB級別)左右,TPS大概在千這個級別(具體測試環境和數據可參考另一篇文檔);當然,我們在這里沒有必要苛求具體的數據,因為這和具體業務場景、實際讀寫比、服務器硬件配置、具體的數據引擎、MySQL的配置參數等相關,比如說,如果只是將MySQL作為日志數據庫(基本只有寫操作,不需要建索引),單表的支撐能力可達到上億甚至是十億的級別;但這畢竟只是種極限場景,不能拿來用作一般場景的參考。
另外需要說明的是,前面說的單庫單表的承載能力有限,並不是說當數據量超過這個上限時,數據庫就會馬上崩潰或者拒絕服務,而是在這種情況下,數據庫的整體的讀寫性能就會急劇下降,甚至於一條很簡單的sql查詢也可能會超時,如果本來就是負載很重的一張表,那與崩潰無異了!
4.2.2 數據切分基本原則
數據切分所要遵循的原則主要有兩點:
第一就是將數據均衡分散在多個處理節點上,其實這里主要強調的是均衡,但這個均衡並不可簡單的看成是每個節點上庫(表)數量相等或是記錄條數一樣;而更多是要從數據訪問和處理能力的均衡上去考慮,主要原則有以下幾條。
a、 不同節點業務關聯度要低
b、 同一節點業務類型盡量一致
c、 數據(訪問量)要均衡
d、 數據的一致性和安全性
4.2.3 垂直切分
垂直切分主要是根據表中數據的業務類型,將不同業務的數據放在不同的表或者數據庫中;
在系統結構設計中我們會經常提到一個原則,叫做高內聚、低耦合,其實這個原則在數據庫的垂直切分中同樣適用;所以在做水平切分的時候要盡量做到不同功能的表關聯盡可能少,這不但可以減少SQL語句中的join出現的幾率,同時后續表結構變更時也更容易;
對於中小型系統來講,很多人喜歡各種復雜功能一個sql搞定,甚至有些還使用存儲過程,這樣對前端程序來講確實方便了許多;但是這種方便也往往會給我們帶來傷害,尤其是當數據量和訪問量都增長比較快的情況下,你會發現幾條慢查詢可能讓你的整個系統直接失去響應!
4.2.4 水平切分
4.2.4.1 什么是水平切分
垂直切分主要是按照系統功能來切分的,所以同樣是有瓶頸存在的,比如說,某一項功能比其他的要復雜許多,或者數據量要大很多,很難再進一步拆分,這樣就不適合垂直切分了;這在實際的業務場景中是存在的;
比如說淘寶的訂單系統,雖然功能看起來簡單,但是由於交易類型復雜,中間狀態繁多,耦合性非常強,加之訂單數量巨大,其系統是很難再進行功能上的剝離的;但是這個系統屬於底層基礎系統,為了支撐巨大的訪問量,只能采取水平數據切分方式來解決高並發問題。
水平切分就是我們通常所說的分庫分表,主要是將一張表中的數據按照某個字段(比如說用戶id、商品id、訂單id等)分散存儲在多張結構相同的表中,這樣訪問的壓力就會分散到多張表上;
在平時的開發中,數據的水平切分比垂直切分應用的更多,因為一般來講,需要進行垂直數據切分時,通常系統的規模和負載都已經很大了(尤其是使用oracle的時候),這時候我們最先實施的,往往是通過RPC或者服務化將應用分成多個系統,底層數據表之間的依賴,很自然的會轉化成系統間的接口依賴,所以這個時候,數據庫當然也會跟着分開了,不需要太刻意其考慮垂直切分這個概念了。
4.2.4.2 水平切分優點
成本固定;只要在系統設計之初就指定好分表數量和分表字段,不管是要分成8個庫1024張表,還是16個庫4096張表,其成本都是一樣的;
解決了單表瓶頸問題;水平切分方式很好的解決了垂直切分時可能存在的單表瓶頸問題,只要在開始時做好容量預估,設定適當的切分數量,基本可以滿足業務很長一段時期內的存儲需求;
對事務透明;分表對於數據庫來說是透明的,所以原來的事務該怎么做還怎么做。
4.2.4.3 水平切分缺點
水平切分的雖然很有效,但是其缺點也不少,主要如下:
sql路由變得復雜;每次在做了切分的庫或表上執行sql時,都必須要明確指出目標分庫或分表;這無形中增加業務方的成本。
分表字段單一;水平切分只能使用單一的分表字段;如果業務中有需求按照非分表字段進行查詢,則會變得很困難,只能掃描所有的表;一個解決方案就是,按照不同查詢字段做多份分表;但是又要花費精力去解決數據冗余問題。
join操作變得困難;顯而易見,以前單表間的join放到多表上是無法執行的,這時候我們最好還是選擇放棄;
二次擴展比較麻煩;如果分表之后,我們數據增長太快,又達到存儲瓶頸了,就面臨着二次拆分的命運;但因為路由規則發生了改變,遷移數據的麻煩是避免不了的;所以要有必要的手段去保證遷移數據時系統依然能夠對外提供服務。
4.2.4.4 水平切分注意事項
在做水平切分后,我們的部分業務實現方式或是開發方式可能需要隨着改變;以下是我們再做水平切分時需要注意的點,主要是針對水平切分的弱點而言的:
根據業務場景確定切分字段;業務中根據什么字段去查詢,就用什么字段去分表;
避免熱點數據問題;通常切分時采用的hash算法理論上可以保證數據的分散性,但在實際應用中,仍可能遇到數據熱點問題;理論是理論,實際歸實際,沒有絕對的,不要以為分了表就萬事大吉了。
分表宜多不宜少;這樣做主要是為了盡量避免后期可能遇到的二次拆分,因為前面我們說過,拆成1024張表和拆成4096張表的操作成本是一樣的。
避免分表上的join操作;在分表的缺點中我們就提到過,join在水平切分場景下會很困難,所以在業務實現中,對這種情況能避免就避免,哪怕犧牲一些簡潔性,多繞幾步。
避免非分表字段查詢;道理也是一樣的,切分后只能按照切分字段進行查詢;如果非要按其他字段查詢,那就冗余數據吧。
4.2.5 其他切分方式
上邊我們提到的是我們最常見的切分方式,其他還有一些切分方式不太“規矩”,它們具有部分水平切分或垂直切分的特點,但又很難直接歸入到某一分類中;正如那句老話所言:山無定勢,水無定形。
4.2.5.1 邏輯切分
邏輯切分類似於垂直切分,也是將數據按照不同業務邏輯拆分到不同的表中;但這種方式追求的不是單純的負載均衡,而是不同業務的數據隔離;比如說某部分數據讀多些少,某部分數據則可能讀少寫多;這樣進行隔離后,可以充分的利用不同的業務特點進行優化,比如說創建不同的索引結構,使用不同的查詢方案等等;前面我們提到的數據冗余其實就屬於這類切分方式。
另一個典型的案例就是數據傾斜;比如說對於淘寶上的賣家來說,有些賣家比較小,可能他的店鋪中的寶貝只有幾十數百個,但是有些品牌賣家或熱門店鋪,他們的寶貝可能有上萬甚至是數十萬,再加上未上架的或是已下架的歷史寶貝,數量會更多!這兩類用戶的處理,如果使用相同的邏輯,很可能會產生問題;但是如果我們將這部分大賣家提出來放在另外的節點上來處理,效果可能會好很多。
4.2.5.2 時間切分
時間的切分主要是將記錄按照創建時間的先后順序,放在不同的表中;mysql的分區表便提供了這樣一種切分的機制;分區表對外邏輯上表現為一張表,但是實際物理存儲上是多張表,不同的表對應不同的時間區段;用戶可以設定創建新分區表的周期,比如說一天或者是一周,在某個時間點插入的記錄便會寫入對應的分區表;讀取時,會從最近一個分區開始掃描,直到找到目標記錄。
4.2.5.3 冷熱切分
冷熱切分主要是按照數據的訪問頻率對數據進行隔離,有點類似於前邊我們提到的邏輯切分。
淘寶的交易歷史庫就是典型的代表。淘寶的訂單總量巨大,每天產生的訂單數量也非常多;如果我們為用戶提供訂單查詢服務,那么巨大的訂單量會使我們的服務性能下降很多;但是實際中我們會發現,用戶很少會去查詢自己三個月以前的訂單,那我們不妨將用戶三個月以前的訂單拿出來單獨提供存儲和查詢服務,這樣就可以使用戶訪問頻繁的訂單表數據量變得很小,從而可以提供更高的處理性能。
4.2.5.1 體積切分
按體積切分主要是按照數據表的尺寸或是記錄條數進行切分,這種切分一般適用於業務類型單一,對表的體積可以很好預估的情況,主要是為了避免表的尺寸過大而是性能下降。
一般來說,只有日志類型的表適用於這種切分方式,通常是以自增id作為判斷標識,因為基本不存在刪除和修改操作,所以可以很好的控制體積;不過這種情況下大部分系統更傾向於使用按時間切分的方式,所以按體積切分的實際應用很少。
4.3 數據路由與合並
當我們進行了分庫分表之后,一個我們不得不面對的問題就是sql的路由。當我們將數據分散存儲在諸如名為test_0000、test_0001的分表中時,我們會發現,必須要對原來的程序代碼或數據庫進行相應的改造,否則程序將找不到正確的庫或表;這種情況下,通常的解決方案有三種:
4.3.1 修改程序
這種方案,只需將程序里涉及到數據庫讀寫的代碼按照分表邏輯進行改造即可,技術上比較簡單,不需要額外的軟件或者是技術的支持;缺點是對業務代碼侵入性強,可能涉及到多個地方的修改,工作量較大,而且后期的修改和維護成本也比較高。
4.3.2 修改數據庫
這種方法對程序透明,是的上層業務邏輯不需要考慮分庫分表的讀寫規則,應用代碼可以保持不變;
缺點是需要修改數據庫系統,或者以模塊、插件等形式對數據庫進行增強,開發成本和后期維護成本都很高,部分商業數據庫根本無法自行修改。
4.3.3 使用中間層代理
這個應該是目前采用最多的方式;其優點是對上層業務邏輯和底層數據庫都透明,只需要對應用的訪問層進行簡單改造,即可快速切換到拆分之后的數據庫,不管是開發人員還是數據庫管理人員都不需要增加多少工作成本;
其缺點是技術門檻較高,需要專門的人員來開發和維護;功能受限,部分在單庫單表下的常見操作在這種中間層代理的方式下會變得麻煩,比如說跨庫、跨表join,全局數據分組與排序等。
對於以上幾種數據路由方案,不同的場景可能會有不同的選擇,具體要看自身的業務需求,技術儲備以及實施成本等。
4.4 Scale up之閃存存儲
以上通篇幾乎都在介紹如何對數據庫進行水平擴展和數據切分,其實有時候我們不必搞得如此復雜,如果僅通過簡單的硬件升級就能滿足業務增長對數據庫的要求,我們何樂而不為呢?
我們都知道,普通的服務器,甚至是很多高端存儲,都是基於機械硬盤存放數據的;而機械硬盤最大的缺點就是速度上存在物理上限;
一般的機械硬盤讀寫數據都要經過尋道操作,主要由兩個步驟組成:一是移動磁頭,二是轉動盤片;這兩者都屬於機械操作,前者一般在2ms~4ms,而后者,對於現在常用的SATA盤或SAS盤來說,一般會在1ms以內,所以,一次尋道操作要耗費大約5ms左右的時間,而對於一些老式硬盤來說,這個時間可能要達到10ms甚至更多!也就是說,單塊磁盤隨機讀寫的iops只有100左右,這簡直低的不可忍受,再牛B的系統也被拖死了!
對於一個初級團隊,再沒有很多技術積累的情況下,盲目的進行水平擴容往往會給自己帶來更高的維護成本和更差的系統穩定性,所以這個時候我們不妨嘗試一下數據庫性能提升的又一利器——閃存存儲;畢竟我們不斷對系統進行優化的目標就是為了獲得更高的性能!