以下是Xmind筆記,看着有些費勁。。。記錄下
高性能Mysql
mysql架構和基礎
mysql邏輯架構圖
客戶端
服務器層
連接/線程處理、查詢緩存、解析器、優化器
存儲引擎
鎖粒度
表鎖:服務器會爲諸如 alter Table之類語句使用表鎖,而忽略存儲引擎的鎖機制
行級鎖:只在存儲引擎層,不在服務器層
事務
ACID
atomicity 原子性
consistency 一致性
isolation 隔離性
durability 持久性
隔離級別
Read uncommitted
Read committed
Repeatable read (mysql默認)
Serializable 可串行化
死鎖
InnoDB 處理方法:將持有最少行級排它鎖的事務進行回滾
服務器層不管理事務,是由下層的存儲引擎實現的。 最好不要在事務中混合使用存儲引擎(InnoDB-事務型、MyIsAM-非事務型)
MVCC 多版本併發控制
在每條記錄後保留2條隱藏的列:行版本號、行刪除版本號。每開始一個事務,系統版本號自動遞增。Select:只查 【 行版本號<=當前系統版本號 && 行刪除版本要麼未定義,要麼大於當前系統版本號--確保未被刪除】。
存儲引擎
InnoDB (mysql默認)
事務型、高併發 間隙鎖、聚簇索引、熱備份
MyIsAM
不支持事務(沒有回滾)、不支持行級鎖、不支持崩潰後的安全恢復
對整張表加鎖
對於只讀數據可以考慮
導入導出
Create table a Like b; Insert into a Select * from b;
Schema與數據類型優化
數據類型
整數類型: -2^(N-1)到2^(N-1)-1. Unsigned。INT(11) 指定寬度,只是規定了交互工具(mysql命令行客戶端)來顯示字符的個數,值的合法範圍,比如INT(1)和INT(20)是相同
實數類型:DECIMAL ,數據量大時也可以用BIGINT代替
字符串類型
VARCHAR 可變長字符串、CHAR 定長,括號裏的是字符數
ENUM 枚舉代替字符串類型
日期和時間類型:TIMESTAMP(1970年-2038年) 效率更高、DATATIME(1001年-9999年)
IPv4地址:32位無符號整數,不是字符串
範式和反範式
第一範式:無重複的列
第二範式:屬性完全依賴於主鍵
第三範式:屬性不依賴於其他表的非主鍵
反範式可以避免表關聯,所以一般是 混用範式和反範式
緩存表與彙總表
緩存表:存儲從其他表獲取速度慢的信息,例如 邏輯上冗餘的數據
彙總表:保存 Group By語句聚合數據,例如 邏輯上不冗餘的數據
總結
一切爲了 更快地讀,更慢地寫
儘量使用可以正確存儲數據的最小數據類型
儘量 NOT NULL
索引
索引類型
BTREE,InnoDB使用B+Tree,通過比較節點頁的值和要查找的值找到合適的指針進入下層子節點(二分查找),有序的
哈希索引:對索引列計算一個哈希碼
InnoDB有 自定義哈希索引:當注意到某些索引列被使用得非常頻繁時,它會在內存中基於BTree索引之上再創建一個哈希索引,自動的內部行爲
創建自定義哈希索引
新增一個被索引的url_crc 列
Select id From url Where url_crc=CRC32("http://myslq.com") And url="http://myslq.com";
無法排序、不支持部分索引列匹配查找、不支持範圍查詢
索引策略
獨立的列:索引列不能是表達式的一部分,也不能是函數的參數
前綴索引
索引開始的部分字符,提高索引效率。對於BLOB、Text、很長的VARCHAR,必須使用前綴索引
索引的選擇性:不重複的索引值(基數)/數據表的記錄總數(#T),1/#T到1之間,例如 唯一索引的選擇性=1 --P154
無法Order By 和Group By,無法覆蓋掃描
多列索引
在多個列上建立獨立的單列索引大部分情況下不能提高mysql的查詢性能
Or -> UNION ALL
選擇合適的索引列順序:當不需要考慮排序和分組時,將選擇性最高的列放在前面通常是最好的
聚簇索引 (InnoDB)
數據存儲方式,表示數據行和相鄰的鍵值緊湊地存儲在一起。一般 一個表只能有一個聚簇索引
葉子頁包含了行的全部數據,但是節點頁只包含了索引列
缺點: 二級索引訪問需要倆次索引查找。 存儲引擎需要找到二級索引的葉子節點獲得對應的主鍵列,然後根據這個值取聚簇索引中查找到對應的行
聚簇索引:主鍵索引,非聚簇索引:二級索引 --P167
主鍵索引:Auto_Increment
併發插入 可能導致間隙鎖競爭 ,innodb_autonic_lock_mode配置=0:鎖,保證ID連續; >=1: 性能提升,ID可能不連續
覆蓋索引
定義:一個索引包含所有需要查詢的字段的值
由於InnoDB的聚簇索引,覆蓋索引對InnoDB表非常有用:若二級主鍵能覆蓋查詢,則可以避免對主鍵索引的二次查詢。
延遲關聯: P174、P187
Select name,sex From profiles Where sex='M' Order By rating Limit 100000,10
轉化爲 Select name,sex From profiles Inner Join( Select id From profiles where x.sex='M' Order By rating Limit 100000,10) As x Using(id);
索引掃描來做排序
EXPLAIN出來的type列:"index"
若索引不能覆蓋查詢所需的列,按索引順序讀取的速度通常要慢於順序全表掃描,因爲 每掃描一條索引都得回表查詢
OrderBy 索引最左前綴
查詢需要關聯多表,則只當OrderBy子句所引用的字段全部爲第一個表時,才能使索引用作排序
總結
BTree索引的限制
最左前綴
不能跳過索引的列
若查詢中有某個列的範圍查詢,則其右邊所有列都無法使用索引優化查找
mysql 不能在索引中執行LIKE操作
只在索引中做最左前綴匹配的LIKE比較,若是通配符開頭的LIKE查詢,將無法匹配
儘量擴展已有索引而不是創建新索引,但有時出於性能考慮需要冗餘索引
id > 45 轉化爲 id IN (1,4,99) :範圍條件查詢,就無法使用範圍列後面的其他索引列,但是對於多個等值查詢就可以
查詢性能優化
慢查詢
是否向數據庫請求了不需要的數據? LIMIT、只取需要的列
是否在掃描額外的記錄?
“Extra: Using Where”: Mysql將通過Where條件來篩選存儲引擎返回的記錄
索引中使用where 是在存儲引擎層完成
索引覆蓋掃描(Extra:Using index)直接從索引中過濾不需要的記錄並返回命中結果,無須再回表查詢,Mysql服務器層完成
Using Where 需要先從數據表讀出記錄然後過濾,Mysql服務器層完成
重構查詢方式
大查詢分解成多個小查詢
分解關聯查詢
讓緩存的效率更高:關聯中的某個表變化,就無法使用查詢緩存,
拆分後,某個表很少變化可重複利用查詢緩存結果
查詢的過程 -圖P204
mysql客戶端/服務端通信協議
半雙工,要麼服務器向客戶端發數據,要麼客戶端到服務器, 倆個動作不能同時發生
庫函數實際是從緩存獲取數據
查詢狀態 -P207
查詢緩存
查詢優化器
靜態優化:直接對解析樹分析優化,第一次完成後一直有效,編譯時優化
Or -> IN()先對列表內數據排序,再二分查找,O(log N)優於O(N)
動態優化
關聯查詢 :嵌套循環關聯
先在一個表循環取出單條數據,再嵌套循環到下一個表尋找匹配的行,直到找到所有表的匹配的行爲止,然後回溯到上一個表
關聯查詢優化器 評估不同順序的成本來選一個代價最小的關聯順序
排序優化 不能用索引生成排序結果時,需自己排序(filesort)
倆次傳輸排序 , I/O成本高
單次傳輸排序,當查詢需要所有列的總長度不超過參數
max_length_for_sort_data ,Mysql使用此排序算法。
查詢執行引擎
逐步執行計劃
返回結果給客戶端
增量、逐步返回的過程
查詢優化器的侷限性
糟糕:Where條件中包含IN()的子查詢語句
Select * From film Where film_id IN(
Select film_id From film_actor Where actor_id = 1)
轉化爲
Select film.* From film inner Join film_actor Using(film_id) Where actor_id = 1
UNION
Union的各個子句中分別使用這些 Order By、Limit
優化特定類型查詢
Count
count(列值):不統計列值NULL
最好用Count(*)
優化關聯查詢
確保Group By和Order By只涉及到一個表中的列,這樣索引纔可以優化
優化子查詢
儘量用關聯查詢代替
優化Limit 分頁
延遲關聯 -見上一章
優化UNION查詢
創建並填充臨時表來執行Union查詢; 儘量用Union All,否則會對整個臨時表做唯一性檢查,代價高
使用用戶自定義變量
Set @rownum := 0;
Select id,@rownum := @rownum + 1 AS rownum From actor
總結
分組查詢:若可以,在應用程序中做超級聚合更好
快速地完成事情:儘量使用Update 代替先Select For Update再Update的寫法,因爲事務提交的越快,持有鎖的時間越短
儘量少做事:看是否真的需要這麼精確的計算,可以運用簡單的方案過濾大多數數據
高級特性
分區表 (粗粒度)
定義:是一個獨立的邏輯表,但是底層由多個物理子表組成。底層表必須相同存儲引擎
CREATE TABLE sales(
order_date DATETIME NOT NULL,
-- other columns omitted
)ENGIN=InnoDB PARTITION BY RANG(Year(order_date))(
PARTITION p_2010 values less than (2010),
PARTITION p_2010 values less than (2010),
PARTITION p_2010 values less than (2010),
PARTITION p_catchall values less than MAXVALUE );,
表達式返回的值是一個確定的整數,且不能是常數
策略
全量掃描數據,不要任何索引(根據分區的規則大致定位需要的數據位置)
索引數據,並分離熱點 (數據有明顯的熱點,可以單獨放一個分區)
可能的問題
Null值會使分區過濾無效
PARTITION BY RANGE COLUMNS(order_date)
--直接使用列本身而不是基於列的函數,mysql5.5版本
儘量分區列和索引列匹配
限制分區的數量,一般最多100個左右分區
總結
要做Where條件帶入分區列!!!
根據粗粒度索引優勢,通過分區過濾通常可以讓查詢掃描更少的數據。
視圖
本身是一個虛擬表,不存放任何數據,返回的數據是從其他表中生成的
CREATE VIEW Oceania AS
Select * from country where continent = 'Oceania'
With CHECK OPTION;
合併算法、臨時表算法(包含Group By、Distinct、聚合函數、Union、子查詢)
不支持在視圖上建觸發器
只有合併算法的視圖 纔可更新。
(update Oceania Set population = popluation * 1.1 Where Name='Australia')
存儲過程
適用於: 相比查詢執行的成本,解析和網絡開銷很明顯
觸發器
每個表的每個事件,最多隻能定義一個觸發器
基於行的觸發
作用: 實現更新反範式化數據、記錄數據變更日誌、系統維護任務、約束
事件
指定mysql 在某個時候執行一段代碼,或者每隔一段時間間隔執行一段代碼
綁定變量
SET @sql := 'Select id,first_name FROM actor where first_name=?';
客戶端向服務器發送一個 sql 語句原型,服務器解析並存儲這個 sql 部分執行計劃,返回客戶端處理句柄。以後每次執行這類查詢,客戶端都指定使用這個句柄。
字符集和校對規則
一種從二進制編碼到字符的映射; 校對: 用於字符集的排序規則
服務器 -> 數據庫 -> 表,這是一個逐級繼承的默認設置,最靠底層的默認設置影響
大小寫敏感:_cs 、_ci (case sensitive; case insensitive);
字符串編碼的二進制:_bin
col2 CHAR(1) CHARSET utf8,
col3 CHAR(1) COLLATE latin1_bin
只有排序查詢要求的字符集與服務器數據的字符集相同的時候,才能使用索引進行排序
全文索引 (B-Tree)
通過關鍵字匹配來進行查詢過濾,基於相似度的查詢
只有MyISAM 支持全文索引,msql5.6版本後,InnoDB 也支持了
自然語言的全文索引
在整個索引中出現次數越少的詞語,匹配時的相關度就越高
SELECT film_id,title, RIGHT(description,25)
From film_text
Where MATCH(title,description) AGAINST('factory casu');
自動按照相似度進行排序
布爾全文索引
SELECT film_id,title, RIGHT(description,25)
From film_text
Where MATCH(title,description) AGAINST('+factory +casu' IN BOOLEAN MODE);
只有MyISAM 引擎才能使用
查詢緩存
定義:緩存完整的select 查詢結果
默認應該關閉查詢緩存;
若實在查詢緩存作用很大,也要配置很小的查詢緩存空間(幾十兆)
若查詢語句包含任何不確定的函數,那麼查詢緩存不能找到緩存結果。如:current_date
命中和寫入的比率,Qcache_hits/Qcache_inserts >3:1 通常查詢緩存是有效的;即緩存帶來的資源節約大於其本身的資源消耗;
Flush query cache: 碎片管理。小心用,因爲無法訪問查詢緩存,服務器會僵死一段時間。
query_cache_size 設置成0, 來關閉查詢緩存。
總結:
對於寫密集型應用,直接禁用 可以提高系統性能