Web項目中,當Java或者Go等語言速度提升到瓶頸的時候,我們需要關心MySQL的優化 可以優化的方面有很多:設計表、負載均衡、讀寫分離、SQL語句優化等 (1)IP地址設計 例如我們需要存儲IP地址:192.168.1.1 第一反應是選用VARCHAR(15);但是更好的方式是INT UNSIG ...
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主鍵;表之間的關係由關聯欄位(或關聯表)進行表示
通俗來講:我們建表中,基本都是滿足三大範式的