良好的邏輯設計和物理設計是高性能系統的基石,好比反範式設計能夠加快某些類型的查詢同時也會影響另一些類型的查詢效率,因此咱們必須重視Mysql對於數據庫的設計(本文主要講述表字段類型對於數據庫性能的影響)。sql
因爲Mysql獨有的特性和實現細節對性能的影響是很明顯的,由於作好Mysql數據庫的設計很關鍵。對於數據庫設計,咱們不得不提表字段的類型選擇,因爲Mysql支持的數據類型很是多,所以如何選擇正確的數據類型對於得到高性能相當重要。無論要存儲的數據是什麼類型,咱們都須要根據一些數據庫設計原則來考慮。數據庫
更小的一般是更好的(通常狀況下,應該儘量使用正確存儲數據的最小數據類型。)緩存
爲何呢?服務器
(1) 由於更小的數據類型一般更快,由於它們佔用更少的磁盤、內存和CPU緩存,而且處理時須要的CPU週期也更短。 (2) 要確保沒有低估須要存儲的值的範圍,更小是相對與數據類型的最大值範圍來說的。 (3) 若是沒法肯定哪一個數據類型是最好的,就選擇你認爲不會超過範圍的最小類型。
簡單就好(簡單數據類型的操做一般須要更短的CPU週期。)數據庫設計
爲何呢?下面有幾個例子說明一下緣由。ide
(1) 整型比字符串操做代價更低,由於字符串集和校對規則(排序規則)是的字符比較比整型比較更復雜。 (2) 存儲日期和時間應該使用Mysql內建的類型(date,time,datatime)。 (3) IP地址的存儲應該用整型(int)。
儘可能避免 NULL (空值)函數
爲何呢?工具
(1) 不少表都包含可爲NULL的列,就算程序並不須要保存NULL也是如此,這是由於列的默認屬性就是可爲NULL。一般狀況下最好指定列NOT NULL,除非真的須要存儲NULL。 (2) 若是查詢中包含可爲NULL的列,對於Mysql來講是很難優化的,由於NULL的列使得索引,索引統計和值比較都更復雜。可爲NULL的列會使用更多的存儲空間,在Mysql裏也須要特殊處理。當可爲NULL的列被索引時,每一個索引記錄須要一個額外的字節,在MyISAM裏甚至還可能致使固定大小的索引變成可變大小的索引。 (3) 一般把可爲NULL的列改成NOTNULL帶來性能提高比較小,若是計劃在列上建索引的話,就應該儘可能避免設計成可爲NULL的列。(也有一個例外,那就是在InnoDB中,會使用單獨的位(bit)來存儲NULL值,因此對稀疏數據有很好的空間效率。)
總結性能
在爲列選擇數據類型時,第一步須要肯定合適的大類型(數字、字符串、時間等等),這一般是很簡單的,那麼下一步就是選擇具體的類型了。優化
不少Mysql的數據類型能夠存儲相同類型的數據,只是存儲的長度和範圍不同、容許的精度不一樣,或者須要的物理空間(磁盤和內存空間)不一樣。相同大類型的不一樣子類型數據有時候也有一些特殊的行爲和屬性。
好比:DATATIME 和 TIMESAMP列均可以存儲相同類型的數據(時間和日期)而且精確到秒,然而TIMESTAMP只使用DATATIME一半的存儲空間,而且會根據時區變化,具備特殊的自動更新能力。另外TIMESTAMP容許的時間範圍要小得多,有時候它的特殊能力會成爲障礙,這都是咱們開發者須要考慮的。
有兩個類型的數字:整數(whole number)和實數(real number)。
若是存儲整數,可使用這幾種整數類型:TINNYINT(8)、SMALLINT(16)、MEDIUMINT(24)、INT(32)、BIGINT(64)。
整數類型有可選的的UNSIGNED屬性,表示不容許爲負值,這大體能夠是正數的上限提升一倍。
好比:TINYINT UNSIGNED能夠存儲的範圍是0~255,而TINYINT的存儲範圍是-127~128.
有符號和無符號類型使用相同的存儲空間,並具備相同的功能.
所以能夠根據實際狀況選擇合適的類型。
你的選擇決定Mysql是怎麼在內存和磁盤中保存數據的。
整數通常選擇64位的BIGINT整數,即便在32位環境下也是如此。(可是一些聚合函數是例外,它們是使用DECIMAL或DOUBLE進行計算的)
Mysql能夠爲整數類型指定寬度。
好比:INT(11),對大多數應用這是沒有意義的:它不會限制值的合法範圍,只是規定了Mysql的一些交互工具(例如Mysql命令行客戶端)用來顯示字符的個數。對於存儲和計算來說,INT(1)和INT(20)是相同的。
一些第三方存儲引擎(好比Infobright)有時也有自定義的存儲格式和壓縮方案,並不必定使用常見的Mysql內置引擎的方式。
實數是帶有小數部分的數字。
它們不僅是將來存儲小數部分,也可使用DECIMAL存儲比BIGINT還要大的整數。Mysql既支持精確類型,也支持不精確類型。
DECIMAL類型用於存儲精確的小數。
在Mysql5.0或者更高版本支持精確運算,而在Mysql4.1以及更早版本中使用浮點運算會出現異常(主要是精度的損失致使的)。
FLOAT和DECIMAL類型均可以指定進度。
對於DECIMAL列能夠指定小數點先後所容許的最大位數,這會影響列的空間消耗。有不少方法能夠指定FLOAT(浮點)列所須要的精度,這會使得Mysql悄悄選擇了不一樣的數據類型,或者在存儲時對值進行取捨,可是這些精度每每都是非標準的,因此通常建議只指定數據類型不指定精度。
因爲須要額外的空間和計算開銷,因此應該儘可能只在對小數進行精確計算時才使用DECIMAL。
好比存儲財務數據,可是若是數據量比較大的時候,能夠考慮使用BIGINT代替DECIMAL,將須要存儲的貨幣單位根據小數的位數乘以相應的倍數便可。
FLOAT和DOUBLE類型支持使用標準的浮點運算進行近似計算。
Mysql支持多種字符串類型,每種類型還有不少變種。其中VARCHAR和CHAR是兩種最主要的字符串類型。
注意:Mysql存儲引擎存儲CHAR或者VARCHAR值的方式在內存中和在磁盤上可能不同,因此Mysql服務器從存儲引擎讀取的值可能須要轉換爲另一種存儲格式。
VARCHAR類型用於存儲可變長字符串,是最多見的字符串數據類型。
VARCHAR比定長類型更節省空間,由於它僅使用必要的空間(越短的字符串使用越少的空間)。
VARCHAR須要使用1或2個額外字節記錄字符串的長度。
VARCHAR節省了存儲空間,因此對性能是有幫助的。
下面是一些VARCHAR適合使用的場景:
(1)字符串列的最大長度比平均長度大不少。
(2)列的更新不多,因此碎片不是問題。
(3)使用了像UTF-8這樣複雜的字符集,每一個字符都使用不一樣的字節數進行存儲。
CHAR類型是定長的。(Mysql老是根據定義的字符串長度分配足夠的空間)
CHAR適合存儲很短的字符串,或者全部值都接近同一個長度。
和VARCHAR和CHAR相似的類型還有BINARY和VARBINARY,它們存儲的都是二進制字符串。
注意:使用VARCAHR(5)和VARCHAR(200)存儲「hello」的空間開銷都是同樣的,那麼使用更短的列有什麼優點呢?(事實證實有很大的優點)
更長的列會消耗更多的內存,由於Mysql一般會分配固定大小的內存塊來保存內部值。尤爲是使用內存臨時表進行排序或者操做時會特別糟糕。在利用磁盤臨時表進行排序時也一樣糟糕。
注意:歸根到底,最好的策略是隻分配真正須要的空間。
BLOB和TEXT都是爲存儲很大的數據而設計的字符串數據類型,分別使用二進制和字符方式存儲。
實際上它們分別屬於兩組不一樣的數據類型家族:
字符串類型有TINYTEXT、SMALLTEXT、TEXT、MEDIUMTEXT、LONGTEXT;
二進制類型有TINYBLOB、SMALLBLOB、BLOB、MEDIUMBLOB、LONGBLOB;
可使用枚舉(ENUM)代替字符串類型。不少時候建議使用枚舉列代替經常使用的字符串類型。
(1)枚舉列能夠把一些不重複的字符串存儲成一個預約義的集合。
(2)Mysql在存儲枚舉時很是緊湊,會根據列表值的數量壓縮到一到兩個字節中。
(3)Mysql在內部會將每一個值在列表中的位置保存爲整數,而且在表的.frm文件中保存「數字-字符串」映射關係的「查找表」。
注意:有一個使人吃驚的地方是,枚舉字段是按照內部存儲的整數而不是定義的字符串進行排序的。
注意:枚舉最很差的地方是:字符串列表是固定的,添加或者刪除字符串必須使用ALTER TABLE,所以對於一系列將來可能會改變的字符串,使用枚舉並非一個好主意,除非接受只能在列表末尾添加元素。
注意:因爲Mysql把每一個枚舉值保存爲整數,而且必須進行查找才能轉換爲字符串,因此枚舉列有一些開銷。
Mysql有不少類型能夠保存日期和時間值,好比YEAR和DATE。
Mysql能存儲的最小時間粒度爲秒(MariaDB支持微秒級別的事件類型)。可是Mysql也可使用微秒級別的粒度進行臨時運算。
大部分時間類型都沒有替代品,所以沒有什麼是最佳選擇的問題。
接下來惟一的問題是保存日期和時間的時候須要作什麼。
DATETIME
(1)這個類型能保存大範圍的值,從1001年到9999年,精度爲秒。
(2)DATETIME把時間和日期封裝到格式爲YYYYMMDDHHMMSS的整數中,與時區無關。
(3)DATETIME使用8個字節的存儲空間。
TIMESTAMP
(1)TIMESTAMP類型保存了從1970年1月1日午夜以來的秒數,它和UNIX時間戳相同。
(2)TIMESTAMP只使用4個字節的存儲空間,所以它的範圍比DATETIME小得多。
(3)TIMESTAMP顯示的值依賴時區。
DATETIME和TIMESTAMP的對比:
(1)默認狀況下,若是插入時沒有指定第一個TIMESTAMP列的值,Mysql則設置這個列的值爲當前時間。(這是DATETIME沒有的特性)
(2)在插入一行記錄時,Mysql默認也會更新第一個TIMESTAMP列的值。
(3)TIMESTAMP列默認爲NOT NULL,這與其餘的數據類型不同。
總結
(1)除了特殊行爲以外,一般也應該儘量使用TIMESTAMP,由於它比DATETIME空間效率更高。
(2)通常來說不建議把UNIX時間戳保存爲整數值,這不會帶來任何收益,用整數保存時間戳格式一般不方便處理。
(3)若是需呀存儲比秒更小粒度的日期和時間值,可使用BIGINT類型存儲微秒級別的時間戳,或者使用DOUBLE存儲秒以後的小數部分,也能夠用MariaDB替代Mysql。
BIT定義一個包含單個位的字段,BIT(2)存儲2個位,最大長度是64個位。
注意:通常建議謹慎使用BIT類型,對於大部分應用來說最好避免使用這種類型。
爲identifier(標識列)選擇合適的數據類型很是重要。
通常來說更有可能用標識列與其餘值進行比較,或者經過標識列尋找其餘列。
當選擇標識列的類型時,不只僅須要考慮存儲類型,還須要考慮Mysql對這種類型怎麼執行計算和比較。
一旦選定了一種類型,要確保在全部關聯表中都使用一樣的類型。
在能夠知足值的範圍需求,而且預留將來增加空間的前提下,應該選擇最小的數據類型。
注意:整數一般是標識列最好的選擇,由於它們很快並且可使用AUTO_INCREMENT。
注意:ENUM和SET是最糟糕的選擇了;若是可能也儘量避免使用字符串做爲標識列,由於它們很消耗空間而且一般比數字類慢。
對於數據庫設計,必定要三思然後行,選擇最適合的數據列類型還有決定數據列的大小都是很關鍵的一步。
其實大可沒必要驚慌,不管對於任何類型需求的數據表設計,你只要記住一個原則,很重要很重要很重要的原則:儘量使用正確存儲數據的最小數據類型。
PS:很晚了,總結先草草了事,往後還會更新......