1. 程式人生 > >Mysql優化(出自官方文件) - 第九篇(優化資料庫結構篇)

Mysql優化(出自官方文件) - 第九篇(優化資料庫結構篇)

目錄

  • Mysql優化(出自官方文件) - 第九篇(優化資料庫結構篇)
    • 1 Optimizing Data Size
    • 2 Optimizing MySQL Data Types
    • 3 Optimizing for Many Tables
    • 4 Internal Temporary Table Use in MySQL

Mysql優化(出自官方文件) - 第九篇(優化資料庫結構篇)

1 Optimizing Data Size

通常來講,定義表的時候,一個合理的資料型別,往往意味著更少的儲存空間,更少的磁碟I/O,索引掃描也會更快,接下來將從五個方面來介紹如何進行這種優化:

  • Table Columns

    • 儘量選擇更合適的資料型別,比如說,對於integer型別來講,選擇MEDIUMINTINT能節約25%的儲存空間。
    • 如果某列不為NULL,那麼儘可能的將其定義為NOT NULL,這樣子,不僅能節約1bit的儲存空間,更重要的是,Mysql的處理速度能夠得到大大的提高,因為這樣子Mysql就不需要處理NULL的情況,且索引的處理也能得到加速。
  • Row Format

    • InnoDB預設使用DYNAMIC的行格式,如果要使用其他的格式,可以通過配置innodb_default_row_format的方式來指定,或者在建立表/更改表的時候顯示指定行格式。

      為了使用緊湊型的行格式,可以指定 COMPACT

      , DYNAMIC, and COMPRESSED,這三種格式一方面可以減少儲存空間,另外一方面,也可以大大提高cache命中率,減少磁碟I/O。

    • 使用ROW_FORMAT=COMPRESSED格式,InnoDB壓縮表支援讀和寫,而MyISAM則只支援讀
    • 對於MyISAM表,如果沒有指定變長的列(如VARCHAR, TEXT或者BLOB),那麼將會預設使用固定長度的型別,對於MyISAM來講,固定長度的列速度更快,當然代價是會浪費一部分空間。

  • Indexes

    • primary key應該儘可能的短,這樣子查詢的時候效率更高,在InnoDB中,由於每一個primary key在二級索引中都會被複制一份,當擁有很多的二級索引時,這就顯得很有必要。
    • 只有需要的時候再去建立索引,索引雖然可以加快訪問速度,但是同時也會增加insertupdate的開銷;如果訪問一個表的時候,經常訪問多列的組合,那麼在這個組合上建立索引,並且儘量保證訪問頻次最高的列在最前面,千萬不要分別在這些列上建立索引。
    • 通常來講,有很大的可能一個string型別的列有著不同的字首,這個時候建議只在這些字首上建立索引,這樣子不僅可以增大索引的cache命中率,還可以減少磁碟I/O
  • Joins

    • 在某些場景下,將一個經常被掃描的表分成兩個是有較大好處的,特別是當這個表是一個dynamic-format表且可以使用一個更小的static format表來索引它的時候。
    • 將需要join的兩個表的列設定為類似的資料型別,可以加快join的速度。
    • 使用簡單的名字,這樣子當查詢多個表的時候可以使用一樣的名字,並且能夠簡化join語句,比如:在一個customer表中,使用name,而不要使用customer_name
  • Normalization

    • 通常來講,儘量保證所有的資料是非冗餘的(參考第三正規化的定義),避免重複的使用冗長的值(比如names或者addresses之類的),而是選擇為他們賦一個唯一的ID,這樣子在多個小表中就可以重複使用這些ID,在join的時候儘量使用ID來進行join,這樣子就可以加快訪問速度。
    • 如果速度的優先順序非常高,比磁碟空間和維護多個重複資料的優先順序還高,這個時候,就不需要嚴格遵循這些規範,可以適當的冗餘一些資訊或者建立一個類似於summary的表來加快訪問速度。

2 Optimizing MySQL Data Types

  • Optimizing for Numeric Data

    • 對於唯一的ID或者其他可以用string和數字表達的值,儘量使用numeric,因為相對於string型別,numeric佔用的位元組數更小,並且在處理的時候也會佔用更小的記憶體空間。
    • 如果使用numeric資料,訪問的資料庫的速度要遠比訪問文字檔案的速度快,因為在資料庫中儲存的空間更加的緊湊。
  • Optimizing for Character and String Types

    • 使用BINARY型別的callation,速度更快
    • 當比較不同列時,將這些列宣告為同種字符集和collation,可以避免型別之間的轉換。
    • 當列小於8kb的時候,使用二進位制形式的VARCHAR來取代BLOB型別,因為GROUP BYORDER BY有可能會建立臨時表,對於Mysql來講,如果沒有BLOB欄位,那麼有可能會直接使用MEMORY儲存引擎來建立臨時表。
    • 如果一個表包含諸如name或者address這樣的列,並且這些列的訪問頻次非常低,那麼,請將這些列分割到小表中,並使用join來查詢,使用外來鍵來同步更新,因為當Mysql獲取某一行的時候,會讀取一個data block,這麼做可以減少Mysql讀取block的大小,從而減少磁碟I/O和記憶體佔用。
    • InnoDB中,如果使用隨機值作為primary key,那麼最好用當前的日期或者時間作為該列的字首,對於連續的primary key,Mysql在物理上儲存的方式也是連續的,這樣子可以加快insertselect的速度。
  • Optimizing for BLOB Types

    • 當儲存一個非常大的BLOB列,且該列包含文字資料時,考慮壓縮它,但是如果表已經被設定為壓縮了,那麼不要這樣做。
    • 類似於string型別的優化方式,如果BLOB的訪問頻次不高,為了減少記憶體佔用,考慮將BLOB分離為一個小表。
    • 鑑於獲取BLOB型別列的效能需求非常不同於其他資料型別,考慮將只有BLOB型別的表放在不同的儲存裝置或者其他的資料庫例項中,比如:因為BLOB型別涉及到大量的順序讀寫,那麼將BLOB儲存在傳統機械磁碟上速度將可能優於SSD裝置。
    • 當需要測試一段非常長的文字是否相同時,考慮使用一個專門的列來儲存這段文字的HASH值(使用MysqlMD5()CRC32()函式來實現),並且對其建立索引,因為雜湊函式可能會產生相同的結果,所以,在查詢的時候,除了檢測HASH列外,還需要加上AND blob_column = long_string_value來確保出現這種情況時的正確性,這種優化方式可以大大加快BLOB型別列的處理速度。

3 Optimizing for Many Tables

我們已經知道為了加快查詢速度,將資料分割為多個表是一種非常方便的優化方式,但是,當表的數量增大到上千個甚至上萬個時,優化方式將會有些不一樣。

  • How MySQL Opens and Closes Tables

    使用mysqladmin status命令,可以看到如下結果:

    Uptime: 426 Running threads: 1 Questions: 11082
    Reloads: 1 Open tables: 12

    為了提高效能,Mysql針對每一個session都會open一個table,這樣子,如果有多個客戶端連線到Mysql,那麼這裡的open tables可能比實際的表要多。(注意,MyISAM是不一樣的,所有的session共享檔案描述符)

    出現下面的情況,Mysql將會關閉一個沒有用的table,並且將其從cache中移除:

    • cache滿了,此時有一個執行緒嘗試開啟不在cache中的table
    • cache中包含超過table_open_cachetable,此時cache中有沒有被任何執行緒使用的table
    • 當進行table-flushing操作時,通常是通過FLUSH TABLES , 或者 mysqladmin flush-tables or mysqladmin refresh 觸發。

    如果一個table cache滿了,但是此時有需要開啟一個table,Mysql將會使用如下策略:

    • 那些沒有被使用的table將會被釋放,採用LRU原則
    • 如果一個table必須被開啟,但是cache又滿了,此時,cache會被臨時擴充,在擴充狀態下的cache,一旦某個table變成unused狀態,那麼會被直接移除。

    對於MyISAM表,該表沒被訪問一次,就會被開啟一次,也就是說如果有兩個執行緒訪問一樣的表或者說一張執行緒訪問了該表兩次,那麼該表會被open兩次;第一次開啟表的時候需要兩個檔案描述符:第一個是資料file,另外一個是索引file,其中資料file每個執行緒使用一個,索引file所有執行緒共享。

  • Disadvantages of Creating Many Tables in the Same Database

    很容易會導致table cache不夠用,然後出現不斷開啟關閉的情況,降低效率。

4 Internal Temporary Table Use in MySQL

很多情況下,Mysql都會進行建立臨時表(記憶體表或者會下盤的表),這些條件如下(因為原文太長,所以這裡省略和合並部分條件):

  • 絕大多數union語句,只有部分特殊情況才不會進行此操作(下面會進行討論)。
  • 對於一些views的評估,如使用了TEMPTABLE演算法,UNION或者聚合函式
  • derived tables, common table expressions,帶有子查詢的建立表操作或者semijoin
  • ORDER BYGROUP BY的列不同,或者在join中,ORDER BYGROUP BY的列包含除了第一個表以外其他表的列。
  • ORDER BY並且帶有DISTINCT可能會需要建立臨時表
  • 帶有SQL_SMALL_RESULT 識別符號的語句,會使用一個記憶體臨時表,除非該語句包含必須建立on-disk臨時表的部分。
  • INSERT ... SELECT形式的語句。
  • 多表UPDATE操作,GROUP_CONCAT或者COUNT(DISTINCT)語句,window functions

對於一些查詢,Mysql無法使用in-memory臨時表,這個時候就只能建立on-disk臨時表,這些情況包括:

  • 包含BLOB或者TEXT列的表,然而,對於Mysql8.0.13,TempTable這種in-memory儲存引擎,已經支援了大的二進位制型別。
  • SELECT中,如果使用了UNION或者UNION ALL,並且string型別的列包含了超過512(對於二進位制來講單位是bytes,對於非二進位制型別來講是字元數),那麼將無法使用in-memory儲存引擎。
  • 對於 SHOW COLUMNSDESCRIBE,且使用了BLOB型別的語句

某些情況下,UNION也不一定會使用臨時表,這些情況包括:

  • union型別是 UNION ALL, not UNION or UNION DISTINCT.
  • 沒有全域性的ORDER BY語句
  • 對於{INSERT | REPLACE} ... SELECT ...語句,union不在最外層。

在具體實現時,內部in-memory臨時表儲存在TempTable或者MEMORY儲存引擎,on-disk臨時表儲存在InnoDB中:

在Mysql8.0.16之前,可以通過internal_tmp_disk_storage_engine=MYISAM引數來制定具體的儲存引擎,但是其或者之後,該引數就已經被拋棄了,使用者將無法配置使用哪種儲存引擎。

NOTE:

當使用MEMORY儲存引擎時,剛開始臨時表會被建立在記憶體中,但是當臨時表變得比較大了,Mysql會開始建立臨時檔案,並使用對映的方式對映到記憶體中。