選擇合適的數據類型
在使用MySQL創建數據表的時候會遇到一個問題,如何為字段選擇合適的數據類型.比如創建一個員工信息表,每個字段都可以用很多種類型來定義,
int,char,float等等.
char和varchar
char和varchar都是用來存儲字符串類型的數據,但是他們保存和檢索的方式不一樣.char屬於固定長度的字符類型,二varchar屬於可變成的字符類型
值 | char(4) | 存儲需求 | varchar(4) | 存儲需求 |
'' | ' ' | 4個字節 | '' | 1個字節 |
'ab' | 'ab ' | 4個字節 | 'ab' | 3個字節 |
'abcd' | 'abcd' | 4個字節 | 'abcd' | 5個字節 |
'abcdefg' | 'abcd' | 4個字節 | 'abcd' | 5個字節 |
由於char是固定長度的,所以它的處理速度比varchar快得多,但是其缺點是浪費存儲空間,程序需要對尾行空格進行處理,所以對那些變化不打並且查詢速度有較高的要求的數據可以考慮使用char類型來存儲
在mysql中,不同的存儲引擎對char和varchar的使用原則有所不同
- MyISAM存儲引擎
- 建議使用固定長度的數列代替可變長度的數據列
- InnoDB存儲引擎
- 建議使用varchar類型,對於InnnoDB數據表,內部的行存儲格式沒有區分固定長度和可變長度,因此使用char列不一定比可變長度的varchar性能好
- 由於char平均占用空間多余varchar,因此varchar來UI消化需要處理的數據航的存儲總量和磁盤I/O是比較好的.
TEXT和BLOB
介紹
在選擇大文本的時候我們會優先選擇text類型或者blob比如文章.
那么TEXT和BLOB最主要的區別是 BLOB能用來保存二進制數據比如照片.而text智能保存字符串數據,比如文章和日記.
根據存儲的文本長度不同和存儲的字節不同我們可以使用
MEDIUMTEXT,LONGTEXT和MUDIUMBLOB,LONGBLOB
常見問題
空洞問題
BLOB和text在執行了大量的刪除操作時,會留下很大的'空洞',以后填入這些'空洞'的記錄在插入的性能上回有影響.為了提高性能,建議使用OPTIMIZE TABLE功能對類表進行碎片整理,
避免空洞帶來的性能問題
空洞例子:
CREATE TABLE t ( id VARCHAR(100), context TEXT ); INSERT INTO t VALUES(1,repeat('haha',100)); INSERT INTO t VALUES(2,repeat('haha',100)); INSERT INTO t VALUES(3,repeat('haha',100)); insert into t select * from t; insert into t select * from t; ..... insert into t select * from t;
這時候文件的大小為:
刪除id=1的數據,那么就是刪除了1/3的數據:
mysql> delete from t where id=1; Query OK, 32768 rows affected (0.63 sec)
再看文件大小,我們可以看到文件大小還是96MB,這就形成了空洞.
我們用OPTIMIZE進行優化:
mysql> optimize table t;
此時我們再看文件,已經變成了60MB,文件大大的縮小了,說明'空洞被收回了'
文件索引
使用合成索引來提高大文本字段的查詢性能:
合成索引就是根據大文本的字段的內容建立一個散列值,並且把這個值存儲在單獨的數據列中,接下來可以通過檢索散列值來找到數據.
但是,要注意這種技術只能用於精確匹配(對於< >=等范圍搜索是沒有用的)
可以使用MD5()函數來生成散列值.
下面介紹一下合成索引的方法:
CREATE TABLE t ( id VARCHAR(100), context BLOB, hash_value char(32) ); INSERT INTO t VALUES(1,repeat('beijing',2),MD5(context)); INSERT INTO t VALUES(1,repeat('beijing2008',2),MD5(context)); mysql> select * from t where hash_value=md5(repeat('beijing2008',2)); +------+------------------------+----------------------------------+ | id | context | hash_value | +------+------------------------+----------------------------------+ | 1 | beijing2008beijing2008 | 0fe88accc8741a9d1bc323bd286866bb | +------+------------------------+----------------------------------+
由於這種技術只能用於精確匹配,從一定程度上減少了I/O,提高了查詢效率.如果需要對BLOB字段進行模糊查詢,MYsql提供了前綴索引,也就是只為字段的前n列創建索引
create index idx_blob on t(context(100));
mysql> desc select * from t where context like "beijing%" \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t type: range possible_keys: idx_blob key: idx_blob key_len: 103 ref: NULL rows: 2 Extra: Using where 1 row in set (0.04 sec)
對context的前100個字符進行模糊查詢,就可以用前綴索引.
注意這里的%不能放在前面,否則不能命中索引
避免使用select *
不要用select * 檢索大型的BLOB或者TEXT值
除非能夠確定約束條件where只會找到需要的數據,否則很可能毫無目的在網絡上傳輸大量的值.
用戶可以用搜索索引列,決定需要的那些數據行,然后從符合條件的數據中檢索BLOB或者TEXT
分表
水平分表,在某些環境中,如果把這些大型的列數據移動到第二章表數據中,那么把原數據表中的數據列轉換成固定長度的數據行格式,
那么它就是有意義的.這會減少表中的碎片,可以得到固定長度的性能優勢.
浮點數和定點數
在MYSQL中用float,double來標識浮點數.當一個字段被定義浮點類型后,如果插入的數據精度超過該列定義的實際精度,那么會采取四舍五入的辦法來得到實際的值.
定點數不同於浮點數,他是用字符串的形式存存放的,所以插入的實際值精度大於實際定義的精度,如果在傳統模式下,會直接報錯,不能插入數據.
CREATE TABLE test(c1 float(10,2),c2 decimal(10,2)); INSERT INTO text VALUES(131072.32,131072.32); mysql> select * from test; +-----------+-----------+ | c1 | c2 | +-----------+-----------+ | 131072.31 | 131072.32 | +-----------+-----------+
可以看到c1列的值從131072.32 變成了131072.31,這是上面的數值在使用單精度浮點數表示時,產生了誤差.
注意:
- 浮點數存在誤差問題
- 對貨幣等對精度要求比較高的數據,應該用定點數表示或存儲
- 在變成中,如果用到浮點數,要特別注意誤差問題,盡量避免做浮點數比較
日期類型的選擇
- 根據實際需要選擇能夠滿足的最小存儲的日期類型,如果只需要記錄年,那么用一個字節來存儲的YEAR類型就而已滿足.而不需要用4個字節的date,不僅節省空間,還提高查詢效率
- 如果要記錄年月日時分秒,並記錄比較久遠,那么最好使用datetime,不要使用timestamp
- 如果記錄的日期需要不同的時區的用戶使用,那么最好使用timestamp,因為日期類型中,只有他能夠和實際時區相對應