最近在學習MySQL優化方面的知識。本文就數據類型和schema方面的優化進行介紹。
數據庫
MySQL支持的數據類型有不少,而如何選擇出正確的數據類型,對於性能是相當重要的。如下幾個原則可以幫助肯定數據類型:緩存
應儘量使用能夠正確存儲數據的最小數據類型,夠用就好。這樣將佔用更少的磁盤、內存和緩存,而在處理時也會耗時更少。微信
當兩種數據類型都能勝任一個字段的存儲工做時,選擇簡單的那一方,每每是最好的選擇。例如整型和字符串,因爲整型的操做代價要小於字符,因此當在二者之間選擇時,選擇整型一般可以得到更好的性能。數據庫設計
當列可爲NULL時,對於MySQL來講,在索引和值比較等方面須要作更多的工做,雖然對性能的影響不是很大,但也應儘可能避免設計爲可爲NULL。ide
除了以上原則,在選擇數據類型時,需遵循的步驟:首先肯定合適的大類型,例如數據、字符串、時間等;而後再選擇具體的類型。下面將討論大類型下的一些具體類型,首先是數字,有兩種類型:整數和實數。工具
整數類型和所佔用的空間以下:
整數類型 空間大小(bit)
TINYINT 8
SMALLINT 16
MEDIUMINT 24
INT 32
BIGINT 64
整數類型所能存儲的範圍和空間大小有關:-2^(N-1)至2^(N-1)-1,其中N爲空間大小的位數。性能
整數類型具備UNSIGNED的可選屬性,當聲明時,表示不容許負數,則存儲範圍變爲:0至2^(N)-1,擴大了一倍。學習
在MySQL中,還能夠爲整數類型指定寬度,例如INT(1),但這樣的意義並不大,並不會限制值的合法範圍,仍能存儲-2^31至2^31-1的值,所影響的是與MySQL的交互工具顯示字符的個數。優化
實數類型的對好比下:
實數類型 空間大小(Byte) 取值範圍 計算精度
FLOAT 4 負數:-3.4E+38~-1.17E-38;非負數:0、1.17E-38~3.4E+38 近似計算
DOUBLE 8 負數:-1.79E+308~-2.22E-308;非負數:0、2.22E-308~1.79E+308 近似計算
DECIMAL 與精度有關 同DOUBLE 精確計算設計
從上面能夠看出,FLOAT和DOUBLE都有固定的空間大小,但同時因爲是使用標準的浮點運算,因此只能近似計算。而DECIMAL則能夠實現精確計算,與此同時佔用的空間會相較更大,所耗費的計算開銷也更多。
DECIMAL所佔空間大小與指定的精度有關,例如DECIMAL(M,D):
MySQL在存儲DECIMAL類型時會做爲二進制字符串存儲,每4個字節存9個數字,當不足9位時,數字的佔用空間以下:
數字個數 佔用空間(Byte)
一、2 1
三、4 2
五、6 3
七、8 4
小數點先後將分別存儲,同時小數點也要佔1個字節。下面舉兩個計算的例子:
DECIMAL(18, 9):整數部分長度爲9,佔用4個字節。小數部分長度爲9,佔用4個字節。同時加上小數點1個字節,則總共佔用9個字節。
能夠看出DECIMAL的空間佔用仍是很大的,所以只有當須要對小數進行精確計算時,才須要使用DECIMAL。除此以外,咱們還可使用BIGINT代替DECIMAL,例如須要保證小數點後5位的計算,能夠將值乘上10的5次方後做爲BIGINT存儲,這樣能同時避免浮點存儲計算不精確和DECIMAL精確計算代價高的問題。
最經常使用的字符串類型當屬VARCHAR和CHAR。VARCHAR做爲可變長字符串,會使用1或2個額外字節記錄字符串的長度,當最大長度未超過255時,只需1個字節記錄長度,超過255,則需2個字節。VARCHAR的適用場景:
CHAR則爲定長字符串,根據定義的字符串長度分配足夠的空間,適用場景:
除了VARCHAR和CHAR,針對存儲大字符串,可使用BLOB和TEXT類型。BLOB和TEXT的區別在於,BLOB是以二進制方式存儲,而TEXT是以字符方式存儲。這也致使,BLOB類型的數據沒有字符集的概念,沒法按字符排序,而TEXT類型則有字符集的概念,能夠按字符排序。二者的使用場景,也由存儲格式決定了,當存儲二進制數據時,例如圖片,應使用BLOB,而存儲文本時,例如文章,則應使用TEXT類型。
MySQL中所能存儲的最小時間粒度爲秒,經常使用的日期類型有DATETIME和TIMESTAMP。
類型 存儲內容 空間大小(Byte) 時區概念
DATETIME 格式爲YYYYMMDDHHMMSS的整數 8 無
TIMESTAMP 從1970年1月1日零點以來的秒數 4 有
TIMESTAMP顯示的值將依賴於時區,意味在不一樣時區查詢到的值將不同。除了以上列出的不一樣,TIMESTAMP還具備一個特殊屬性,在插入和更新時,若是沒有指定第一個TIMESTAMP列的值,將會設置這個列的值爲當前時間。
咱們在開發過程當中,應儘可能使用TIMESTAMP,主要是由於其空間大小僅需DATETIME的一半,空間效率更高。
若是咱們想存儲的日期和時間精確到秒以後,怎麼辦?因爲MySQL並未提供,因此咱們可使用BIGINT存儲微妙級別的時間戳,或者使用DOUBLE存儲秒以後的小數部分。
一般來講整數是標識符的最好選擇,主要是由於其簡單,計算快,且可以使用AUTO_INCREMENT。
簡單來講,範式就是一張數據表的表結構所符合的某種設計標準的級別。第一範式,屬性不可分割,如今的RDBMS系統建成的表都是符合第一範式的。而第二範式,則是消除非主屬性對碼(能夠理解爲主鍵)的部分依賴。第三範式消除非主屬性對碼的傳遞依賴。具體的介紹,能夠讀讀知乎上的這個回答(https://www.zhihu.com/question/24696366/answer/29189700)
嚴格範式化的數據庫中,每一個事實數據會出現且只出現一次,不會出現數據冗餘,這樣所能帶能帶來的好處有:
但也因爲數據分散存在各張表中,查詢時須要對錶進行關聯。而反範式的優勢則是不用進行關聯,將數據冗餘存儲。
在實際應用中,不會出現徹底的範式化或徹底的反範式化,時常須要混用範式和反範式,使用部分範式化的schema,每每是最好的選擇。關於數據庫設計,在網上看到這樣一段話,你們能夠感覺下。
數據庫設計應該分爲三個境界: 第一境界:剛入門數據庫設計,範式的重要性還未深入理解。這時候出現的反範式設計,通常會出問題。 第二境界:隨着遇到問題解決問題,漸漸瞭解到範式的真正好處,從而能快速設計出低冗餘、高效率的數據庫。 第三境界:再通過N年的鍛鍊,是必定會發覺範式的侷限性的。此時再去打破範式,設計更合理的反範式部分。 範式就像武俠裏面的招數,初學者妄想不按招數來,只能死的很難堪。畢竟招數都是高手總結概括的精華。而隨着武功提升,招數熟練以後,必然是發現招數的侷限性,要麼忘掉招數,要麼自創招數。 只要努力,加上多熬幾年,總能達到第二個境界,總會以爲範式是經典。此時能不過度依賴範式,快速突破範式侷限性的人,天然是高手。
除了上述說到的反範式,在表中存儲冗餘數據,咱們還能夠建立一張徹底獨立的彙總表或緩存表,來知足檢索的須要。
緩存表,指的是存儲能夠從schema其餘表中獲取數據的表,也就是邏輯上冗餘的數據。而彙總表,則指的是存儲使用GROUP BY等語句聚合數據,計算出的不冗餘的數據。
緩存表,可用於優化搜索和檢索查詢語句,這裏可使用的技巧有對緩存表使用不一樣的存儲引擎,例如主表使用InnoDB,而緩存表則可以使用MyISAM,得到更小的索引佔用空間。甚至能夠將緩存表放到專門的搜索系統中,例如Lucene。
彙總表,則是爲了不實時計算統計值所帶來的高昂代價,代價來自兩方面,一是須要掃描表中的大部分數據,二是創建特定的索引,會對UPDATE操做有影響。例如,查詢微信過去24小時的朋友圈數量,則可固定每1小時掃描全表,統計後寫一條記錄到彙總表,當查詢時,只需查詢彙總表上最新的24條記錄,而沒必要每次查詢時都去掃描全表進行統計。
在使用緩存表和彙總表時,必須決定是實時維護數據仍是按期重建,這取決於咱們的需求。按期重建相比實時維護,能節省更多的資源,表的碎片更少。而在重建時,咱們仍需保證數據在操做時可用,須要經過「影子表」來實現。在真實表後建立一張影子表,當填充好數據後,經過原子的重命名操做來切換影子表和原表。
當MySQL在執行ALTER TABLE操做時,每每是新建一張表,而後把數據從舊錶查出並插入到新表中,再刪除舊錶,若是表很大,這樣須要花費很長時間,且會致使MySQL的服務中斷。爲了不服務中斷,一般可使用兩種技巧:
在一臺不提供服務的機器上執行ALTER TABLE操做,而後再與提供服務的主庫進行切換;
但也不是全部的ALTER TABLE操做會引發表重建,例如在修改字段的默認值時,使用MODIFY COLUMN會進行表重建,而使用ALTER COLUMN則不會進行表重建,操做速度很快。這是由於ALTER COLUMN在修改默認值時,會直接修改了存在表的.frm文件(存儲字段的默認值),而並未重建表。