1. 程式人生 > >Schema與數據類型優化

Schema與數據類型優化

ber 2nf 減少 掃描 時間 進行 時也 分布 包括

  選擇數據類型  

    更小的通常更好:盡量使用可以正確存儲數據的最小數據類型。因為它們占用的更少的磁盤,內存和CPU緩存,並且處理需要CPU周期也更少。

    簡單就好:簡單數據類型的操作通常需要更少的CPU周期。應該使用MySQL內建的類型而不是字符串來存儲日期和時間,用整形存儲IP地址。

    盡量避免NULL:通常情況下,盡量指定列為NOT NULL。NNULL的列使索引,索引統計和值比較都比較復雜。當存在NULL的列被設置為索引時,每個索引記錄需要額外的一個字節。InnoDB使用單獨的位(bit)存儲NULL值,所以對於稀疏數據有很好的空間效率。但這點不適合MyISAM。

  TIMESTAMP只使用DATETIME一半的存儲空間,並且會根據時區變化,具有特殊的自動更新能力。TIMESTAMP允許時間範圍要小很多。

  整數類型

    整數(whole number)和實數(real number)。如果存儲整數,可以使用這幾種整數類型:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT。分別使用8,16,32,64位存儲空間,它們惡意存儲的範圍址從-2的N-1次方到2的N-1次方-1,N為存儲空間的位數。

    整數類型有可選的UNSIGNED屬性,表示不允許負值。

    整數計算一般使用64位的BIGINT整數,即使在32位的環境下也是如此。MySQL可以為整數類型指定寬度,它對於存儲和計算來說是沒有區別的,只是會限制一些交互工具顯示字符的個數。

  實數類型

    實數是帶有小數部分的數字。MySQL即支持精確類型,也支持不精確類型。

    FLOAT和DOUBLE類型支持使用標準的浮點運算進行近似計算,

    DECIMAL類型用於存儲精確的小數。

    浮點和DECIMAL類型都可以指定精度。對於DECIMAL列,可以指定小數點前後所允許的最大位數,則會影響列的空間消耗。DECIMAL只是一種存儲格式,在計算中DECIMAL會轉換為DOUBLE類型。有很多種方法可以指定浮點列所需要的精度,這使得MySQL會選擇不同的數據類型或在存儲時對值進行取舍。這些精度定義是非標準的,所以建議只指定數據類型而不指定精度。

    浮點類型在存儲同樣範圍的值時,通常比DECIMAL使用更少的空間。應盡量支隊小樹進行精確計算時才使用DECIMAL。在數據量比較大時,可以使用BIGINT代替DECIMAL。

  字符串類型

    VARCHAR和CHAR是兩種最主要的字符串類型。它的存儲和存儲引擎的具體實現相關。

    VARCHAR類型用於存儲可變字符串,是最常見的字符串數據類型,它比定長類型更節省空間。若MySQL表使用ROW_FORMAT=FIXED創建的話,每一行都會定長存儲。VARCHAR需要使用1或2個額外字節記錄字符串的長度:如果列的最大長度小於或等於255字節,則只使用1個字節表示,否則用兩個。由於行是變長的,所以在UPDATE時可能使行變得比原來更長,需要額外的工作。若一個行占用的空間增長,並且在頁內沒有更多的空間可以存儲,此時,MyISAM會將行拆成不同的片段進行存儲,InnoDB或者額需要分裂頁來使行放進頁內。當字符串的最大長度比平均長度大很多時;列的更新很少;使用了UTF-8時比較適合用VARCHAR。MySQL在存儲和檢索時會保留末尾空格。InnoDB會把過長的VARCHAR存儲為BLOB。

    CHAR類型時定長的:MySQL總是根據定義的字符串的長度分配足夠的空間。當存儲CHAR值時,MySQL會刪除所有的末尾空格。CHAR值會根據需要采用空格進行填充以方便比較。CHAR適合存儲很短的字符串,或者所有值都接近於一個長度。CHAR適合存儲密碼的MD5值,對於不經常變更的值CHAR也比較合適。對於比較短的列,CHAR比VARCHAR在存儲空間上也更有效率。

    Memory引擎只支持定長的行,即使有變長字段也會根據最大長度分配最大空間。

    填充和截取空格的行為在不同存儲引擎都是一樣的,這是在MySQL服務器層進行處理的。

    BINARY和VARBINARY存儲的事二進制字符串,二進制字符串存儲的是字節碼而不是字符。MySQL填充BINARY采用的是\0而不是空格,在檢索時也不會去掉填充值。MySQL比較BINARY字符串時,每次按一個字節,並且根據該字節的數值進行比較,因此二進制比較比字符比較簡單得多,所以更快。

  BLOB和TEXT類型

    BLOB和TEXT都是為存儲很大的數據而設計的字符串數據類型,分別采用二進制和字符方式存儲。

    字符類型時:TINYTEXT,SMALLTEXT,TEXT,MEDIUMTEXT,LONGTEXT。

    二進制類型:TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB,LONGBLOB。

    MySQL把每個BLOB和TEXT值當作一個獨立的對象處理。存儲引擎在存儲時通常會作特殊處理。當BLOB和TEXT值太大時,InnoDB會使用外部存儲區域進行存儲,此時每個值在行內需要1~4個字節存儲一個指針,然後在外部存儲區域存儲實際的值。

    BLOB和TEXT的區別是BLOB類型存儲的是二進制數據,沒有排序規則或字符集,而TEXT是字符集和排序規則。

    MySQL對BLOB和TEXT列進行排序與其他類型是不同的:它只對每個列的最前max_sort_length字節而不是整個字符串進行排序。若只需要排序前面一小部分,可以減少max_sort_length的配置,或使用ORDER BY SUSTRING(column, length)。MySQL不能將BLOB和TEXT列全部長度的字符串進行索引,也不能使用這些索引消除排序。

    因為Memory引擎不支持BLOB和TEXT,所以查詢使用BLOB或TEXT列並且需要使用隱式臨時表,此時可以在所有用到BLOB的字段都使用SUBSTRING(column, length)將列的值轉換為字符串,這樣就可以使用內存臨時表了。但要確保街區的子字符串足夠短,不會使臨時表的大小超過max_heap_table_size或tmp_table_size,超過以後MySQL會將內存臨時表轉換為MyISAM磁盤臨時表。

  使用枚舉(ENUM)代替字符串類型

    枚舉列可以把一些不重復的字符串存儲成一個預定義的集合。MySQL在存儲枚舉時非常緊湊,會根據列表值的數量壓縮到一個或兩個字節中。MySQL在內部會將每個值在列表中的位置保存為整數,並且在表的.frm文件中保存“數字-字符串”映射關系的“查找表”。枚舉字段時按照內部存儲的整數而不是定義的字符串進行排序的。一種繞過這種限制的方式是按照需要的順序來定義枚舉列。另外也可以在查詢中使用FIELD()函數顯式地指定排序順序,但這會導致MySQL無法利用索引消除排序。枚舉字符串列表是固定的,添加或刪除字符串必須使用ALTER TABLE。因此對於未來可能會改變的字符串,使用枚舉不死一個好主意,除非能接受旨在列表末尾添加元素。由於MySQL把每個枚舉值保存為整數,並且必須進行查找才能轉換為字符串。通常枚舉的列表都比較小。

  日期和時間類型

    MySQL可以使用許多類型來保存日期和時間值。例如YEAR和DATE。MySQL能存儲的最小時間粒度為秒。但MySQL可以使用微秒級的粒度進行臨時運算。MySQL提供兩種日前類型;DATETIME和TIMESTAMP。

    DATETIME精度為秒。它把日期和時間封裝到格式為YYYYMMDDHHMMSS的整數中,與時區無關。使用8個字節的存儲空間。

    TIMESTAMP只使用了4個字節的存儲空間。MySQL提供了FROM_UNIXTIME()函數把Unix時間轉換為日期,並提供UNIX_TIMESTAMP()函數把日期轉換為Unix的時間戳。TIMESTAMP顯示的值也依賴於市區。MySQL服務器,操作系統以及客戶端連接都有時區的設置。默認情況下,若插入時沒有指定第一個TIMESTAMP列的值,MySQL則設置這個列的值為當前時間。TIMESTAMP列默認為NOT NULL。

    我們應該盡量使用TIMESTAMP。

  

  位數據類型

    BIT:BIT是TINYINT的同義詞。可以使用BIT列在一列中存儲一個或多個true/false值。BIT(1)定義一個包含單個位的字段,BIT列的最大長度是64個位。BIT的行為因存儲引擎而異。MyISAM會打包所有的BIT列;Memory和InnoDB位每個BIT列使用一個足夠存儲的最小整數型來存放,所以不能節省存儲空間。MySQL把BIT當作字符串類型,而不是數字類型。當檢索BIT(1)的值時,結果是一個包含二進制0或1值的字符串,而不是ASCII的0或1。然而在數字上下文的場景中檢索時,結果將是位字符串轉換成數字。若想在一個bit的存儲空間中存儲一個true/false,可以創建一個可以為空的CHAR(0),可以保存NULL或空字符串。

    SET:如果需要保存很多true/false,可以考慮合並這些列到一個SET數據類型,它在MySQL內部是一一些列打包的位的集合來表示的。缺點是改變列的定義的代價比較高:需要ALTER TABLE。一般來說也無法在SET裂傷通過索引來查找。

    在整數列上進行按位操作:一種替代SET的方式是使用一個整數包裝一些列的位。好處是可以不實用ALTER TABLE改變字段代表的“枚舉值”,缺點是查詢語句不好寫並且不好理解。

  選擇標識符(identifier)

    一旦選擇了一種類型,要確保所有關聯表中國年都使用同樣的類型。類型之間需要精確匹配,包括像UNSIGNED這樣的屬性。整數類型通常是標識列最好的選擇,因為他們可以很快並可以使用AUTO_INCREMENT。ENUM和SET列適合存儲固定信息。應該避免使用字符串類型作為標識列,它們很消耗空間,並且唱比數字類型慢。對於完全隨機的的字符串,如MD5(),SHA1(),UUID()等產生的字符串,這些函數生成的新值會任意分布在很大的空間內。因為插入值會隨機的寫到索引的不同位置,所以INSERT很慢;邏輯上相鄰的行分布在磁盤和內存的不同地方,所以SELECT很慢。隨機值導致緩存懟所有類型的查詢語句效果都很差。若存儲UUID值,則應該移除“-”富豪;或者用UNHEX()函數轉換UUID值為16字節的數字,並且存儲在一個BINAEY(16)列中。檢索時可以通過HEX()函數啦格式化為十六進制格式。UUID值雖然分布不均魚,但是有一定順序,但還是應該選擇遞增的整數。

  存儲IP時應使用無符號整數。MySQL提供INET_ATON()和INET_NTOA()函數在這兩種方法之間相互轉換。

  

  MySQL schema中的陷阱

    太多的列:MySQL的存儲引擎API工作時需要在服務器層和存儲引擎層之間通過行緩沖格式拷貝數據,然後在服務器層將緩沖內容解碼成各個列。從行緩沖功能中國年將編碼過的列轉換成行數據結構的操作代價是非常高的。MyISAM的定長行結構實際上與服務器層的行結構正好匹配,所以不需要轉換。但是MyISAM和變長結構和InnoDB的行結構則總需要轉換。轉換的代價依賴於列的數量。

    太多的關聯:實體——屬性——值(EVA)設計模式在MySQL下不能很好的工作。MySQL限制了每個關聯操作最多只有61張表。單個查詢最好在12個表以內做關聯。

    全能的枚舉:防止過度使用枚舉。

    變相的枚舉:枚舉列允許在列中存儲一組定義值中的單個值,集合(SET)則允許在列中存儲一組定義值中的一個或多個值。

    非此發明(Not Invent Here)的NULL:避免使用NULL,可以使用0或某個特殊值,或者空字符串來作為代替。

  範式和反範式

    1NF(第一範式):強調原子性,即列不能夠再被復制

    2NF(第二範式):首先是1NF,且必然有主鍵,沒有包含在主鍵的列必須完全依賴於主鍵

    3NF(第三範式):首先是2NF,且非主鍵列必須直接依賴於主鍵

    在範式話數據庫中,每個事實數據會出現並且只出現一次。相反,在反範式化的數據庫中,信息是冗余的,可能會存儲在多個地方。

    範式的優點

      範式化的更新操作通常比反範式化要快

      當數據較好地範式化時,就只有很少或者沒有重復數據,所以只需要修改很少的數據

      範式化的表通常更小,可以更好地放在內存裏,所以執行操作會更快

      很少有多余的數據意味著檢索列表數據時更少需要DISTINCT或者GROUP BY語句

    範式的缺點是通常需要關聯。  

    反範式化的schema因為所有數據都在一張表中,可以很好地避免關聯。若不需要關聯,則對大部分查詢最差的情況是全表掃描。當數據比內存大時可能比關聯要快得多,因為避免了隨機I/O。

    最常見的反範式化數據的方式是復制或者緩存,在不同的表中存儲相同的特定列,可以使用觸發器更新緩存值。從父表冗余一些數據倒子表的理由是排序需要。

  緩存表和匯總表

    有時挺升性能最好的辦法是在同一張表中保存衍生的冗余數據,有時也需要創建一張完全獨立的匯總表或緩存表。我們使用緩存表來存儲哪些可以比較簡單地從schema其他表獲取數據的表,匯總表保存的是使用GROUP BY語句聚合數據的表。

    緩存表對優化搜索和檢索查詢語句很有效。可以對緩存表使用不同的存儲引擎。

    當重建匯總表和緩存表時,通常需要保證數據在操作時依然可用。這需要使用影子表。影子表指的是一張在真實表背後創建的表。當完成建表操作後,可以通過一個原子命名操作切換影子表和原表。

    CREATE TABLE t2 AS SELECT * FROM t1; //只有表結構和數據,沒有索引,註釋,序列等

    CREATE TABLE t2 LIKE t1;//有完整的表結構,但是沒有數據

  

  物化視圖

    物化視圖實際上是預先計算並且存儲在磁盤上的表,可以通過各種各樣的策略刷新和更新。MySQL並不支持物化視圖。可以使用Flexviews,它由下面幾個部分組成。

      變更數據抓取(change data capture, CDC)功能,可以讀取服務器的二進制日誌並且解析相關行的變更

      一系列可以幫助創建和管理試圖的定義的存儲過程

      一些可以應用變更到數據庫中國年的物化視圖的工具

    相比傳統的維護匯總表和緩存表的方法,Flexviews通過提取對源表的更改,可以增量地重新計算物化視圖的內容。

  

  計數器表

    創建一張獨立的表存儲技術器可以幫助避免查詢緩存實效,並且可以使用本節展示的一些更高級的技巧。例如在高並發下,可以將計數器保存在多行,每次隨機選擇一行進行更新。

  加快ALTER TABLE操作的速度

    MySQL執行大部分修改表結構操作的方法是用新的結構創建一個空表,從舊表中查處所有數據插入新表,然後刪除舊表。大部分ALTER TABLE會導致MySQL服務中斷。可以通過兩個技巧:一種是先在一臺不提供服務的機器上執行ALTER TABLE操作,然後和提供服務的主庫進行切換;另一種技巧是影子拷貝。影子拷貝的技巧是用要求的表結構創建一張和源表無關的新表,然後通過重命名和刪表操作交換兩張表。

    更改表可以通過ALTER COLUMN修改表中的列,這個命令直接修改.frm文件,因此速度很快。也可以直接修改.frm來改動表達到不新建一張表的目的;修改.frm只適用以下幾種方式:

      移除一個列的AUTO_INCREMENT屬性

      增加,移除或更改ENUM和SET常量。若移除的是已經有有行數據用到其值的常量,查詢將會返回個空字符串。

    基本的技術是為想要的表結構創建一個新的.frm文件,然後用它替換已經存在的那張表的.frm,步驟如下:

      創建一張由相同結構的空表,並進行所需要的更改;(CREATE TABLE t2 LIKE t1;ALTER TABLE t2 MODIFY COLUMN rating ENUM(‘A‘,‘B‘,‘C‘) DEFAULT ‘A‘;)

      執行FLUSH TABLES WITH READ LOCK。它會關閉所有正在使用的表,並且禁止任何表被打開;(FLUSH TABLES WITH READ LOCK;)

      交換.frm文件;(mv t1.frm t1_temp.frm;mv t2.frm f1.frm;mv t1_temp.frm f2.frm)

      執行UNLOCK TABLES來釋放第二步的讀鎖(UNLOCK TABLES)

 

  快速創建MyISAM索引

    為了高效地載入數據到MyISAM表中,常用的技巧是先禁用索引,載入數據然後重新啟用索引:

    ALTER TABLE test.load_data DISABLED KEYS;

    ALTER TABLE test.load_data ENABLE KEYS;

    這麽做的原因是構建索引的工作被延遲到數據完全載入以後,這是可以通過排序來構建索引,使得索引數的碎片更少,更緊湊。缺點是對唯一索引無效。DISABLED KEYS只對非唯一索引有效。

   

  在InnoDB中,有個技巧是先刪除所有的非唯一索引,然後增加新的列,最後重新創建刪掉的索引。

Schema與數據類型優化