1. schema與數據類型優化
1.1 數據類型選擇
更小: 選擇不超過需求範圍的最小類型
更簡單
避免使用Null: 含有Null列會使索引,索引統計和值更爲複雜
分配空間: 根據實際須要分配.使用內存臨時表或操做時會比較糟糕
特殊類型:書中舉例,ip地址應該用無符號整數存儲,MySQL提供inet_aton, inet_ntoa方法轉換html
1.2 schema 設計陷阱
- 太多列,存儲引擎api須要在服務器層和存儲引擎層經過行緩衝格式拷貝數據,而後解碼.轉換代價依賴於列的數量
- 太多的關聯,單個查詢最好在12個表之內
- 最好不用枚舉enum
- 可使用其餘"空值"替代Null
1.3 範式和反範式
- 範式化: 每一個事實數據只出現一次
- 反範式化: 信息是冗餘的
- 第一範式1NF: 關係中的每一個屬性都不可再分
- 第二範式2NF: 每一個表中的非主屬性徹底依賴於碼(例如主鍵, 能夠惟一決定屬性集合)
- 第三範式3NF: 消除非主屬性之間的依賴關係,只保留非主屬性與碼的依賴關係
- 範式化優勢: 更新操做更快,佔用空間更小
- 範式化缺點: 表的關聯查詢更多
- 混用範式和反範式: 從父表冗餘一些數據到子表有利於排序, 緩存衍生值減小子查詢計算
1.4 緩存表和彙總表
業務上有時須要一張徹底獨立的彙總表或緩存表主要用於知足檢索的需求mysql
- Flexviews實現物化視圖,能夠增量從新計算物化視圖的內容
- 計數器表: 若是須要在表中保存計數器,更新計數器時,會有全局的互斥鎖.要獲的高併發更新的性能,能夠將計數器保存在多行,每次隨機選擇一個進行更新
1.5 修改表結構
- 大部分的alter table操做將致使服務中斷
- 全部的modify column 操做都將致使表重建
- 能夠新建一個.frm文件爲修改後的表結構,替換原來的.frm文件避免表重建
2. 建立高性能的索引
索引是存儲引擎用於快速查找記錄的數據結構sql
2.1 索引基礎
MySQL中,存儲引擎先在索引中找到對應值根據匹配的索引記錄找到對應的數據行.索引在存儲引擎層實現數據庫
2.1.1 B-Tree索引
- 目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構.
- InnoDB使用B+Tree, NDB集羣存儲引擎實際使用T-Tree. 索引對多個值的排序一句是根據表定義索引時列的順序.
- 索引對以下類型的查詢有效
- 全值匹配
- 匹配最左前綴,只使用索引第一列
- 匹配列前綴: 只匹配某一列值的開頭部分
- 匹配範圍值
- 精確匹配某一列並範圍匹配另一列
- 只訪問索引的查詢: 查詢只須要訪問索引無需訪問數據行(覆蓋掃描)
- 限制
- 若是不是按照索引的最左列開始,則沒法使用索引
- 不能跳過索引中的列,即定義索引(a,b,c),則使用a,c查詢條件時,只能使用索引第一列
- 若是查詢中有某個列的範圍查詢,則右邊全部的列都沒法使用索引優化查找
2.1.2 哈希索引
- 對於每一行數據,存儲引擎會對索引列計算一個哈希碼.索引包含哈希碼和行指針
- MySQL中只有Memory存儲引擎支持
- 限制
- 不能使用索引中的值避免讀取行
- 沒法用於排序
- 不支持部分索引列匹配查找
- 不支持範圍查詢
- 出現哈希衝突時會掃表
2.1.3 僞哈希索引
- 應用: 當存儲引擎不支持哈希索引,在B-Tree基礎上使用哈希值索引查找
- 場景: 須要存儲大量的url並根據url進行搜索查找
- 問題: 若是使用B-Tree存儲,內容很大
- 方案: 刪除url列索引,新增一個被索引的url_crc列,使用CRC32作哈希.
- sql: select id from t_url where url="https://www.google.as/" and url_crc=CRC32("https://www.google.as/")
- 結果: MySQL優化器會使用選擇性很高體積很小的基於url_crc索引完成查找
- 缺陷: 須要維護哈希值, crc32表大時會出現大量衝突
- 注意: 不要使用SHA1和MD5作哈希函數,由於計算出來的哈希值比較長; 必須在where子句中包含常量
- 插件: 移植自Percona Server的FNV64能夠在MySQL中做爲哈希函數使用
2.1.4 空間數據索引(R-Tree)
- MyISAM表支持空間索引,能夠用做地理數據存儲
2.1.5 全文索引
- 全文索引是一種特殊類型索引,查找文本中的關鍵詞,後續討論
2.1.6 其餘
- TokuDB 使用分形樹索引, 對於InnoDB的討論也適用於TokuDB
- ScaleDB 使用Patricia tries
- InfiniDB和Infobright使用一些特殊的數據結構優化某些特殊的查詢
2.2索引的優勢
- 減小掃描數據量
- 避免排序和臨時表
- 將隨機I/O變成順序I/O
- 對於TB級別的數據,常常會使用塊級別的數據技術來替代索引
2.3 高性能的索引策略
2.3.1 獨立的列
- 索引列不能是表達式一部分或函數參數,不然不能使用索引
2.3.2 前綴索引
- 場景: 須要索引很長的字符列,這會讓索引變得大且慢
- 方案: 根據業務找到最適合的前綴長度,建立前綴索引
- 優勢: 索引更小,更快
- 缺點: MySQL沒法使用前綴索引order by 和group by,也沒法使用前綴索引作覆蓋掃描
- 常見: 用16進制惟一id存儲session id,若是採用長度爲8的前綴索引能顯著提高性能
2.3.3 多列索引
- MySQL的索引合併(index merge)策略,必定程度上可使用多個單列索引定位
- where查詢條件的列最好使用多列索引而不是單獨列單獨索引
2.3.4 選擇合適的索引順序
- 將選擇性最高的列放在索引最前列
- 性能不僅依賴於全部索引列的選擇性,也和查詢條件的具體值分佈有關,可能須要根據運行頻率最高的查詢調整索引列順序
- 有時須要根據排序,分組和範圍條件綜合考慮
2.3.5 聚簇索引
- 聚簇索引並非單獨的索引類型,而是一種數據存儲方式.葉子頁包含行的所有數據,節點頁只包含索引列
- InnoDB默認使用主鍵彙集數據,若是沒有會選擇一個惟一的非空索引做爲聚簇索引
- 能夠把相關數據保存一塊兒,減小磁盤I/O
- 數據訪問更快
- 插入速度嚴重依賴插入順序
- 更新聚簇索引代價高
- 可能面臨"頁分裂"問題,致使佔用更多磁盤空間
- 可能致使全表掃描變慢,因爲行比較稀疏或頁分裂致使數據存儲不連續
- 二級索引變大,二級索引訪問須要兩次索引查找
2.3.6 覆蓋索引
- 若是一個索引包含全部須要查詢的字段的值
- 覆蓋索引必需要存儲索引列的值
- MySQL不能在索引中執行like 操做,但能在索引中作最左前綴匹配的like比較,由於能夠轉換爲簡單的比較操做
- 延遲關聯: 先經過覆蓋索引返回須要的主鍵再經過這些主鍵關聯原表得到須要的行
2.3.7 使用索引掃描作排序
- 只有索引列的順序和order by 子句的順序一致時才能使用索引對結果作排序
- 若是關聯多表,則只有當order by子句引用的字段所有爲第一個表時才能使用索引排序
2.3.8 壓縮索引
- MyISAM使用前綴壓縮減小索引的大小
- create table 時經過指定pack_keys控制壓縮方式
2.3.9 未使用的索引
- 經過查詢INFORMATION_SCHEMA.INDEX_STATISTICES能查到每一個索引的使用頻率
- 冗餘和重複的索引會下降性能
2.3.10 索引和鎖
- InnoDB在二級索引上使用共享(讀)鎖,但訪問主鍵索引須要排他(寫)鎖,消除了使用覆蓋索引的可能
2.4 維護索引和表
- check table 一般可以找出大多數的表和索引的錯誤
- MyISAM表易損壞,InnoDB不容易,損壞多是硬件或人爲錯誤
- MySQL查詢優化器經過兩個api,一個獲取範圍大概數據量,一個返回各類類型的數據包括索引基數
- 減小索引和數據的碎片,B-TREE索引可能碎片化,這會下降效率