MySQL選擇合適的資料型別及優化Schema設計

2021-08-17 06:34:08 字數 4496 閱讀 1485

「良好的邏輯設計和物理設計是高效能的基石,應該根據將要執行的查詢語句來設計schema,這往往需要權衡各種因素」。

mysql支援的資料型別非常多,選擇正確的資料型別對於獲得高效能至關重要。

在為列選擇資料型別時,第一步需要確定合適的大型別:數字、字串、時間等。第二步是選擇具體型別,很多資料型別可以儲存相同型別的資料,但是儲存的長度和範圍不一樣、允許的精度不同。

整數型別

tinyint、smaillint、mediumint、int、bigint,分別使用8、16、24、32、64位儲存空間,可以儲存的值的範圍-2(n-1)~2(n-1) -1(n代表位數)。

整數型別有可選的unsigned屬性,表示不允許負值,例如tinyint unsigned的儲存範圍是0~255,而tinyint的儲存範圍是-128~127。

可以為整數型別指定寬度,例如int(11),對大多數應用這是沒有意義的,它不會限制值的合法範圍,只是規定了mysql的一些互動工具用來顯示字元的個數,對於儲存和計算來說,int(1)和int(20)是相同的。

實數型別

實數是帶有小數部分的數字。float(4個位元組)和double(8個位元組)型別支援使用標準的浮點運算進行近似計算。decimal型別用於儲存精確的小數,支援精確計算(mysql自己實現了decimal的高精度計算,cpu直接支援原生浮點計算,所以浮點計算更快)。

浮點和decimal型別都可以指定精度。對於decimal可以指定小數點前後所允許的最大位數,將數字打包儲存到乙個二進位制字串中(每4個位元組存9個數字)。例如,decimal(18,9)小數點兩邊各儲存9個數字,一共佔9個位元組:小數點前的數字用4個位元組,小數點後的數字用4個位元組,小數點本身用1個位元組。

因為需要額外的空間和計算開銷,所以應該盡量只在對小數進行精確計算時才使用decimal,例如儲存財務資料。但是資料量比較大的時候,可以考慮使用bigint代替,將需要儲存的貨幣單位根據小數的位數乘以相應的倍數即可。

字串型別

varchar型別用於儲存可變長字串,需要使用1或2個額外位元組記錄字串的長度,最大長度不能超過65535位元組。當字串列的最大長度比平均長度大很多;列的更新很少,所以碎片不是問題;使用了像utf-8這樣複雜的字符集,每個字元都使用不同的位元組數進行儲存適合使用varchar。

char型別是定長的,適合儲存很短的字串,或者所有值都接近同乙個長度。例如,char非常適合儲存密碼的md5值,因為這是乙個定長的值。對於經常變更的資料,char也比varchar更好,因為定長的char不容易產生碎片,對於非常短的列,char比varchar在儲存空間上也更有效率,例如用char(1)來儲存只有y和n的值,如果採用單位元組字符集只需要乙個位元組,但是varchar(1)取需要兩個位元組,因為還有乙個記錄長度的額外位元組。

注:使用varchar(5)和varchar(200)儲存」hello」的空間開銷是一樣的,但是更長的列會消耗更多的記憶體,因為可變長字串,其在臨時表和排序時可能導致悲觀的按最大長度分配記憶體。所以最好的策略是只分配真正需要的空間

blob和text型別

為儲存很大的資料而設計的可變長字串資料型別,分別採用二進位制和字元方式儲存。字元型別是tinytext、smalltext(text)、mediumtext、longtext,對應的二進位制型別是tinyblob、smallblob(blob)、meduimblob、longblob。通過最大字元數來限制,text的最大長度為65535(216-1)個字元。

日期和時間型別

datetime**從2023年到2023年,精度為秒。把日期和時間封裝到格式為yyyymmddhhmmss的整數中,與時區無關,使用**8個位元組的儲存空間。例如,」2018-01-01 08:00:00」。

timestamp儲存了從格林威治時間2023年01月01日00時00分00秒起至現在的秒數,只使用4個位元組的儲存空間,只能表示從2023年到2023年

如果在多個時區儲存或訪問資料,timestamp和datetime的行為將很不一樣,前者提供的值與時區有關,後者則保留文字表示的日期和時間。timestamp可以在插入時自動生成當前時間戳,以及在修改時根據當前時間戳自動更新。

除了特殊行為外,通常應該盡量使用timestamp,因為它比datetime空間效率更高。用整數儲存時間戳的格式通常不方便處理,不推薦這樣做。

bit、set、enum

bit的最大長度是64位。在myisam中,bit的儲存空間很小,是真正的實現了通過bit來儲存,但是在其他的一些儲存引擎中就不一樣了,因為它們是轉換為最小的int型別儲存的,所以占用的空間也沒有節省,還不如直接使用int類的資料型別存放來得直觀。

對於set和enum型別,主要內容基本處於較少變化狀態且值比較少的字段。雖然這兩個欄位所占用的儲存空間都較少,但是由於在使用方面較其他的資料型別要略為複雜一些,所以在實際環境中一般使用還是較少。

選擇識別符號

選擇合適的識別符號是非常重要。選擇時不僅應該考慮儲存型別,而且應該考慮mysql是怎樣進行運算和比較的。一旦選定資料型別,要確保保證所有關聯表中都使用相同的資料型別。

在可以滿足值的範圍的需求,並且預留未來增長空間的前提下,應該選擇最小的資料型別。

整數型別:整數通常是標識列最好的選擇,因為它們很快並且可以使用auto_increment。

字串型別:盡量避免使用字串型別作為識別符號,因為它們很消耗空間,並且通常比數字型別慢。而且,對於隨機的字串,它們在索引中的位置也是隨機的,這會導致頁**、磁碟隨機訪問,以及對於聚簇儲存引擎產生聚簇索引** 。

混用正規化和反正規化,適度冗餘

正規化的優點就是讓資料庫中盡量的去除資料的冗餘,保持資料的一致,使資料的修改簡單,然而它的缺點是通常需要關聯,這不但代價昂貴,也可能使一些索引策略無效。反正規化因為所有資料都在一張表中,可以很好地避免關聯

雖然冗餘欄位的更新成本增加了,但是查詢效率提高了,而且大多冗餘欄位的選取是查詢頻率遠大於更新頻率的字段。

雖然正規化化的資料庫中的表一般都較小,使表中相關列最少,在某些情況下增強了資料庫的可維護性,但在系統要完成一些資料查詢時,可能要用複雜的join才能實現,這勢必會造成查詢效能的低下,如果通過拆分join,通過多次簡單的查詢來在應用中實現join邏輯,將會帶來巨大的網路開銷。

大字段垂直拆分

適度冗餘策略是將別的表中的字段拿過來在自己身上也存乙份資料,而大字段垂直拆分簡單來說就是將自己身上的字段拆分出去放在另外(單獨)的表裡面。自相矛盾?不,將別表的字段拿過來,是因為很多時候查詢需要使用該欄位,為了減少join帶來的效能消耗。而將大字段拿出去,是在大部分查詢不需要使用該字段的時候才會拿出去。首先大字段(文章的內容、帖子的內容、產品的介紹等),其次是和表中其他字段相比訪問頻率明顯要少的字段很多適合拆分出去。

大字段存放的內容較多,佔整條記錄的80%以上,而資料庫中資料在資料檔案中的格式一般都是以一條一條記錄為單位來存放,也就是說,要查詢某些記錄的某幾個字段,資料庫並不是只需要訪問這幾個字段,而是需要讀取其它所有字段(可以在索引中完成整個查詢的情況除外),這就不得不讀取包括大字段在內的很多並不相干的資料,而由於大字段所佔的空間比例非常大,自然所浪費的io資源也就非常大了。故將該大字段從原表中拆分出來,通過單獨的表進行存放,這樣在訪問其它資料時大大降低io訪問,從而使查詢效能得到較大的改善。

實際上,不一定非要大字段才能進行垂直拆分,某些場景下,有的表中大部分字段平時都很少訪問,而其中的某幾個字段卻是訪問頻率非常高,對於這種表,也非常適合通過垂直拆分來達到優化效能的目的。

大表水平拆分

舉個例子,某論壇系統有張post表存放帖子資料,現在有個需求,系統管理員能夠發布系統訊息,並且在貼子的每一頁置頂顯示。第一反應肯定是通過在post表中增加乙個標識列,用來存放帖子的型別,標識出是使用者的普通貼還是管理員的置頂貼,然後在每個列表展示頁面都通過對post表的兩次查詢(一次置頂貼,乙個普通貼)並在應用程式中合併再展示。這樣可能造成的結果是由於整個post表的資料較大,查詢置頂的query成本會相對有些高。

置頂資訊和普通帖完全不會產生任何關聯互動、置頂資訊的變化相對於普通帖來說變化很少、置頂訊息的訪問頻率非常高、置頂資訊的量和普通帖子來比非常之少。經過這幾個分析,可以將置頂訊息單獨存放在普通貼之外的其它表裡面。由於訪問頻率非常高,這種方案使得每次檢索置頂訊息的成本下降,數量少而且變化不怎麼頻繁的特點則非常適合使用mysql的query cache,而如果和普通貼存放在一起會由於普通貼的頻繁變化帶來post表相關query cache失效。

統計表準實時優化

這些統計的計算都會涉及到大量資料,同時也需要大量的計算資源,訪問頻率也都非常的高。如果都通過實時統計,只要資料量稍微大些,都會帶來非常大的硬體資源開銷。但在短時間內的不夠精確,並不會帶來太大使用者體驗的降低,所以完全可以通過定時任務程式,每隔一定時間段進行一次統計後存放在專門設計的統計表中

Mysql選擇合適的資料型別

一般情況下,由於char是固定長度的,所以它的處理速度比varchar要快的多,但是器缺點是浪費儲存空間,程式需要對行尾空格進行處理,所以對於那些長度變化不大並且對查詢速度有較高要求的詩句可以考慮使用char型別儲存。mysql中不同引擎對varchar和插入的使用原則 myisam 使用固定長度 ...

為Mysql選擇合適的資料型別

前提 使用適合儲存引擎。選擇原則 根據選定的儲存引擎,確定如何選擇合適的資料型別。下面的選擇方法按儲存引擎分類 對於innodb資料表,內部的行儲存格式沒有區分固定長度和可變長度列 所有資料行都使用指向資料列值的頭指標 因此在本質上,使用固定長度的char列不一定比使用可變長度varchar列簡單。...

選擇合適的資料型別

選擇合適的資料型別 char和varchar char是固定長度的字元型別,varchar是可變長度的字元型別,因此char檢索起來速度更快。char由於是固定長度的在檢索的時候刪除了尾部的空格。用text和blob時需要注意的問題有 在執行大量刪除操作的時候會在資料表中留下很大的空洞,以後填入這些...