Web項目中,當Java或者Go等語言速度提升到瓶頸的時候,我們需要關心MySQL的優化
可以優化的方面有很多:設計表、負載均衡、讀寫分離、SQL語句優化等
(1)IP地址設計
例如我們需要存儲IP地址:192.168.1.1
第一反應是選用VARCHAR(15);但是更好的方式是INT UNSIGNED(占用四個字節)
因為:IP地址可以很容易地轉換為無符號整數
仔細觀察IP地址,四部分都是0-255的數字,1個字節(8位)恰好可以表示0-255的整數
而MySQL有函數:inet_aton():地址轉成數字,inet_ntoa():數字轉成地址
示例:
SELECT INET_ATON('192.168.0.1')
結果:3232235521
SELECT INET_NTOA("3232235521")
結果:192.168.0.1
INT UNSIGNED占用字節少於VARCHAR(15),並且整數查詢效率更高(代價是需要使用轉換函數)
總結:在設計字段的時候,盡量使用整數來表示字符串
(2)關聯表設計
整型的優勢:固定存儲空間,通常是少量空間
例如:MySQL內部的枚舉和集合類型:
enum(男,女,未知)
insert into user(gender) values(男)
這句話在MySQL中實際存儲的是1,這就是最典型的把字符串存成整數
注意:實際中,很少使用mysql的enum和set,因為維護成果過高
比如性別需要把未知改為人妖,需要執行alter table modify column操作,需要獨占整張表,檢查記錄性別值的合法性
如果一定要使用這種方式:關聯表,字段id,title,存儲1,男,2,女。這種方式使用較為廣泛
但是,比如家庭地址這樣的字符串,是無法改成整型的,不能強行操作
(3)金額存儲
金額的存儲對數據的精度要求較高,按理來說要使用DECIMAL()
例如DECIMAL(10,2):有2位小數的定點數
實際中有另一種操作:INT或BIGINT,這時候為了精度不丟失,采用”分“為單位(12.51元記錄為1251)
計算機中小數是無法做到不損失精度的,但是金額較特殊,固定了兩位小數,所以可以采用這種方式
而且編程中,整數的計算相對於小數較為方便
DECIMAL也有擅長的地方,比如存儲大數:123456789123456789
這時候不可以使用INT,只能使用BIGINT或者DECIMAL
注意:這里為什么我們不選擇浮點數DOUBLE和FLOAT呢?因為浮點數會導致精度丟失
原因:浮點數占用固定的存儲空間,無論存儲多大的數,空間是一定的;但是定點數空間會隨着數字變大而增加
由此引出了定長類型和變長類型:
定長類型:存儲空間固定(INT、FLOAT、DOUBLE、CHAR、DATE、TIME、DATETIME、YEAR、TIMESTAMP)
變長類型:存儲空間可變(VARCHAR、DECIMAL、TEXT)
注意:只有定長類型才會有損失精度的問題,定長類型效率較高
結論:在乎存儲空間采用定長類型,在乎存儲精度采用變長類型
(4)TEXT和VARCHAR的選擇
TEXT:通常感覺存儲容量較大,其實最大容量和VARCHAR的最大容量幾乎一樣
但是,TEXT是獨立存儲的,不占用字段的總空間,但是VARCHAR占用字段總空間,通常總空間是65535字節
結論:更大的數據還是采用TEXT更好
更大的數據類型有LONGTEXT,可以用於選擇
(5)字段設計的原則
1.盡可能選擇小的數據類型,這條無需多說
2.盡可能使用NOT NULL,因為數據庫不需要判斷是否為NULL,NULL在MYSQL中的存儲和運算更麻煩:
NULL參與常規運算的結果都是NULL,當判斷是否為空的時候,必須采用IS NULL和IS NOT NULL
MYSQL中每條記錄會使用到額外的存儲空間,用於表示每個字段是否為NULL
通常使用一個特殊的數據來占位,比如我要表達NULL通常設置為空字符串或者0
這種情況又會出現問題,比如成績字段,0代表沒有的話無法區分0分的學生,所以可以采用-1,消除歧義
3.字段注釋要完整:gender int comment '性別'
4.單張表的字段數量不宜過多,通常最多二三十個;數量過多通常會出現某個業務邏輯只是用其中一部分,浪費性能
5.預留字段,比如field1 int field2 varcahr等等;后期項目如果需要更改表結構,這樣做會方便很多
(6)關聯表的設計
一對一:一條記錄的字段較多,分布到多個表中存儲
例如學生表,基礎信息:姓名、身高、班級,還有一些不常用的數據:籍貫、家庭成員等信息
這時候應該設計基礎信息一張表,不常用數據一張表,使用相同的主鍵來表示
一對多:在多的一端使用關聯字段,關聯一端的主鍵
例如文章和分類表,分類是一端,文章是多端;那么在文章表中需要有一個分類ID的字段做關聯
多對多:使用中間表來實現
例如文章和標簽,多對多,那么就需要一張表,字段至少有ID、文章ID、標簽ID,每一條記錄代表一個關聯
(7)范式
第一范式:字段的原子性,不可再分割
關系型數據庫默認滿足第一范式,MYSQL滿足
但也可以強行做:比如一個時間字段,同時寫入開始時間和結束時間,這就不合理
一個容易出現的問題:(6)中的例子,文檔和標簽的設計中:如果我為了省事,不引入第三張表,而是在文章表中用一個字段標簽IDs字段(例如存入1,2,3)
這種情況很常見,是不合理的做法,在更新的時候會出現很多問題,需要把逗號拆開處理,而且無法建索引
除非是類似日志系統,存入后不再維護,那么可以使用這種方式
第二范式:滿足第一范式后,消除對主鍵的部分依賴(A字段可以確定B字段,那么B字段依賴A字段)
主鍵:可以唯一標識記錄的字段或者字段集合
部分依賴:如果某個字段依賴復合主鍵的一部分字段,稱之為對主鍵的部分依賴
例如一個課程信息表,字段有:老師,性別,班級,教室,時間,但是不存在ID
這時候需要我們選一個主鍵,這里面每一個字段都不能作為主鍵
老師和班級同時可以作為一個主鍵(復合主鍵)
但是性別對主鍵是部分依賴,如何消除呢?
部分依賴的產生必須是復合主鍵,那么增加一個ID即可消除對主鍵的部分依賴
第三范式:第二范式的基礎上,消除對主鍵的傳遞依賴
傳遞依賴:C依賴於B,B依賴於主鍵,那么C對主鍵存在傳遞依賴關系
上門的例子:性別依賴於老師,老師依賴於ID,那么存在傳遞依賴關系
消除方式:將獨立數據單獨建表,使用關聯字段進行存儲
例子中,建立一個單獨的表,記錄老師和性別的關系
總結:獨立數據獨立建表;表中存在與業務邏輯無關的ID主鍵;表之間的關系由關聯字段(或關聯表)進行表示
通俗來講:我們建表中,基本都是滿足三大范式的