1. 程式人生 > >mysql的char,varchar,text,blob

mysql的char,varchar,text,blob

mysql的char,varchar,text,blob是幾個有聯絡但是有有很大區別的欄位型別,這算是mysql的基礎吧,可是基礎沒有學好,惡補一下。

先簡單的總結一下:

char:定長,最大255個字元

varchar:變長,最大65535個字元(既是單列的限制,又是整行的限制)

text:變長,有字符集的大物件,並根據字符集進行排序和校驗,大小寫不敏感

blob:變長,無字符集的二進位制大物件,大小寫敏感

以下只是我個人的理解,才疏學淺,望各路高人指點。

我使用的引擎是myisam,所以以下的探討是集中在myisam上的。

首先解釋char,char是專案中常用的欄位型別之一,它代表的含義是採用固定長度儲存資料,換句話說,資料初始化的是就為該型別的欄位分配固定長度的儲存空間,即使沒有達到儲存空間的長度,實際佔用的儲存空間也是定義時的長度。舉個例子來說,比如某欄位 a     char(50),指定的長度是50個字元的儲存空間,那麼當你存入一個字元:“abc”的時候,實際上字元長度是3個字元,但是佔用的硬碟空間還是50個字元。很顯然,char的缺點就出來了:浪費儲存空間!但是同時char的優點也顯示出來了:固定長度,(索引)效率極高,不存在碎片。

這裡我們再探討一下char的儲存方式,雖然char會浪費極大的儲存空間,但是你想過對於字串的前後空格char是如何處理的嗎?當儲存的字串沒有達到char的最大長度時,字串後面是不會以空格來填充的,而且char會過濾字串末端的空格然後儲存,而在比較字串的時候又會自動空格填充到字串的末端。

好了,對char有了瞭解以後,對varchar的理解也就容易了。

varchar是儲存可變長度的字串,簡單的說我們定義表機構的時候指定的欄位長度是最大長度,當字串沒有達到最大長度的時候以字串的實際長度來儲存的,不佔用多餘的儲存空間。因此,一般情況下,varchar比char節省儲存空間,但是也經常有例外,後面接著探討這個問題。

一個特殊的情況是建立表的時候採用ROW_FORMAT=FIXED選項(預設的是ROW_FORMAT=DYNAMIC),那麼mysql就會為每行資料分配固定長度的儲存空間,當然這是特例。不知道你有沒有想過:為什麼有管理員願意這樣做呢?對varchar分配固定長度的儲存空間是有道理的。舉一個常見的例子,假設採用DYNAMIC預設選項,那麼我們建立一個欄位b  varchar(100),現在我們插入一個只有10個字元的資料:abcdefghij,很好,只佔用了10個字元的空間,相比char節省了不少儲存空間。但是你想到問題了嗎?比如某天以後你發現這個欄位需要更新一下,更新為20個字元的資料:abcdefghijklmnopqrst,你知道資料庫該如何儲存嗎?原先的儲存位置分配的只有10個字元的空間,現在要儲存20個字元,小於長度限制(最大長度是100個字元),問題就來了,mysql會如何處理呢?這裡接下來可能要探討mysql儲存層面的分頁機制或者拆分機制,就不再繼續深入了。總之無論mysql採取什麼方式,肯定會在磁碟上形成碎片,久而久之形成的磁碟碎片對系統效率是一個致命的打擊,所以我們經常看到有管理員要把mysql匯出然後匯入,就是為了解決這個問題,提高效率。

上面我們提出來了一個問題:varchar比char節省儲存空間,但是也經常有例外!對於這個疑問我們如何理解呢?要深入分析這個問題,我們需要再次深入瞭解varchar的儲存機制。常用的中文儲存一般採用gbk或者utf-8兩種字符集,gbk每個字元佔2個位元組,utf-8每個字元佔3個位元組,所以:gbk字符集的最大儲存長度是

(65535-1-1)/2= 32766或者(65535-1-2)/2= 32766,這個演算法的含義是:65535是varchar的最大長度,第一個-1表示實際儲存位置是從第二位開始的,第二個-1或者-2代表的含義是:varchar 欄位是將實際內容單獨儲存在聚簇索引之外,內容開頭用1到2個位元組表示實際長度(長度小於255時用1個位元組,長度超過255時需要2個位元組),除以2的原因是一個gbk字符集的字元佔2個位元組長度,所以根據情況可以得出gbk字符集的最大儲存長度是32766。

那麼UTF-8字符集呢?演算法如下:

(65535-1-1)/3=  21844或者(65535-1-2)/3= 21844減1減2的含義同上,除以3的含義是一個utf-8的字符集字元佔用實際長度是3個位元組。

看完了這段你就能理解char(1)和varchar(1)佔用的儲存空間了吧?在這個極端情況下,很顯然char更節省儲存空間,因為char沒有管理資料的額外開銷。

 

接下來,我們重點探討一下varchar的65535儲存長度代表的真正含義是什麼?因為前面說過一句話:既是單列的限制,又是整行的限制!我們詳細來解釋一下。對於單列的限制,我們舉一個例子:create table tablename(c varchar(N)) charset=gbk;那麼N的最大值是(65535-1-2)=32766。同時,如果同一個有其他欄位的話,那麼所有欄位的長度和不能超過65535,舉個例子:create table tablename(c1 int(4), c2 char(30), c3 varchar(N) ) charset=utf8,那麼N的最大值是(65535-1-2-4-30*3)/3=21812,也許你很理解int(4)需要4個字元的位置,這裡可能需要更深入的瞭解int的儲存機制,我不太瞭解,書還沒有看呢。總之int型別佔用4個字元的長度,換句話說create table tablename(c1 int(32), c2 char(30), c3 varchar(N) ) charset=utf8,N的最大值也是(65535-1-2-4-30*3)/3=21812。我做了一個簡單的實驗,看截圖。

第一張varchar的長度大於21812,於是失敗了

 廢話了這麼多,總之一句話:char在浪費儲存空間的劣勢下,獲得了較高的效率,varchar相反。接下來我們也要總結一下什麼情況下使用char,什麼情況下使用varchar。

原則一:根據字串長度確定,凡是固定長度的字串或者類似固定長度的字串一律用char。比如身份證號碼,手機號碼,銀行卡號,MD5,雜湊值等這是字串是固定長度的,毫無疑問用char,還有一類是基本固定長度但是略有出入的,比如中國人的姓名等,一般長度可能是2~5個漢字,這類資訊也非常適合用char來儲存,只要分配一些略大於通常長度即可。

原則二:資料是否經常更改導致碎片,可能經常變動而產生儲存碎片的小字串一律用char。我們知道char型別的資料是一次性分配儲存空間的,無論以後你怎麼修改,資料始終在該儲存空間內的,不會產生碎片。而varchar則不同,varchar的資料長度是可變的,當修改後的資料大於當前儲存長度時,就會產生碎片,如果該應用是反覆修改資料的應用,那麼久而久之就是產生無數碎片,效率可想而知。

原則三:理解varchar的儲存空間和記憶體空間的區別,合理指定varchar的長度。我們知道varchar的儲存長度是根據字串的長度而定的,但是執行時佔用的記憶體空間卻是按照定義的長度分配記憶體空間的(我的理解,不知道是否正確)。這個現象導致儲存一個字串,比如通訊地址,通暢在100個字元內就能儲存完成,於是varchar(100)是一個合理的選擇,但是由於之前講的,可能有人圖方便使用varchar(500),反正用的儲存空間是一樣的,但是效果確實不一樣的。在記憶體模型中varchar(100)與varchar(500)是兩碼事,後者比前者佔用多5倍的記憶體空間,在臨時表和排序的時候這個差別幾乎可能差一個數量級,於是效率可想而知。

 基本上解釋完char和varchar,這裡順帶看看Nchar和Nvarchar是什麼?

nvarchar表示可變長度 Unicode 資料,其最大長度為 4,000 字元;nchar表示固定長度的 Unicode 資料,最大長度為 4,000 個字元。

那Nchar和Nvarchar在什麼情況下使用呢?我們知道字元 中,英文字元只需要一個位元組儲存就足夠了,但漢字眾多,需要兩個位元組儲存,英文與漢字同時存在時容易造成混亂,Unicode字符集就是為了解決字符集這 種不相容的問題而產生的,它所有的字元都用兩個位元組表示,即英文字元也是用兩個位元組表示。於是支援多語言的站點應考慮使用 Unicode nchar 或 nvarchar 資料型別以儘量減少字元轉換問題。同樣的解釋還有下面我們要研討的NText。

 

下面我們看看text和blob

 text分為4種類型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT,分別對應不同的長度。text是非二進位制字串,並且需要指定字符集,並按照該字符集進行校驗和排序。只能儲存純文字,可以看作是VARCHAR在長度不足時的擴充套件。

blob也分為4種類型:TINYBLOB,BLOB,mediumblob和LongBlob,分別對應不同的長度,blob儲存的是二進位制資料,因此無需字符集校驗,blob除了儲存文字資訊外,由於二進位制儲存格式,所以還可以儲存圖片等資訊,blob可以看作是VARBINARY在長度不足時的擴充套件。

 text和blob的各種型別儲存長度,我們用如下的表格表示:

TinyBlob                             最大長度255個字元(2^8-1)  
TinyText                             最大長度255個字元(2^8-1)
Blob                                    最大長度65535個字元(2^16-1)
Text                                    最大長度65535個字元(2^16-1)
MediumBlob                         最大長度 16777215 個字元(2^24-1)
MediumText                         最大長度 16777215 個字元(2^24-1
LongBlob                              最大長度4294967295個字元 (2^32-1)
LongText                              最大長度4294967295個字元 (2^32-1)

 

 好了,到此char,varchar,text,blob內容探討基本完成了,下面是我再次複習一下有關int型資料的內容,列在這裡方便對比。

 

XML/HTML程式碼

    1. · TINYINT——一個微小的整數,支援 -128到127(SIGNED),0到255(UNSIGNED),需要1個位元組儲存   
    2. · BIT——同TINYINT(1)   
    3. · BOOL——同TINYINT(1)   
    4. · SMALLINT——一個小整數,支援 -32768到32767(SIGNED),0到65535(UNSIGNED),需要2個位元組儲存 MEDIUMINT——一箇中等整數,支援 -8388608到8388607(SIGNED),0到16777215(UNSIGNED),需要3個位元組儲存   
    5. · INT——一個整數,支援 -2147493648到2147493647(SIGNED),0到4294967295(UNSIGNED),需要4個位元組儲存   
    6. · INTEGER——同INT   
    7. · BIGINT——一個大整數,支援 -9223372036854775808到9223372036854775807(SIGNED),0到18446744073709551615(UNSIGNED),需要8個位元組儲存   
    8. · FLOAT(precision)——一個浮點數。precision<=24用於單精度浮點數;precision在25和53之間,用於又精度浮點數。FLOAT(X)與相誚的FLOAT和DOUBLE型別有差相同的範圍,但是沒有定義顯示尺寸和小數位數。在MySQL3.23之前,這不是一個真的浮點值,且總是有兩位小數。MySQL中的所有計算都用雙精度,所以這會帶來一些意想不到的問題。   
    9. · FLOAT——一個小的選單精度浮點數。支援 -3.402823466E+38到-1.175494351E-38,0和1.175494351E-38 to 3.402823466E+38,需要4個位元組儲存。如果是UNSIGNED,正數的範圍保持不變,但負數是不允許的。   
    10. · DOUBLE——一個雙精度浮點數。支援 -1.7976931348623157E+308到-2.2250738585072014E-308,0和2.2250738585072014E-308到1.7976931348623157E+308。如果是FLOAT,UNSIGNED不會改變正數範圍,但負數是不允許的。   
    11. · DOUBLE PRECISION——同DOUBLE   
    12. · REAL——同DOUBLE   
    13. · DECIMAL——將一個數像字串那樣儲存,每個字元佔一個位元組   
    14. · DEC——同DECIMAL   
    15. · NUMERIC——同DECIMAL