1. 程式人生 > >表的優化與列型別的選擇

表的優化與列型別的選擇

表的優化與列型別選擇

 

表的優化:

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