表的優化與列型別的選擇
表的優化與列型別選擇
表的優化:
1: 定長與變長分離
如 id int, 佔4個位元組, char(4) 佔4個字元長度,也是定長, time
即每一單元值佔的位元組是固定的.
核心且常用欄位,宜建成定長,放在一張表.
而varchar, text,blob,這種變長欄位,適合單放一張表, 用主鍵與核心表關聯起來.
2:常用欄位和不常用欄位要分離.
需要結合網站具體的業務來分析,分析欄位的查詢場景,查詢頻度低的欄位,單拆出來.
3:合理新增冗餘欄位.
看如下BBS的效果
每個版塊裡,有N條帖子, 在首頁,顯示了版塊資訊,和版塊下的帖子數.
這是如何做的?
boardid |
boardname |
postnum |
8 |
開班資訊 |
2 |
9 |
每日視訊及程式碼 |
1 |
postid |
boardid |
title |
123 |
8 |
論壇開張了 |
129 |
8 |
灌水 |
133 |
9 |
來一帖 |
如果board表只有前2列,則需要取出版塊後,
再查post表,select count(*) from post group by board_id,得出每個版塊下的帖子數.
如果有postnum欄位,每發一個帖子時,對postnum欄位+1;
再查詢版塊下的帖子數時, 只需要1條語句直接查boardid
select boradid, boardname,postnum from board;
典型的”空間換時間”
列選擇原則:
1:欄位型別優先順序 整型 > date,time > enum,char>varchar > blob
列的特點分析:
整型: 定長,沒有國家/地區之分,沒有字符集的差異
time定長,運算快,節省空間. 考慮時區,寫sql時不方便 where > ‘2005-10-12’;
enum: 能起來約束值的目的, 內部用整型來儲存,但與char聯查時,內部要經歷串與值的轉化
Char 定長, 考慮字符集和(排序)校對集
varchar, 不定長 要考慮字符集的轉換與排序時的校對集,速度慢.
text/Blob 無法使用記憶體臨時表
附: 關於date/time的選擇,大師的明確意見
http://www.xaprb.com/blog/2014/01/30/timestamps-in-mysql/
性別: 以utf8為例
char(1) , 3個字長位元組
enum(‘男’,’女’); // 內部轉成數字來存,多了一個轉換過程
tinyint() , // 0 1 2 // 定長1個位元組.
2: 夠用就行,不要慷慨 (如smallint,varchar(N))
原因: 大的欄位浪費記憶體,影響速度,
以年齡為例 tinyint unsigned not null ,可以儲存255歲,足夠. 用int浪費了3個位元組
以varchar(10) ,varchar(300)儲存的內容相同, 但在表聯查時,varchar(300)要花更多記憶體
3: 儘量避免用NULL()
原因: NULL不利於索引,要用特殊的位元組來標註.
在磁碟上佔據的空間其實更大.
實驗:
可以建立2張欄位相同的表,一個允許為null,一個不允許為Null,各加入1萬條,檢視索引檔案的大小. 可以發現,為null的索引要大些.(mysql5.5裡,關於null已經做了優化,大小區別已不明顯)
另外: null也不便於查詢,
where 列名=null;
where 列名!=null; 都查不到值,
where 列名 is null ,或is not null 才可以查詢.
create table dictnn (
id int,
word varchar(14) not null default '',
key(word)
)engine myisam charset utf8;
create table dictyn (
id int,
word varchar(14),
key(word)
)engine myisam charset utf8;
alter table dictnn disable keys;
alter table dictyn disable keys;
insert into dictnn select id,if(id%2,word,'') from dict limit 10000;
insert into dictyn select id,if(id%2,word,null) from dict limit 10000;
alert table dictnn enable keys;
alter table dictyn enable keys;
Enum列的說明
1: enum列在內部是用整型來儲存的
2: enum列與enum列相關聯速度最快
3: enum列比(var)char 的弱勢---在碰到與char關聯時,要轉化. 要花時間.
4: 優勢在於,當char非常長時,enum依然是整型固定長度.
當查詢的資料量越大時,enum的優勢越明顯.
5: enum與char/varchar關聯 ,因為要轉化,速度要比enum->enum,char->char要慢,
但有時也這樣用-----就是在資料量特別大時,可以節省IO.
試驗:
create table t2 (
id int,
gender enum('man','woman'),
key(gender)
)engine myisam charset utf8;
create table t3 (
id int,
gender char(5) not null default '',
key(gender)
)engine myisam charset utf8;
alter table t2 disable keys;
alter table t3 disable keys;
insert into t2 select id,if(id%2,'man','woman') from dict limit 10000;
insert into t3 select id,if(id%2,'man','woman') from dict limit 10000;
alter table t2 enable keys;
alter table t3 enable keys;
mysql> select count(*) from t2 as ta,t2 as tb where ta.gender=tb.gender
mysql> select count(*) from t3 as ta,t3 as tb where ta.gender=tb.gender
列<---->列 |
時間 |
Enum<--->enum |
10.53 |
Char<---->char |
24.65 |
Enum<---->char |
18.22 |
如果t2表的優勢不明顯, 加大t3的gender列 ,char(15), char(20)...
隨著t3 gender列的變大,t2表優勢逐漸明顯.
原因----無論enum(‘manmaman’,’womanwomanwoman’) 列舉的字元多長,內部都是用整型表示, 在記憶體中產生的資料大小不變,而char型,卻在記憶體中產生的資料越來越多.
總結: enum 和enum型別關聯速度比較快
Enum 型別 節省了IO