高性能MySQLmysql
MySQL最重要、不同凡響的特性時它的存儲引擎架構,這種架構的設計將查詢處理及其餘系統任務和數據存儲、提取相分離。 這種處理和存儲的設計能夠在使用時根據性能、特性以及其餘需求來選擇數據的存儲方式
mysql服務器邏輯架構圖
算法
存儲引擎層sql
所謂鎖策略,就是在鎖的開銷和數據的安全性之間尋求平衡。數據庫
是鎖最基本的鎖策略,而且開銷最小的策略。
服務器會爲alter table之類的語句使用表鎖,而忽略存儲引擎的鎖機制設計模式
最大程度的支持併發處理,同時也帶來了最大程度的鎖開銷。api
一組原子性的sql查詢,或者說一個獨立的工做單元。緩存
可重複讀隔離級別仍是沒法解決另一個幻讀的問題:當某一個事務在讀取某個範圍內的記錄時,另外一個事務由在該範圍內插入了新的記錄,當以前的事務再次讀取該範圍的記錄時,會產生幻行安全
使用事務日誌,存儲引擎在修改表的數據時只須要修改其內存拷貝,再把該修改行爲記錄持久化在硬盤上事務日誌中,而不用每次都將修改的數據自己持久到磁盤。事務日誌採用的是追加的方式,所以寫日誌的操做是磁盤上一小塊區域的順序I/O,
Wtite ahead Logging性能優化
Mysql默認採用autocommit模式服務器
SET TRANSACTION ISOLATION LEVEL 設置隔離級別
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED 設置數據庫的隔離級別
Innodb採用的是兩階段鎖定協議,在事務執行過程當中,隨時均可以執行鎖定,鎖只有在執行commit或者rollback的時候纔會釋放。而且全部鎖在同一時間釋放
顯式鎖
SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE
## 1.4 多版本併發控制
MVCC的實現,是經過保存數據在某個時間點的快照來實現的。也就是說,無論須要執行多長時間,每一個事務看到的數據都是一致的。根據事務開始的時間不一樣,每一個事務對同一張表,同一時刻看到的數據可能不同。
REPEATABLE READ隔離級別下MVCC實現 SELECT a.InnoDB查找版本早於當前事務版本的數據行,這樣能夠確保事務讀取的行,要麼是在事務開始前已經存在的,要麼是事務自身插入或修改的。 b.行的刪除版本要麼爲定義,要麼大於當前事務版本號。這能夠確保事務讀取到的行,在事務開始以前未被刪除。 INSERT Innodb 爲新插入的每一行保存當前系統版本號做爲行版本號 DELETE InnoDB 爲刪除的每一行保存當前系統版本號做爲刪除標識 UPDATE InnoDB爲插入一行新記錄,保存當前系統版本號做爲行版本號,同時保存當前系統版本號到原來的行做爲行刪除標識
null對於mysql來講更難優化,由於可爲null的列使得索引、索引統計信息和值都更復雜
DateTime 和Timestamp均可以存儲相同類型的數據,時間和日期,精確到秒。而Timestamp只使用DateTime通常的存儲空間,而且會根據時區變化,具備自動更新的能力。
存儲空間(bit) TINYINT 8 SMALLINT 16 MEDIUMINT 24 INT 32 BIGINT 64
儘可能值在對小數進行精確計算時才使用decimal
varchar 和 char
VARCHAR須要使用1或2個額外字節記錄字符串長度。
可是因爲時變長,在update時可能使得行變得比原來長,這就致使須要額外的工做。 若是一個行佔用的空間增加,而且頁內沒有更多空間存儲時,不一樣存儲引擎處理方式不一樣。
MyISAM會將行拆成不一樣的片斷存儲。 InnoDB須要分裂頁來使得行可存儲。
mysql在內會將每一個值在列表中的位置保存爲證書,並在.frm文件中保存數字-字符串的映射關係
枚舉字段時按照內部存儲的證書而不是定義的字符串進行排序的。
「實體-屬性-值」設計模式是一個常見的糟糕的設計模式,尤爲是在MySQL下不能靠譜的工做。 mysql限制了每一個關聯操做最多隻能有61張表
在範式化的數據庫中,每一個事實數據都會出現而且只出現一次。相反在反範式化的數據庫中,信息時冗餘的,可能會存儲在多個地方。
mysql執行大部分修改表結構操做的方法時用新的結構建立一個空表,從舊錶中查出全部數據插入新表,而後刪除舊錶
列的默認值實際上存放在.frm文件中,因此能夠直接修改這個文件而不須要改動表自己。
alter colum 操做改變列的默認值,而不涉及改表的數據。
在mysql中,索引實在存儲引擎而不是在服務器層實現的
B-TREE一般意味着全部的值都是按順序存儲,而且每個葉子頁到根的距離象通B-TREE索引
B-TREE對索引列的順序組織存儲的,很適合查找範圍數據
可使用b-tree索引的查詢類型:全鍵值、鍵值範圍或鍵前綴查找
關於B-tree索引的限制
mysql中只有memory引擎顯式支持哈希索引
哈希索引的限制
Innodb引擎有一個特殊功能叫作自適應哈希:當Innodb注意到某些索引值被使用的很是頻繁時,它會在內存中基於B-Tree索引值上再建立一個哈希索引。這樣就讓B-tree索引也具備哈希索引的一些優勢
relational database index design and the optimizers ---by Tapio lahdenmak and Mike leach
索引並不老是最好的工具 - 對於很是小的表,大部分狀況下簡單的全表掃描更有效 - 對於中到大型的表,索引就很是有效 - 對於特大型的表,創建和使用索引的代價將隨之增加,可使用分區技術
若是不是獨立的列, mysql就不會使用索引,獨立的列時指索引列不能是表達式的一部分
索引選擇性(cardinality) 是指不重複的索引值和數據表總記錄的比值
select count(*) as cnt LEFT(city,7) as pref from sakil.city_demo group by pref order by cnt desc limit 10; 經過統計發現前綴長度到達7的時候,再增長前綴長度選擇性提高的幅度已經很小了。 建立前綴索引 alter table sakila.city_demo add key(city(7));
索引從最左列進行匹配:
選擇性最高的列放到索引最前列
可是性能不僅是依賴於全部索引列的選擇性,也和查詢條件和具體值有關,也就是和值的分佈有關。
聚簇索引並非一種單獨的索引類型,而時一種數據存儲方法。具體的細節依賴於其實現方式,但InnoDB的聚簇索引實際上再同一個結構中保存了B-TREE索引和數據行
當由聚簇索引時,它的數據行實際上存放在索引的葉子頁中。 聚簇表示數據行和相鄰的鍵值緊湊的存儲在一塊兒。由於沒法同時把數據行存放在兩個不一樣的地方,因此一個表只能有一個聚簇索引。
Innodb過主鍵彙集數據。 若是沒有定義主鍵,InnoDB會選擇一個惟一的非空索引代替,若是沒有這樣的索引,InnoDB會隱式定義一個主鍵來做爲聚簇索引。
InnoDB只彙集再同一個頁面中的記錄,包含相鄰鍵值的頁面可能會相距甚遠。
聚簇索引的一些優勢
聚簇索引的缺點
myIsam中主鍵和其餘索引在結構上並無什麼不一樣myisam索引結構圖
聚簇索引就是表,因此不想myISAM那樣須要獨立的行存儲
聚簇索引的每個葉子節點都包含了主鍵值、事務Id,用於事務和MVCC的回滾指針以及全部剩餘列
innodb的二級索引的葉子節點中存儲的不是行指針,而時主鍵值,並以此做爲指向行的指針。這樣的策略減小了出現行移動時或者數據頁分裂時二級索引的維護工做
Innodb表的主鍵分佈
聚簇索引和非聚簇表對比
最好避免隨機的聚簇索引
隨機寫入的缺點
隨機值載入聚簇索引以後,也須要作一次OPTIMIZE_TABLE 來重建表並優化頁的填充
若是一個索引包含全部查詢字段的值,咱們就稱之爲覆蓋索引
覆蓋索引帶來的好處
當發起一個被索引覆蓋的查詢時,在EXPLAIN的extra列能夠看到using index 的信息。
索引沒法發起覆蓋查詢的緣由
MYSQL 有郎中方式能夠生成有序結果
若是explain 出來的type列值爲index 則說明mysql使用了索引掃描來排序
掃描索引自己時很快的,由於只須要從一條索引記錄移動到緊接着的下一條記錄。但若是索引不能覆蓋查詢所需的所有列,那就不得不沒掃描一條索引記錄就都回表查詢一次對應的行。這基本上都是隨機I/O,所以按索引順序讀取數據的速度一般要比順序的全表掃描慢,尤爲時I/O密集性的工做負載
只有當索引列的順序和order by子句順序徹底一致時,而且全部列排序方向同樣時, mysql才能使用索引對結果作排序。
如通查詢須要關聯多張表,則只有當order by 子句引用的字段所有是第一個表示,才能使用索引作排序。
order by 子句和查找型查詢的限制是同樣的,須要知足索引最左前綴的要求,不然,mysql都須要執行排序操做,而不發使用索引排序
有一種狀況order by 子句能夠不知足索引的最左前綴的要求,就是前導列爲常量的時候,
InnoDB 只有在訪問行的時候纔會對其加鎖,而索引可以減小InnoDB訪問的次數,從而減小鎖的數量。 但這隻有當InnoDB在存儲引擎層可以過濾掉全部不須要的行時纔有效。
選擇索引和編寫利用這些索引查詢時
查詢優化、庫表優化、索引優化
查詢的生命週期大體能夠按照順序來看:從客戶端、服務器、而後在服務器上進行解析、生成執行計劃、執行、返回結果給客戶端。
其中執行能夠認爲是整個生命週期中最重要的階段,這其中包括愛了大量爲檢索數據到存儲引擎的調用以及調用後的數據處理、包括排序、分組。
在完成這些任務的時候查詢須要在不一樣地方花費時間,包括網絡、CPU計算、生成統計信息和執行計劃、鎖等待等操做。尤爲是像底層存儲引擎檢索數據的調用操做。這些調用須要在內存操做、cpu操做和內存不足時致使的I/O操做上消耗時間,根據存儲引擎不一樣,可能還會產生大量的上下文切換以及系統調用。
一、確認應用程序是否在檢索大量超過須要的數據
二、確認mySQL服務器是否在分析大量查過須要的數據行
訪問類型: 全表掃描 索引所秒 範圍掃描 惟一索引掃描 常數引用
通常mysql可以使用以下三種方式應用where條件
delete from messages where created < DATE.SUB(now(),INTERVASL 3 MONTH); rows_affected = 0 do { row_affected = do_query( "delete from messages where created < DATE_SUB(NOW(),INTERVAL 3 MONTH ) limit 1000") } while rows_affected > 0
優點
查詢執行路徑
半雙工通訊協議,意味着在任什麼時候刻,要麼是服務器想客戶端發送數據,要麼是由客戶端向服務器發送數據,這兩個動做不能同時發生。
也意味着,沒法進行流量控制。一旦一段開始發送消息,另外一端要接受完整消息才能響應它。
SHOW FULL PROCESSLIST
查看當mysql的狀態
這個階段包括:解析SQL,預處理,優化SQL執行計劃
mysql可以處理的優化類型
等價轉化成or語句複雜度爲O(N)
嵌套循環關聯
UNION爲例, mySQL首先將一系列的單個查詢結果放到一個臨時表中,而後從新讀出臨時表的數據完成union操做
當前mysql關聯執行的策略很簡單,mysql對任何關聯執行嵌套循環關聯操做,即mysql先在一個表中循環讀出單條數據,而後再嵌套循環到下一個表中尋找匹配的行。依次下去。
全外鏈接就沒法經過嵌套循環和回溯的方式完成,這是當發線關聯表中沒有找到任何匹配行的時候,則多是由於關聯時剛好從一個沒有任何匹配的表開始
mysql執行計劃老是一個左側深度優先的樹
排序是一個成本很高的左槽,從性能角度考慮,應該儘量避免排序或者儘量避免對大量數據進行排序。
當不能經過索引進行排序時,mysql須要本身進行排序,若是數據量小則再內存中進行,若是數據量大則須要使用磁盤,不過mysql將這個過程統一稱爲文件排序(file sort)
mysql將結果返回給客戶端是一個增量、逐步返回的過程
IN的查詢
例子
select * from sakila.file where film_id in ( select file_id from sakila.film_actor where actor_id = 1);
實際上mysql會將象關的外城表壓到子查詢,它認爲這樣能夠更高效率地查找到數據行
select * from sakila.file where exists(select * from sakila.film_actor where actor_id = 1 and film_actor.film_id = film.film_id);
能夠改寫這個查詢爲
select film.* from sakila.file INNER JOIN sakila.film_actor USING(film_id) where actor_id = 1;
使用IN()加子查詢性能可能回不好,因此一般建議使用exists()等效改寫查詢得到更好的效率
mysql沒法利用多核特性來並行執行查詢。儘管其餘不少關係型數據庫可以提供這個特性
mysql不遜於對同一張表同時進行查詢和更新
一般count()須要掃描大量的數據
「快速、精確和實現簡單」三者永遠只能知足其二