MySQL數據庫表設計優化


1.選擇優化的數據類型

       MySQL支持很多種不同的數據類型,並且選擇正確的數據類型對於獲得高性能至關重要。不管選擇何種類型,下面的簡單原則都會有助於做出更好的選擇:

  (1).更小通常更好 

        一般來說,要試着使用正確地存儲和表示數據的最小類型。更小的數據類型通常更快,因爲它們使用了更少的磁盤空間、內存和CPU緩存,而且需要的CPU週期也更少。

       但是要確保不人低估需要保存的值,在架構中的多個地方增加數據類型的範圍是一件極其費力的工作。如果不確實需要什麼數據類型,就選擇你認爲不會超出範圍的最小類型。

 (2).簡單就好

     越簡單的數據類型,需要的CPU週期就越少。例如:比較整數的代價小於比較字符,因爲字符集和排序規則使字符比較更復雜。

  (3).儘量避免空(NULL

        要儘可地把字段定義爲NOT NULL 。即使應用程序無須保存NULL,也有許多表包含了可爲空的列,這僅僅是因爲它爲默認選項,除非真的要保存NULL,否則就把列定義爲NOT NULL

       MySQL難以優化了使用了可空列的查詢,它會使索引、索引統計和值更加複雜。可空列需要更多的存儲空間,還需要在MySQL內部進行特殊處理。當可空列被索引的時候,每條記錄都需要一個額外的字節,還能導致MyISAM中固定大小的索引(例如:一個整數列上的索引)變成可變大小的索引。

        即使要在表中存儲可爲空的字段,也是有辦法不使用NULL的,可以考慮使用0,特殊值或字符串來代替它。

NULL列改爲NOT NULL 帶來的性能提升很小,所以除非確定它引入了問題,否則就不要把它當成優先的優化措施。如果計劃對列進行索引,就要儘量避免把它設置爲可爲空(NULL

 

2.整數

       數字有兩種類型:整數和實數,如果存儲整數,就可以使用這幾種整數類型:tinyint, smallint, mediumint, int, bigint ,它們分別需要816243264位存儲空間。

       整數類型有可選的unsigned(無符號)屬性,它表示不允許爲負數,並大致把正上限提高了一倍,例如:tinyint unsigned保存的翻圍爲0255,而不是-127128

        Signed(有符號)unsigned(無符號)類型佔用的存儲空間是一樣的,性能也一樣。因此可以根據實際情況採用合適的類型。

你的選擇將會決定MySQL把數據保存在內存中還是磁盤上,然而,整數運算通常使用64位的bingint整數。

        MySQL還允許你對整數類型定義寬度,比如int(11)。這對於大在多數應用程序是沒有意義的,它不限制值的範圍,只規定了mysql的交互工具(例如命令客戶端)用來顯示字符的個數。對於存儲計算,int(1)int(20)是一樣的。

 

3.實數

      實數有分數部分,然而,它們並不僅僅是分數。可以使用decimal保存比出bigint還大的整數。MySQL同時支持精確與非精確類型。

Floatdouble類型支持使用標準的浮點運算進行近似計算。如果想知道浮點運算到底如何進行,則要研究生平臺浮點數的具體實現。

比較起decimal類型,浮點類型保存同樣大小的值使用的空間通常更小,float類型佔用4個字節,double佔用8個字節,而且精度更大,範圍更廣。和整數一樣,你選擇的僅僅是存儲類型。mysql在內部對浮點類型使用double進行計算。

由於需要額外的空間和計算開銷,只有在需要對小數進行精確的時候才使用decimal,比如保存金融數據。

 

4.字符串類型

      Varcharchar類型

      varchar:保存了可變長度的字符串,是使用得最多的字符串類型,它能比固定類型佔用更少的存儲空間,因爲它只佔用了自已需要的空間(也就是說較短的值佔用的空間更小)。它使用額外的1-2個字節來存儲值的長度Varchar能節約空間,所以對性能有幫助。然而,由於行的長度是可變的,它們在更新的時候可能會發生變化,這會引起額外的工作。當最大長度遠大於平均長度,並且很少發生更新的時候,通常適合用varchar。這時候碎片就不會成爲問題,還有你使用複雜的字符集,如utf-8時,它的每個字符都可能會佔用不同的存儲空間。Varchar存取值時候,MySQL不會去掉字符串末尾的空格。

       char:固定長度,char存取值時候,MySQL會去掉末尾的空格。Char在存儲很短的字符串或長度近似相同的字符的時候很有用。例如,char適用於存儲密碼的MD5哈希值,它的長度總是一樣的。對於經常改變的值,char也好於varchar,因爲固定長度的行不容易產生碎片,對於很短的列,char的效率也高於varcharChar(1)字符串對於單字節字符集只會佔用1個字節,而varchar(1)則會佔用2個字節,因爲有一個字節用來存儲其長度。

         Charvarchar的兄弟類型爲binaryvarbinary,它們用於保存二進制的字符串,二進制字符串的傳統的字符串很類似,但是它們保存的是字節而不是字符。填充也有所不同,MySQL使用\00字節)填充binary值,而不是空格,並且不會在獲取數據的時候把填充的值截掉。

         使用varchar(5)varchar(200)保存“hello”佔用的空間是一樣的,但是使用較短的列有很大的優勢,較大的列會使用更多的內存,因爲MySQL通常會分配固定大小的內存塊來保存值。這對排序或使用基於內存的臨時表尤其不好。同樣的事情也會發生在使用文件排序或基於磁盤的臨時表的時候。

 

5.BLOBTEXT類型

    BLOBTEXT分別用二進制和字符形式保存大量數據。

    事實在,它們各有自的數據類型家族:字符類型有tinytext, smalltext, text, mediumtextlongtext, 二進制類型有tinyblob, smallblob, blob, medicmblob, longblobBLOB 等同於smallblob, TEXT等同於smalltext

        和其它類型不同,MySQLblob, text當成有實體的對象來處理,存儲引擎通常會特別地保存它們。InnoDB在它們較大的時候會使用單獨的“外部”存儲來進行保存,每個值在行裏面都需要1-4字節,並且還需要足夠的外部存儲空間來保存實際的值。

        BLOBTEXT唯一的區別就是BLOB保存的是二進制數據,沒有字符集和排序規則,TEXT保存的是字符數據,有字符集和排序規則。

       MySQLBLOBTEXT列的排序方式和其它類型不同,它不會按照字符串的長度進行排序,而只是按照max_sort_length規定的前若干個字節進行排序,如果只按照開始的幾個字符排序,就可以減少max_sort_length的值或使用ORDER BY SUBSTRING(column, length)MySQL不能索引這些數據類型的完整長度,也不能爲排序而使用索引。

 

6.使用ENUM代替固定字符串類型

       ENUM列可以存儲65535個不同的字符串,MySQL以非常緊湊的方式保存了它們,根據列表中值的數量,MySQL會把它們壓縮到1-2個字節中,MySQL在內部會把每個值都保存爲整數,以表示值在列表中的位置,並且還保留了一份“查找表”來表示整數和字符串在表的.frm文件中的映射關係。

        Enum最不好的一面是字符串是固定的,如果需要添加或者刪除字符串必須使用ALTER TABLE,因此,對於一系列未知可能會改變的字符串,使用enum就不是一個好主意,MySQL在內部的權限表中使用enum來保存Y值和N值。

       由於MySQL把每個值保存爲整數,並且須進行查找才能把它轉換成字符串形式,所以enum有一些開銷。這通常可以由它們較小的大小進行彌補,但不總是這樣,在特定情況下,把charvarchar列和enum列進行聯接,可能會比聯接另一個charavarchar列慢。

 

7.日期和時間類型

         MySQL可以使用多種類型來保存各種日期和時間值,比中yeardateMySQL能存儲的最細的時間粒度是秒,然而,它可以用毫秒的粒度進行暫時的運算。

        MySQL提供兩種相似的數據類型:DATETIME 和 TIMESTAMP,對於很多應用程序,它們都能正常工作,但是在某些情況下,一種會好於另外一種。

        DATETIME:能夠保存大範圍的值,從1001年到9999年,精度爲秒,它把日期和時間封裝到一個格式爲yyyyMMddHHmmss的整數當中,與時區無關。它使用了8個字節存儲空間。

        TIMESTAMP:保持了自197011日午夜(格林尼治標準時間)以來的秒數,它和Unix的時間戳相同。它只使用了4個字節存儲空間。因此它比DATETIME的範圍小得多。它表示自能從1970年到2038年。MySQL提供了FROM_UNIXTIME()函數把Unix時間戳轉換爲日期,並提供UNIX_TIMESTAMP()函數把日期轉換爲Unix時間戳。

         TIMESTAMP顯示的值依賴於時區,MySQL服務器、操作系統及客戶端連接都有時區設置。因此,保存0值的TIMESTAMP實際顯示的時間是美國東部的時間1969-12-31 19:00:00,與格林尼治標準時間(GMT)相差5小時。

        TIMESTAMP也有DATETIME沒有的特殊性質,在默認情況下,如果插入的行沒有定義TIMESTAMP列的值,MySQL就會把它設置爲當前時間。在更新的時候,如果沒有顯示地定義TIMESTAMP列的值,MySQL也會自動更新它。可以配置TIMESTAMP列的插入和更新行爲。最後,TIMESTAMP默認是NOT NULL,這也和其它的數據類型不一樣!

 

8.選擇標識符

         爲標識列選擇好的數據類型非常重要,你可能會更多地用它們和其他列做比較,還可能把它們用作其它表的外鍵,因爲選擇標識符列選擇數據類型的時候,你也可能是在爲相關的表選擇數據類型。

        當爲標識符列選擇數據類型的時候,不僅要考慮存儲類型,還要考慮MySQL如何對它們進行計算和比較。例如:mysql會在內部把enumset類型保存爲整數,但是在比較的時候把它們轉換爲字符串。

       一旦選擇了數據類型,要確保在相關表中使用同樣的類型。類型之前要精確匹配,包括諸如unsigned這樣的屬性。混合不同的數據類型會導致性能問題,即使沒有性能問題,隱式的類型轉換也能導致難以察覺的錯誤,在你已經忘記了自己是在對不同類型做比較的時候,這些錯誤就會突然出現。

        選擇最小的數據類型能表明所需值的範圍,並且爲將來留出增長的空間。例如,如果用porvince_id來表示中國的省份,那麼我們知道它不會產成千上萬個值,因類就沒有必要使用int,用tinyint就足夠了,它比int3個節字,如果把一個表的主鍵是tinyint,而另一個表以int作爲外鍵,那麼就會造成較大的性能差距。

         整數通常是標識符的最佳選擇,因爲它速度快,並且能使用auto_increment

        Enumset通常不合適用作標識符,儘管它適合用來做靜態的,包含了狀態和“類型”和值的“定義表”。

        Enumset列適合用來性別、國家、省份這些固定不變的信息。

        要儘可能的避免使用字符串來做標識符,因爲它們佔用了很多空間並且通常比整數類型要慢,特別注意不要在myisam表上使用字符串標識符。myisam默認情況下爲字符串使用了壓縮索引,這使查找更爲緩慢。

         MyISAM使用前綴壓縮來減小索引大小,默認情況下會壓縮字符串,也可以壓縮整數

可以使用create table時用PACK_KEYS控制索引壓縮的方式。

        PACK_KEYSMySQL手冊中如下描述:

        如果您希望索引更小,則把此選項設置爲1。這樣做通常使更新速度變慢,同時閱讀速度加快。把選項設置爲0可以取消所有的關鍵字壓縮。把此選項設置爲DEFAULT時,存儲引擎只壓縮長的CHARVARCHAR列(僅限於MyISAM)。

如果您不使用PACK_KEYS,則默認操作是隻壓縮字符串,但不壓縮數字。如果您使用PACK_KEYS=1,則對數字也進行壓縮。

 

9.特殊類型的數據

       一些數據類型沒有直接對應的內建數據類型,精度低於秒的時間戳就是一個例子,另一個例子就是IP地址,人們通常使用varchar(15)來保存IP地址。但是,IP地址實際上是無符號的32位整數,而不是字符串。使用小數點來進行分純粹是爲了增加它的可讀性。在實際使用時應用用無符號整數來存儲IP地址。MySQL提供了INET_ATON()INET_NTOA()函數在IP地址和整數之前轉換。


  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章