儲存引擎與資料型態

1 表格與儲存引擎

表格(table)是資料庫中用來儲存紀錄的基本單位,在建立一個新的資料庫以後,你必須為這個資料庫建立一些儲存資料的表格:

每一個資料庫都會使用一個資料夾,這些資料庫資料夾用來儲存所有資料庫各自需要的檔案:

「Storage engine、儲存引擎」是MySQL用來儲存資料的技術,為了資料庫多樣化的應用,你可以在建立表格的時候,依照自己的需求指定一種儲存引擎,不同的儲存引擎會有不同的資料儲存方式與運作的特色。MySQL提供許多儲存引擎讓你選擇,下列是主要的三種儲存引擎的簡介:

  • MyISAM: MySQL預設的儲存引擎,雖然它支援的功能並沒有像一般的資料庫那麼多(例如交易、transaction);不過也因為它比較簡單,所以運作的效率相對也比較好

  • InnoDB: 這種儲存引擎所提供的功能已經跟大型的商用資料庫軟體一樣了,像是交易(transaction)、紀錄鎖定(row-level locking) 與自動回復(auto-recovery)。

  • MEMORY: 這是一個比較特殊的儲存引擎,它把資料儲存在紀憶體中,所以運作的效率是最快的;不過只要MySQL伺服器關閉後,儲存的資料就全部不見了。

1.1 MyISAM

「MyISAM」是MySQL預設的儲存引擎,「預設」的意思是如果你在建立表格的時候沒有指定一種儲存引擎,MySQL會幫你建立的新表格指定為「MyISAM」儲存引擎。以下列一個使用MyISAM儲存引擎的資料庫來說,在資料庫資料夾中的檔案會像這樣:

當你建立一個表格以後,「MyISAM」儲存引擎會建立以表格名稱為檔案名稱的三個檔案,以「city」表格來說:

使用「MyISAM」儲存引擎的資料庫具有「可攜性、portable」的特色,你可以很容易的把一個資料庫複製到另外一台電腦的MySQL伺服器中:

註: 使用「MyISAM」儲存引擎時,MySQL並不會限制一個資料庫中可以包含的表格數量。不過一個表格會在檔案系統中建立三個檔案,如果超過作業系統對於檔案數量或容量的限制,你就不能再建立任何新的表格。

1.2 InnoDB

MySQL資料庫伺服器從3.23.49版本開始把「InnoDB」儲存引擎列為正式支援的功能,所以從這個版本開始,MySQL也提供與大型商用資料庫軟體一樣的功能。最主要的功能是支援「交易、transaction」,在比較複雜的資料庫應用系統中,很常遇到這樣的情況:

在順利的情況下,當然不會有任何問題。可是如果發生下列的情況:

這樣的情況是一定要避免,否則資料庫中儲存的資料就會出現很大的問題了。所以一般的大型商用資料庫都會使用交易的功能來處理這樣的情況:

「InnoDB」儲存引擎除了提供許多功能外,與「MyISAM」儲存引擎最大的差異是檔案的儲存方式:

「InnoDB」儲存引擎實際儲存在檔案系統中的檔案會像這樣:

註: 因為使用「InnoDB」儲存引擎的表格會使用同一個儲存空間,所以不同資料庫的表格資料也會儲存在一起。「InnoDB」儲存引擎限制在這個共用的儲存空間中不能超過兩百萬個表格。

1.3 MEMORY

「MEMORY」儲存引擎與其它儲存引擎有一個主要的差異,就是它會把紀錄與索引資料儲存在記憶體中。所以使用「MEMORY」儲存引擎的表格,不論在查詢或維護資料時的效率都是很好的。在檔案系統中儲存的檔案只有「frm」檔,也就是儲存表格結構資訊的檔案:

註: 因為「MEMORY」儲存引擎會把紀錄與索引資料儲存在記憶體中,所以只要MySQL伺服器關閉、重新啟動、當機,所有使用「MEMORY」儲存引擎的表格資料都會全部消失,只剩下表格結構;它也不適合儲存大量資料的表格,會耗用太多記憶體

1.4 儲存引擎與作業系統

雖然MySQL資料庫是一個獨立運作的軟體,不過它還是得安裝在某一個作業系統中,例如Windows或Linux。而由作業系統控制的檔案系統可能會有許多限制,例如檔案的數量和檔案的大小。如果MySQL資料庫軟體在建立或使用資料庫檔案的時候,超過作業系統的限制,就會發生錯誤。

如果以支援的功能來決定儲存引擎的話,那就會比較明確。如果要以作業系統的限制來決定儲存引擎的話,你可以參考下列的作法:

  • 使用「MyISAM」儲存引擎可以避免違反檔案大小的限制

  • 使用「InnoDB」儲存引擎可以避免違反檔案數量的限制

  • 如果在檔案數量與大小的限制都遇到問題的話,你只好增加硬體和修改作業系統在檔案系統上的設定

2 欄位資料型態

在建立表格時,你會幫每一個欄位指定適合的「資料型態、data type」。正確的選擇欄位資料型態,除了可以幫你儲存正確的資料外,還可以讓資料庫使用最少的記憶體與儲存空間,這樣會讓資料庫運作的效率更好一些。資料型態主要分為下列三大類:

  • 數值: 任何包含正、負號的整數與小數資料;另外還有位元(bit)的數值資料,它使用二進位來表示一個數字。

  • 字串: 包含non-binary與binary兩種字串值,non-binary字串值是一些使用字元集與collation的字元(character)組合起來的;binary字串值是一些位元組(bytes)組合的資料。

  • 日期與時間: 包含日期、時間與日期加時間。

2.1 數值

數值資料分為整數與小數資料,下列是MySQL提供的整數型態:

型態 Byte(s) 預設長度 有號數範圍 無號數範圍
TINYINT[(長度)] 1 4 -128~127 0~255
SMALLINT[(長度)] 2 6 -32768~32767 0~65535
MEDIUMINT[(長度)] 3 9 -8388608~8388607 0~16777215
INT[(長度)] 4 11 -2147683648~2147683647 0~4294967295
BIGINT[(長度)] 8 20 -9223372036854775808~9223372036854775807 0~18446744073709551615

整數型態的意思就是它們不能儲存小數,在建立表格的時候,如果需要一個可以儲存整數資料的欄位,你可以依照整數資料的大小需求,選擇一個夠用又不會太浪費空間的整數形態。以下列的「cmdev.integertable」表格來說:

欄位名稱 型態 範圍
n TINYINT(4) -128~127
n2 SMALLINT(6) -32768~32767
n3 MEDIUMINT(9) -8388608~8388607
n4 INT(11) -2147683648~2147683647
n5 BIGINT(20) -9223372036854775808~9223372036854775807

整數型態的後面會在左右刮號中指定一個數字,以「SMALLING」型態來說:

當你在執行資料的新增或修改的時候,就要特別注意它們的可以儲存數字的範圍:

整數型態的欄位,就表示它們不可以儲存小數的數值:

數值型態還有下列幾種可以儲存小數資料的浮點數型態:

型態 Byte(s) 預設長度 最大長度 說明
FLOAT[(長度,小數位數)] 4 註1 255, 30 單精確度浮點數(近似值)
DOUBLE[(長度,小數位數)] 8 255, 30 雙精確度浮點數(近似值)
DECIMAL[(長度[,小數位數])] 註2 10, 0 65, 30 自行指定位數的精確值

註1: FLOAT與DOUBLE的預設長度會因為不同的作業系統而有不一樣的長度

註2: 依照指定的位數決定實際儲存的空間

「FLOAT」和「DOUBLE」型態的欄位可以用來儲存包含小數的數值,儲存空間分別是4和8個位元組,它們是一種佔用儲存空間比較小,執行運算比較快的型態。不過因為它們是使用「近似值」來儲存你的數值,所以如果你需要儲存完全精準的數值,就不能使用這兩種型態。

另外一種可以儲存小數數值的「DECIMAL」型態就可以用來儲存完全精準的數值,儲存在這個型態中的數值,不論是查詢或是運算,都不會有任何誤差,不過「DECIMAL」型態佔用的儲存空間就比「FLOAT」和「DOUBLE」型態大。「DECIMAL」型態在MySQL還有一個一樣的關鍵字是「NUMERIC」,這兩種型態完全一樣。

在MySQL中,「FLOAT」、「DOUBLE」和「DECIMAL」都可以依照自己的需要設定長度與位數:

在設定長度與小數位數的時候,要注意下列幾個規則:

  • 不可以超過最大長度

  • 小數位數不可以超過長度

  • 長度與小數位數一樣的時候,表示只可以儲存小數,例如「0.123」

MySQL的數值型態,包含整數與浮點數都可以設定為「只能儲存正數」,以下列的「cmdev.numerictable」表格來說:

欄位名稱 型態
i TINYINT(3) UNSIGNED
i2 SMALLINT(5) UNSIGNED
i3 MEDIUMINT(8) UNSIGNED
i4 INT(10)
i5 BIGINT(20) UNSIGNED
f FLOAT UNSIGNED
f2 DOUBLE
f3 DECIMAL(10, 0) UNSIGNED

設定為只能儲存正數的欄位,就跟字面上的效果一樣,任何希望儲存負數的動作都會造成錯誤:

MySQL的數值型態都可以依照自己的需要設定長度,以下列的「cmdev.numerictable2」表格來說:

欄位名稱 型態
i TINYINT(3)
i2 SMALLINT(3)
i3 MEDIUMINT(3)
i4 INT(3)
i5 BIGINT(3)
f FLOAT(5, 2)
f2 DOUBLE(5, 2)
f3 DECIMAL(5, 2)

同樣為數值型態設定長度,在整數和浮點數會有不一樣的效果。如果你為整數型態的欄位設定長度的話,這個長度只是設定顯示的長度而已,並不會影響實際儲存的長度:

為浮點數型態設定長度與小數位數的時候,效果就跟整數型態不一樣了:

不過在整數位數的部份,就一定會依照設定來儲存,否則會造成錯誤:

MySQL的在數值型態的設定上,還有一個比較特別的設定,就是「ZEROFILL」,以下列的「cmdev.numerictable3」表格來說:

欄位名稱 型態
i TINYINT(3) UNSIGNED ZEROFILL
i2 SMALLINT(4) UNSIGNED ZEROFILL
i3 MEDIUMINT(5) UNSIGNED ZEROFILL
i4 INT(6) UNSIGNED ZEROFILL
i5 BIGINT(7) UNSIGNED ZEROFILL
f FLOAT(5, 2) UNSIGNED ZEROFILL
f2 DOUBLE(7, 3) UNSIGNED ZEROFILL
f3 DECIMAL(9, 5) UNSIGNED ZEROFILL

「ZEROFILL」的設定表示在查詢這些欄位的時候,回傳的資料會在左側根據長度的設定填滿「0」:

註: 「ZEROFILL」一定要跟「UNSIGNED」一起使用,就算你只有為欄位設定「ZEROFILL」,MySQL也會自動加入「UNSIGNED」的設定。

整數型態的部份,在補0的處理上會不太一樣:

2.2 位元

「位元、BIT」型態其實也是用來儲存數值用的,不過它是以二進位的型式儲存資料,也就是只有0跟1兩種資料。MySQL的在數值型態的設定上,還有一個比較特別的設定,就是「ZEROFILL」,以下列的「cmdev.numerictable3」表格來說:

欄位名稱 型態 數字範圍
n BIT 0~1
n2 BIT(8) 0~255
n3 BIT(64) 0~18446744073709551615

你可以直接儲存數字到位元型態的欄位;也可以指定一個使用二進位表示的值:

2.3 字串

MySQL把字串型態分為兩大類: 「非二進位制、non-binary」與「二進位制、binary」。非二進位制就是儲存一般文字的字串,會有特定的字元集與collation;二進位制使用位元組儲存資料,不包含字元集與collation,所以大多用來儲存圖片或音樂這類資料。「非二進位制、non-binary」的字串型態有下列幾種:

型態 最大長度 實際儲存的空間 說明
CHAR[(長度)] 255 指定的長度 固定長度的字串,預設長度為1
VARCHAR(長度) 65535 字元個數加1或2bytes 變動長度的字串
TINYTEXT 255 字元個數加1byte TEXT
65535 字元個數加2bytes MEDIUMTEXT 16,772,215
字元個數加3bytes LONGTEXT 4,294,967,295 字元個數加4bytes

固定長度與變動長度的兩種字串型態都可以儲存字串,差異在儲存的文字個數小於型態指定的長度時,變動長度實際儲存的空間會小一些,以下列的「cmdev.nonbinarytable」表格來說:

欄位名稱 型態
s CHAR(10)
s2 VARCHAR(10)

同樣把長度設定為10的「CHAR」與「VARCHAR」字串型態,它們在儲存字串資料的時候會不太一樣:

「非二進位制、non-binary」的字串都會包含特定的字元集與collation,所以可以用來儲存各種不同國家的文字。不同的字元集會佔用不同的儲存空間,以下列的「cmdev.nonbinarytable2」表格來說:

欄位名稱 型態 字元集
s VARCHAR(6) latin1
s2 VARCHAR(6) big5
s3 VARCHAR(6) utf8

上列的表格中,三個欄位分別設定為「latin1」、「big5」與「utf8」字元集,你可以查詢MySQL資料庫支援的字元集特性,「MAXLEN」欄位是關於儲存空間的資訊:

使用在「LENGTH」函式來查詢儲存在這個表格中的字串資料,就可以很明顯的看出不同的字元集,在儲存字元時使用的儲存空間:

「LENGTH」函式會傳回字串資料實際的儲存長度(byte);如果你要查詢字串的字元數量的話,就要使用「CHAR_LENGTH」函式:

字元集會影響字串的儲存空間,collation會影響字串排列順序。以下列的「cmdev.nonbinarytable3」表格來說:

欄位名稱 型態 字元集 Collation
s VARCHAR(6) latin1 latin1_general_ci
s2 VARCHAR(6) latin1 latin1_general_cs

上列表格中欄位的字元集都指定為「latin1」,不過「s」欄位的collation設定為「latin1_general_ci」,表示排序時不區分大小寫;「s2」欄位設定為「latin1_general_cs」,表示排序時會區分大小寫。以下列儲存在這個表格中紀錄來說:

Collation設定中的「latin1_general_ci」,最後的「ci」表示「case insensitive」,是不分大小寫的意思。在這樣的設定下,MySQL會把字串「ABC」和「abc」當成是一樣的;「latin1_general_cs」,最後的「cs」表示「case sensitive」,是區分大小寫的意思。在這樣的設定下,MySQL就會把字串「ABC」和「abc」當成是不一樣的字串。

是否區分大小寫的collation設定會影響排序的結果:

另外一個影響是條件的判斷:

「二進位制、binary」的字串型態是使用位元組(byte)為單位來儲存字串資料,跟非二進位制的字串類似,它也提供許多應用在不同長度的型態:

型態 最大長度(byte) 實際儲存的空間(byte) 說明
BINARY[(長度)] 255 指定的長度 固定長度的字串,預設長度為1
VARBINARY(長度) 65535 長度加1或2bytes 變動長度的字串
TINYBLOB 255 byte數加1byte BLOB
65535 byte數加2bytes MEDIUMBLOB 16,772,215
byte數加3bytes LONGBLOB 4,294,967,295 byte數加4bytes

「BINARY」與「VARBINARY」兩種型態的差異,與「CHAR」和「VARCHAR」的差異一樣。在一般的情況下,使用「VARBINARY」會比「BINARY」節省一點儲存空間。你也可以使用「二進位制、binary」型態儲存文字資料,只不過MySQL都是以位元組來儲存所有的資料,也就是0到255的數字:

所有「二進位制、binary」的字串型態都不可以指定字元集與collation,不過你可以使用它們來儲存任何語言的文字,也可以儲存類似音樂或圖片資料,因為MySQL都是一個一個byte的把資料儲存到資料庫中;所以在執行查詢時的排序和條件設定,都是以使用位元組為單位來判斷。

2.4 列舉與集合

列舉(ENUM)與集合(SET)是一種特殊的「非二進位制、non-binary」字串型態,所以它們也可以指定字元集與collation。下列是這兩種型態的說明:

型態 最大個數 儲存空間 說明
ENUM(字串值[,...]) 65535 1byte(255個)2bytes(256到65535個) 包含一組合法的字串值(單一值)
SET(字串值[,...]) 64 1byte(8個)2bytes(16個)3bytes(24個)4bytes(32個)8bytes(64個) 包含一組合法的字串值(多個值)

列舉(enumeration)的資料在資料庫中的應用很常見,例如服裝的大小就會以S、M與L來表示小、中與大。你可以使用字串來儲存這類資料,不過這類的資料也很適合使用「ENUM」型態來儲存。以下列的「cmdev.enumtable」表格來說:

在儲存資料的時候,「ENUM」型態看起來似乎與「VARCHAR」完全一樣:

可是列舉型態在資料的正確性方面,就會比單純的字串型態好多了。例如下列錯誤示範:

列舉型態欄位除了可以直接使用字串值來新增與更新資料外,還可以使用數值資料的編號來代替,任何一個列舉型態中的成員,MySQL都會幫它們編一個號碼:

瞭解列舉型態中成員的編號以後,你可以選擇字串值或數值來管理列舉型態欄位儲存的資料:

雖然在查詢列舉型態欄位資料的時候,所得到的結果都是成員的字串值;不過真正儲存在資料庫中的資料卻是成員的編號,所以指定列舉型態欄位為排序欄位的時候,資料庫會使用編號來排序,而不是以成員的字串值:

在指定列舉型態欄位的查詢條件時,可以使用成員的字串值或編號:

集合(SET)型態同樣可以設定一組成員,不過它可以儲存多個成員資料。例如星期的成員總共有七個,而需要工作的星期就會有一個以上了,類似這樣的需求就應該使用集合型態。以下列的「cmdev.settable」表格來說:

欄位名稱 型態
workingday SET(‘MON’,'TUE’,'WED’,'THU’,'FRI’,'SAT’,'SUN’)

你可以使用一個字串值來管理集合型態欄位,在這個字串值中,使用逗號來隔開不同的成員字串:

集合型態欄位與列舉型態欄位同樣具有檢查資料是否正確的能力:

列舉型態欄位的成員編號使用簡單的連續數字;集合型態欄位會比較複雜一些:

瞭解集合型態欄位的成員所代表的數字後,你就可以使用數值來管理儲存的資料:

要使用數值來代表多個成員的時候,你只要把所有成員的數字加總起來就可以了:

列舉與集合型態都可以設定需要的字元集與collation,以下列的「cmdev.estable」表格來說:

欄位名稱 型態 字元集 Collation
enumsize enum(‘XS’, …) latin1 latin1_general_ci
enumsize2 enum(‘XS’, …) latin1 latin1_general_cs
workingday set(‘MON’, …) latin1 latin1_general_ci
workingday2 set(‘MON’, …) latin1 latin1_general_cs

字元集的設定可以決定可以儲存字串資料的編碼,而collation的設定會決定字串值是否區分大小寫:

如果指定字串值的時候違反collation設定的大小寫規則,就會發生錯誤:

2.5 日期與時間

MySQL提供下列幾個可以儲存日期與時間資料的欄位型態:

型態 Byte(s) 說明 範圍
DATE 3 日期 ’1000-01-01′~’9999-12-31′
TIME 3 時間 ‘-838:59:59′~’838:59:59′
DATETIME 8 日期與時間 ’1000-01-01 00:00:00′~’9999-12-31 23:59:59′
YEAR[(4\ 2)] 1 西元年 1901~2155[YEAR(4)]1970~2069[YEAR(2)]
TIMESTAMP 4 日期與時間 ’1970-01-01 00:00:00′~2037

日期(DATE)型態欄位可以儲存年、月、日的資料,範圍從「1000-01-01」到「9999-12-31」,你的日期資料不可以超過「9999-12-31」,可是你可以儲存「1000-01-01」以前的日期,不過MySQL建議你最好不要這麼作,不然可能會造成一些奇怪的問題。

因為日期中的西元年份可以使用四個或兩個數字,使用兩個數字的時候,「70」到「99」表示「1970」到「1999」;如果是「00」到「69」就是「2000」到「2069」。所以要注意下列的情況:

另一個日期資料會變成這樣:

時間(TIME)型態可以儲存時、分、秒的資料,範圍從「-838:59:59」到「838:59:59」。這個儲存時間資料的範圍可能會跟你想的不太一樣。一般來說,時間資料指的是從「00:00:00」到「23:59:59」,也就是一天的時間。MySQL的時間型態欄位可以讓你儲存類似「經過的時間」這樣的資料:

在指定一個時間資料的時候,你可以省略秒或分,省略的部份,MySQL都會幫你設定為「0」:

日期與時間(DATETIME)型態可以儲存完整的年、月、日與時、分、秒資料,範圍從「1000-01-01 00:00:00」到「9999-12-31 23:59:59」。在表示一個日期與時間資料的時候,日期與時間之間,至少要使用一個空白隔開。時間部份的時、分、秒都可以省略,省略的部份,MySQL都會幫你設定為「0」:

如果只需要儲存年份資料的話,你可以使用西元年(YEAR)型態,這樣會節省很多儲存空間。你可以視需要把西元年型態設定為兩位或四位數字,四位數字可以儲存的範圍從「1901」到「2155」;兩位數字的範圍從「00」到「99」,實際的西元年份是「1970」到「2069」。

在指定一個年份資料給西元年型態欄位的時候,可以使用字串值或數值來表示西元年份,不同個數的資料會有不同的儲存效果:

如果西元年型態欄位的值是「0」的話,MySQL會把它當成是一個不正確的西元年資料,所以你應該不會指定這樣的資料,不過指定不同的資料也會有不同的儲存結果:

「TIMESTAMP」型態的格式與「DATETIME」一樣,都包含完整的年、月、日與時、分、秒資料,不過它使用的儲存空間只有4bytes,是「DATETIME」型態的一半。

「TIMESTAMP」也是MySQL日期與時間型態中具有「時區」特性的型態。它可以儲存從「1970-01-01 00:00:00」到目前經過的秒數。這個起始日期與時間使用「Coordinated Universal Time、UTC」世界標準時間為儲存資料的依據,它與「Greenwich Mean Time、GMT」格林威治標準時間是一樣的。

全世界分為許多不同時區(time zone),所有時區都使用跟標準時間的差異來當作自己的標準時間。以台灣來說,你會在安裝Windows平台的電腦中,經由控制台裡的日期和時間,看到這個關於時區的設定:

MySQL資料庫採用與作業系統同樣的時區設定,所以在儲存「TIMESTAMP」型態欄位的資料時,過程中會有一些計算的動作:

而查詢「TIMESTAMP」型態欄位資料的時候,也會有這樣的情況:

瞭解時區設定與「TIMESTAMP」型態的關係後,你就可以知道下列的動作為什麼會發生錯誤了:

你可以使用查詢敘述取得MySQL資料庫伺服器關於時區的設定:

如果想要設定其它的時區,可以使用「+時時:分分」或「-時時:分分」的格式。例如日本東京時區比格林威治標準時間晚九小時,你可以設定為「+09:00」;而美國舊金山比格林威治標準時間早七小時,可以設定為「-07:00」:

設定新的時區以後,使用下列的範例測試「DATETIME」和「TIMESTAMP」兩種型態,可以看出在儲存日期時間資料上的差異:

因為「TIMESTAMP」型態儲存的是格林威治標準時間,所以在修改時區後,查詢得到的日期時間資料就會有差異了:

results matching ""

    No results matching ""