一、選擇優(yōu)化的數(shù)據(jù)類型
- 一般情況下,應該盡量使用可以正確存儲數(shù)據(jù)的最小數(shù)據(jù)類型。但是要確保沒有低估需要存儲的值的范圍,因為在schema中的多個地方增加數(shù)據(jù)類型的范圍是一個非常耗時的操作。
- 在使用字符類型時需要考慮其校對規(guī)則(排序規(guī)則)。
- 盡量避免使用NULL,因為可以為NULL的列會使用更多的存儲空間,當它被索引時,每個索引的記錄需要一個額外的字節(jié)用于記錄它是否為NULL。
二、整數(shù)類型
MySQL可以為整數(shù)類型指定寬度,對于大多數(shù)應用這是沒有意義的,它不會限制值的合法范圍,只是規(guī)定了MySQL的一些交互工具(如命令行客戶端)用來顯示字符的個數(shù)。
三、實數(shù)類型
實數(shù)是帶小數(shù)部分的數(shù)字,MySQL即支持精確類型,也支持不精確類型。FLOAT和DOUBLE類型支持使用標準的浮點運算進行近似計算;DECIMAL類型用于存儲精確的小數(shù)。
因為需要額外的控件和計算開銷,所以應該盡量只在對小數(shù)進行精確計算時才使用DECIMAL--如存儲財務數(shù)據(jù)。但在數(shù)據(jù)量比較大的時候,可以考慮使用BIGINT代替DECIMAL,將需要存儲的貨幣單位根據(jù)小數(shù)位數(shù)乘以相應的倍數(shù)即可。
四、字符串類型
字符串定義的長度不是字節(jié)數(shù),如varchar(10),定義的是字符長度為10的varchar類型。
varchar類型用于存儲可變長字符串,它需要使用1或2個額外的字節(jié)記錄字符串的長度。
由于varchar是變長的,在update時,可能新的值比原先的長,從而使得新的行比原來的行長,此時導致需要做一些額外的操作,不同的存儲引擎處理方式不一樣,InnoDB會通過分裂頁的方式來使行可以放進頁內(nèi)(產(chǎn)生碎片)。
下面這些情況適合使用varchar:
a. 字符串列的最大長度比平均長度大很多;
b. 列的更新很少,減少碎片的產(chǎn)生。
c. 使用了像UTF-8這樣復雜的字符集,每個字符都使用了不同的字節(jié)數(shù)進行存儲。char類型是定長的,Mysql總是根據(jù)定義的字符串長度分配足夠的空間。char適合存儲很短的字符串,或者所有值都接近同一個長度。對于經(jīng)常變更的數(shù)據(jù),char也比varchar更好,因為定長不易產(chǎn)生碎片
慷慨是不明智的,使用varchar(5)和varchar(200)存儲‘hello’的空間開銷是一樣的,但是在實際使用時,我們應該按照實際的存儲長度來定義,因為Mysql在運行時,更長的列會消耗更多的存儲內(nèi)存。Mysql通常會分配固定大小的內(nèi)存塊來保存內(nèi)部值,尤其是使用內(nèi)存臨時表進行排序操作時使用更長的列會顯得特別糟糕。
BLOB和TEXT類型
a. Mysql把每個BLOB和TEXT值當作一個獨立的對象處理,存儲引擎在存儲是通常會做特殊處理
b. BLOB和TEXT值太大時,InnoDB會使用專門的“外部”存儲區(qū)域來進行存儲,此時每個值在行內(nèi)需要1~4個字節(jié)存儲一個指針,然后在外部存儲區(qū)域存儲實際的值
c. BLOB類型存儲的是二進制數(shù)據(jù),沒有排序規(guī)則或字符集
d. Mysql只對每個TEXT列的最前max_sort_length字節(jié)而不是整個字符串做排序
e. Mysql不能將BLOB和TEXT列全部長度的字符串進行索引。
四、其他類型
使用枚舉類型時要注意,枚舉字段是按照內(nèi)部存儲的證書而不是定義的字符串進行排序的。枚舉最不好的地方是,字符串列表是固定的,添加或刪除字符串必須使用ALTER TABLE,而在舊版本的MySQL中,該操作是會鎖表的。
日期和時間類型
a. DATETIME精度為秒,使用8個字節(jié)的存儲。
b. TIMESTAMP使用4個字節(jié)存儲。默認情況下,如果插入數(shù)據(jù)時,沒有指定TIMESTAMP列的值,MySQL則設置這個列的值為當前時間。
d. TIMESTAMP列默認為NOT NULL.在選擇標識列(identifier,主鍵)的類型時,不僅僅需要考慮存儲類型,還需要考慮mysql對這種類型怎么執(zhí)行計算和比較。通常整數(shù)是標識列最好的選擇,因為計算快,而且可以使用AUTO_INCREMENT;因該避免使用字符串類型作為標識列,因為它們很消耗空間,并且通常比數(shù)字類型慢,MyISAM默認對字符串使用壓縮索引,這會導致查詢慢很多。
太多的列 Mysql在執(zhí)行查詢操作時需要在執(zhí)行層和存儲引擎層之間通過行緩沖格式拷貝數(shù)據(jù),然后在執(zhí)行層將緩沖的內(nèi)容解碼成各個列,轉(zhuǎn)換的代價依賴于列的數(shù)量
五、范式和反范式
- 在范式化的數(shù)據(jù)庫中,每個事實數(shù)據(jù)會出現(xiàn)并且只出現(xiàn)一次。相反,在反范式化的數(shù)據(jù)庫中,信息是冗余的,可能會存儲在多個地方。
六、加快ALTER TABLE操作的速度
Mysql執(zhí)行修改表結(jié)構(gòu)的操作方法是用新的結(jié)構(gòu)創(chuàng)建一個空表,從舊表中查處所有數(shù)據(jù)插入新表,然后刪除舊表。
一般而言,大部分ALTER TABLE操作將導致Mysql服務中斷。常使用兩個技巧來完成相關的修改操作:一種是先在一臺不提供服務的機器上執(zhí)行ALTER TABLE操作,然后和提供服務的主庫進行切換; 另一種是通過“影子拷貝”,影子拷貝的技巧是用要求的表結(jié)構(gòu)創(chuàng)建一張和源表無關的新表(影子表),然后通過重命名和刪表操作交換兩張表。
不是所有的ALTER TABLE操作都會引起表重建。例如,有兩種方法可以改變或刪除一個列的默認值。
eg: ALTER TABLE tb MODIFY COLUMN tb.col_1 TINYINT(3) NOT NULL DEFAULT 5;
列的默認值實際上存在表的.frm文件中,所以理論上可以直接修改這個文件而不需要改動表本身,但是Mysql并沒有采用這種優(yōu)化,所有的MODIFY COLUMN 操作都將導致表重建。
但是通過ALTER COLUMN來操作的話則會直接修改.frm文件而不涉及表數(shù)據(jù),所以這個操作非??臁H缦滤荆?br> eg: ALTER TABLE tb ALTER COLUMN tb.col_1 TINYINT(3) NOT NULL DEFAULT 5;