高性能Mysql-Mysql高級特性

1、分區表

對用戶來說,分區表是一個獨立的邏輯表,但是底層由多個物理子表組成。實現分區的代碼實際上是對一組句柄對象的封裝。對分區表的請求, 都會通過句柄對象轉化成對存儲引擎的接口調用。 MySQL實現分區表的方式一一對底層表的封裝一一意味着索引也是按照分區的子表定義的, 而沒有全局索引。 這和Oracle不同,在Oracle中可以更加靈活地定義索引和表是否進行分區。

mysql在創建表時用PARTITION BY子句定義每個分區存放的數據。在執行查詢的時候,優化器會根據分區定義過濾那些沒有我們需要數據的分區,這樣查詢就無須掃描所有分區一一只需要查找包含需要數據的分區就可以了。

分區的一個主要目的是將數據按照一個較粗的粒度分在不同的表中。 這樣做可以將相關的數據存放在一起, 另外, 如果想一次批量刪除整個分區的數據也會變得很方便。在下面的場景中, 分區可以起到非常大的作用:

•    表非常大以至於無法全部都放在內存中, 或者只在表的最後部分有熱點數據, 其他均是歷史數據。
•    分區表的數據更容易維護。例如,想批量刪除大量數據可以使用清除整個分區的方式。 另外, 還可以對一個獨立分區進行優化、 檢查、 修復等操作。
•    分區表的數據可以分佈在不同的物理設備上, 從而高效地利用多個硬件設備。
•    可以使用分區表來避免某些特殊的瓶頸, 例如InnoDB的單個索引的互斥訪問 ext3 文件系統的inode鎖競爭等。

•    如果需要,還可以備份和恢復獨立的分區,這在非常大的數據集的場景下效果非常好。

分區表本身也有一些限制,下面是其中比較重要的幾個點:

•    一個表最多能有1024個分區。

•    在MySQL5.1中,分區表達式必須是整數,或者是返回整數的表達式。 在MySQL5.5中, 某些場景中可以直接使用列來進行分區。
•    如果分區字段中有主鍵或者唯一索引的列, 那麼所有主鍵列和唯一索引列都必須包含進來。
•    分區表中無法使用外鍵約束。

1.1 分區表原理

如前所述, 分區表由多個相關的底層表實現, 這些底層表也是由句柄對象(Handler object) 表示, 所以我們也可以直接訪問各個分區。 存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎), 分區表的索引只是在各個底層表上各自加上一個完全相同的索引。從存儲引擎的角度來看, 底層表和一個普通表沒有任何不同, 存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

分區表上的操作按照下面的操作邏輯進行:

SELECT 查詢

當查詢一個分區表的時候, 分區層先打開井鎖住所有的底層表, 優化器先判斷是否可以過濾部分分區, 然後再調用對應的存儲引擎接口訪問各個分區的數據。

INSERT 操作
當寫入一條記錄時,分區層先打開並鎖住所有的底層表,然後確定哪個分區接收這條記錄,再將記錄寫人對應底層表。

DELETE 操作

當刪除一條記錄時,分區層先打開 並鎖住所有的底層表,然後確定數據對應的分區,最後對相應底層表進行刪除操作。

UPDATE 操作

當更新一條記錄時,分區層先打開並鎖住所有的底層表,MySQL先確定需要更新 的記錄在哪個分區,然後取出數據井更新,再判斷更新後的數據應該放在哪個分區,最後對底層表進行寫入操作,井對原數據所在的底層表進行刪除操作。

1.2 分區表的類型

MySQL支持多種分區表。 我們看到最多的是根據範圍進行分區,每個分區存儲落在某個範圍的記錄,分區表達式可以是列,也可以是包含列的表達式。 例如,下表就可以將每一年的銷售額存放在不同的分區裏:
CREATE TABLE sales {
order date DATETIME NOT NULL,
) ENGINE=InnoDB PARTITION BY RANGE(YEAR{order_date)) {
PARTITION p_2010 VALUES LESS THAN (2010),
PARTITION p_2011 VALUES LESS THAN (2011),
PARTITION p_2012 VALUES LESS THAN (2012),
PARTITION p_catchall VALUES LESS THAN MAXVALUE );

PA RTITION 分區子句中可以使用各種函數。但有一個要求,表達式返回的值要是一個確定的整數,且不能是一個常數。

MySQL還支持鍵值、哈希和列表分區,這其中有些還支持子分區,不過我們在生產環境中很少見到。

1.3 如何使用分區表

假設我們希望從一個非常大的表中查詢出一段時間的記錄,而這幾個表中包含了很多年的歷史數據,數據是按照時間排序的,例如,希望查詢最近幾個月的數據,這大約有10億條記錄。你打算如何查詢這個表?如何才能更高效?

首先很肯定:因爲數據量巨大,肯定不能在每次查詢的時候都掃描全表。考慮到索引在空間和維護上的消耗,也不希望使用索引。即使真的使用索引,你會發現數據並不是按照想要的方式聚集的,而且會有大量的碎片產生,最終會導致一個查詢產生成千上萬的隨機I/O,應用程序也隨之僵死。這時候只有兩條路可選:所有的查詢都只在數據表上做順序掃描, 或者將數據表和索引全部都緩存在內存裏。

這正是分區要做的事情。 理解分區時還可以將其當作索引的最初形態, 以代價非常小的 方式定位到需要的數據在哪一片 “區域”。在這片 “區域” 中, 你可以做順序掃描, 可以建索引, 還可以將數據都緩存到內存, 等等。 因爲分區無須額外的數據結構記錄每個分區有哪些數據一一分區不需要精確定位每條數據的位置, 也就無須額外的數據結構一一 所以其代價非常低。 只需要一個簡單的表達式就可以表達每個分區存放的是什麼數據。

爲了保證大數據量的可擴展性,一般有兩個策略:全量掃描數據, 不妥任何索引;索引數據, 並分離熱點。

1.4 什麼情況下會出現問題

① NULL 位會使分區過濾無效

關於分區表一個容易讓人誤解的地方就是分區的表達式的值可以是NULL:第一個 分區是一個特殊分區。假設按照PARTITION BY RANGE YEAR(order date)分區,那麼所有order_date爲NULL或者是一個非拉值的時候,記錄都會被存放到第一個分區。現在假設有下面的查詢: where order date BETWEEN 2012-01-01'AND'2012-01-31’。實際上,MySQL會檢查兩個分區,而不是一個。

② 分區列和索引列不匹配

如果定義的索引列和分區列不匹配, 會導致查詢無住進行分區過濾。 

③ 選擇分區的成本可能會很高

④ 打開並鎖住所有底層表的成本可能很高

⑤ 維護分區的成本可能很高

1.5 查詢優化

引入分區給查詢優化帶來了一些新的思路(同時也帶來新的bug)。分區最大的優點就是優化器可以根據分區函數來過濾一些分區。根據粗粒度索引的優勢,通過分區過濾通常可以讓查詢掃描更少的數據(在某些場景下)。所以,對於訪問分區表來說, 很重要的一點是要在WHERE 條件中帶入分區列,有時候即使看似多餘的也要帶上, 這樣就可以讓優化器能夠過濾掉無須訪問的分區。如果沒有這些條件,MySQL就需要讓對應存儲引擎訪問這個表的所有分區, 如果表非常大的話,就可能會非常慢。使用EXPLAIN PARTITION 可以觀察優化器是否執行了分區過濾。

MySQL只能在使用分區函數的列本身進行比較時才能過濾分區,而不能根據表達式的值去過濾分區,即使這個表達式就是分區函數也不行。例如:EXPLAIN PARTITIONS SELECT* FROM sales_by_day WHERE  YEAR(day) = 2010。一個很重要的原則是:即便在創建分區時可以使用表達式,但在查詢時卻只能根據列來過濾分區。

1.5 合併表

合併表(Merge table)是一種早期的、簡單的分區實現,和分區表相比有一些不同的限制,並且缺乏優化。分區表嚴格來說是一個邏輯上的概念,用戶無站訪問底層的各個分區,對用戶來說分區是透明的。但是合併表允許用戶單獨訪問各個子表。

合併有很多的限制和行爲,下面列舉的這幾點需要在使用的時候時刻記住。

① 在使用 CREATE 語句創建一個合併表的時候,井不會檢查各個子表的兼容性。如果子表的定義稍有不同,那麼MySQL就可能創建出一個後面無戰使用的合併表。

② 根據合併表的特性,不難發現,在合併表上無法使用 REPLACE 語法,無撞使用自增字段。

③ 如果一個查詢訪問合井表,那麼它需要訪問所有子表。這會讓根據鍵查找單行的查詢速度變慢,如果能夠只訪問一個對應表,速度肯定將更快。

2、視圖

視圖本身是一個虛擬表,不存放任何數據,在使用SQL語句訪問視圖的時候,它返回的數據是mysql從其他表生成的。視圖和表實在同一個命名空間,MySQL在很多地方對於視圖和表是同樣對待的。不過視圖和表也有不同,例如,不能對視圖創建觸發器,也不能使用DROPTABLE命令刪除視圖。

MySQL可以使用這兩種辦戰中的任何一種來處理視圖。 這兩種算法分別稱爲合併算法和臨時表算怯,如果可能,會儘可能地使用合併算怯。 MySQL甚至可以嵌套地定義視圖,也就是在一個視圖上再定義另一個視圖。 

2.1 可更新視圖

可更新視圖是指可以通過更新這個視圖來更新視圖涉及的相關表。 只要指定了合適的條件, 就可以更新、 刪除甚至向視圖中寫人數據。 如果視圖定義中包含了GROUP BY、 UNION、 聚合函數, 以及其他一些特殊情況, 就不能被更新了。

2.2 視圖對性能的影響

多數人認爲視圖不能提升性能,實際上,在MySQL中某些情況下視圖也可以幫助提升性能。而且視圖還可以和其他提升性能的方式疊加使用。例如,在重構schema的時候可以使用視圖,使得在修改視圖底層表結構的時候,應用代碼還可能繼續不報錯的運行。可以使用視圖實現基於列的權限控制,卻不需要真正的在系統中創建列權限,因此沒有額外的開銷。

2.3 視圖的限制

在其他的關係數據庫中你可能使用過物化視圈,MySQL還不支持物化視圖(物化視圖是指將視圖結果數據存放在一個可以查看的表中,並定期從原始表中刷新數據到這個表中)。 MySQL也不支持在視圖中創建索引。 不過,可以使用構建緩存表或者彙總表的辦法來模擬物化視圖和索引。 

MySQL視圖實現上也有一些讓人煩惱的地方。 例如,MySQL並不會保存視圖定義的原 始SQL語句,所以如果打算通過執行SHOW CREATE VIEW後再簡單地修改其結果的方式來重新定義視圖,可能會大失所望。

3、外鍵約束

InnoDB是目前MySQL中唯一支持外鍵的內置存儲引擎,所以如果需要外鍵支持那選擇就不多了(PBXT也有外鍵支持)。使用外鍵是有成本的。比如外鍵通常都要求每次在修改數據時都要在另外一張表中多執行一次查找操作。雖然InnoDB強制外鍵使用索引,但還是無法捎除這種約束檢查的開銷。如果外鍵列的選擇性很低,則會導致一個非常大且選擇性很低的索引。

不過,在某些場景下,外鍵會提升一些性能。如果想確保兩個相關表始終有一致的數據,那麼使用外鍵比在應用程序中檢查一致性的性能要高得多,此外,外鍵在相關數據的刪除和更新上,也比在應用中維護要更高效,不過,外鍵維護操作是逐行進行的,所以這樣的更新會比批量刪除和更新要慢些。

4、在Mysql內部存儲代碼

MySQL允許通過觸發器、 存儲過程、 函數的形式來存儲代碼。 從MySQL5.1開始,還可以在定時任務中存放代碼,這個定時任務也被稱爲 “事件”。存儲過程和存儲函數都被統稱爲 “存儲程序”。

MySQL中使用存儲代碼的優點:

•    它在服務器內部執行,離數據最近,另外在服務器上執行還可以節省帶寬和網絡延遲。
•    這是一種代碼重用。 可以方便地統一業務規則,保證某些行爲總是一致,所以也可以爲應用提供一定的安全性。
•    它可以簡化代碼的維護和版本更新。

•    它可以幫助提升安全, 比如提供更細粒度的權限控制。一個常見的例子是銀行用於轉移資金的存儲過程z這個存儲過程可以在一個事務中完成資金轉移和記錄用於審計的日誌。應用程序也可以通過存儲過程的接口訪問那些沒有權限的表。

•    服務器端可以緩存存儲過程的執行計劃, 這對於需要反覆調用的過程, 會大大降低消豔。
•    因爲是在服務器端部署的, 所以備份、 維護都可以在服務器端完成。所以存儲程序的維護工作會很簡單。它沒什麼外部依賴, 例如, 不依賴任何Perl包和其他不想在 服務器上部署的外部軟件。
•    它可以在應用開發和數據庫開發人員之間更好地分工。不過最好是由數據庫專家來開發存儲過程, 因爲不是每個應用開發人員都能寫出高效的SQL查詢。

MySQL中使用存儲代碼的缺點:

•    MySQL本身沒有提供好用的開發和調試工具, 所以編寫MySQL的存儲代碼比其他的數據庫要更難些。
•    較之應用程序的代碼, 存儲代碼效率要稍微差些。例如, 存儲代碼中可以使用的函數非常有限, 所以使用存儲代碼很難編寫複雜的字符串維護功能, 也很難實現太複雜的邏輯。

•    存儲代碼可能會給應用程序代碼的部署帶來額外的複雜性。

•    因爲存儲程序都部署在服務器內, 所以可能有安全隱患。

•    存儲過程會給數據庫服務器增加額外的壓力, 而數據庫服務器的擴展性相比應用服務器要差很多。

•    MySQL井沒有什麼選項可以控制存儲程序的資源消耗, 所以在存儲過程中的一個小錯誤, 可能直接把服務器拖死。

5、遊標

遊標概念:有數據緩衝的思想:遊標的設計是一種數據緩衝區的思想,用來存放SQL語句執行的結果。 先有數據基礎:遊標是在先從數據表中檢索出數據之後才能繼續靈活操作的技術。 類似於指針:遊標類似於指向數據結構堆棧中的指針,用來pop出所指向的數據,並且只能每次取一個。

遊標的優點:

因爲遊標是針對行操作的,所以對從數據庫中select查詢得到的每一行可以進行分開的獨立的相同或不同的操作,是一種分離的思想。可以滿足對某個結果行進行特殊的操作。 遊標與基於遊標位置的增刪改查能力。 MySQL數據庫中沒有專門描述一行的表達形式,但這是需要的,所以,個人理解的話,我覺得遊標是在關係數據庫這種面向集合的系統中抽離出來,單獨針對行進行表達(也可以理解成網上資料說的:遊標是面向集合與面向行的設計思想之間的一種橋樑

遊標缺點

遊標的缺點是針對有點而言的,也就是隻能一行一行操作,在數據量大的情況下,是不適用的,速度過慢。這裏有個比喻就是:當你去ATM存錢是希望一次性存完呢,還是100一張一張的存,這裏的100一張一張存就是遊標針對行的操作。 數據庫大部分是面對集合的,業務會比較複雜,而遊標使用會有死鎖,影響其他的業務操作,不可取。 當數據量大時,使用遊標會造成內存不足現象。

遊標的使用場景

針對遊標的優缺點,我總結遊標的使用場景,主要用在循環處理、存儲過程、函數中使用,用來查詢結果集,就比如:我們需要從表中循環判斷並得到想要的結果集,這時候使用遊標操作很方便速度也很快。

6、全文索引

通過數值比較、 範圍過濾等就可以完成絕大多數我們需要的查詢了。 但是, 如果你希望通過關鍵字的匹配來進行查詢過濾, 那麼就需要基於相似度的查詢, 而不是原來的精確數值比較。 全文索引就是爲這種場景設計的。

全文索引可以支持各種字符內容的搜索(包括 CHAR、 VARCHAR 和 TEXT 類型), 也支持自然語言搜索和布爾搜索。 

MyISAM 的全文索引是一類特殊的 B-Tree 索引,共有兩層。 第一層是所有關鍵字,然後對於每一個關鍵字的第二層,包含的是一組相關的 “文檔指針”。全文索引不會索引文檔對象中的所有詞語,它會根據如下規則過濾一些詞語:

•    停用詞列表中的詞都不會被索引。 默認的停用詞根據通用英語的使用來設置,可以使用參數ft_stopword_file 指定一組外部文件來使用自定義的停用詞。
•    對於長度大於 ft_min_word_length的詞語和長度小於 ft_max_word_length的詞語,都不會被索引。

7、分佈式(XA)事物

XA事務中需要有一個事務協調器來保證所有的事務參與者都完成了準備工作(第一階段)。 如果協調器收到所有的參與者都準備好的消息,就會告訴所有的事務可以提交了,這是第二階段。 MySQL在這個XA事務過程中扮模一個參與者的角色,而不是協調者。

實際上,在MySQL中有兩種XA事務。 一方面,MySQL可以參與到外部的分佈式事務中;另一方面,還可以通過XA事務來協調存儲引擎和二進制日誌。

7.1 內部XA事物

MySQL本身的插件式架構導致在其內部需要使用XA事務。 MySQL中各個存儲引擎是完全獨立的,彼此不知道對方的存在,所以一個跨存儲引擎的事務就需要一個外部的協調者。 如果不使用XA協議,例如,跨存儲引擎的事務提交就只是順序地要求每個存儲引擎各自提交。 如果在某個存儲提交過程中發生系統崩憤,就會破壞事務的特性(要麼就全部提交,要麼就不做任何操作)。

如果將MySQL記錄的二進制日誌操作看作一個獨立的 “存儲引擎”,就不難理解爲什麼即使是一個存儲引擎參與的事務仍然需要XA事務了。 在存儲引擎提交的同時, 需要將 “提交 ” 的信息寫入二進制日誌, 這就是一個分佈式事務, 只不過二進制日誌的參與者是MySQL本身。XA事務爲MySQL帶來巨大的性能下降。從MySQL5.0開始,它破壞了MySQL內部的“批提交” (一種通過單磁盤I/O操作完成多個事務提交的技術), 使得MySQL不得不進行多次額外的fsync()調用。

7.2 外部XA事物

MySQL能夠作爲參與者完成一個外部的分佈式事務。 但它對XA協議支持並不完整, 例如, XA協議要求在一個事務中的多個連接可以做關聯,但目前的MySQL版本還不能支持。
因爲通信延遲和參與者本身可能失敗, 所以外部XA事務比內部消耗會更大。 如果在廣域網中使用XA事務, 通常會因爲不可預測的網絡性能導致事務失敗。 如果有太多不可控因素, 例如, 不穩定的網絡通信或者用戶長時間地等待而不提交,則最好避免使用 XA事務。 任何可能讓事務提交發生延遲的操作代價都很大, 因爲它影響的不僅僅是自己本身, 它還會讓所有參與者都等待。

 

 

 

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