mysql 優化mysql
核心且經常使用的字段,宜建成定長放在一個表中 而varchar、text、blob等變長類型的字段,適合放在另外的表中,用主鍵進行關聯
根據場景將不經常使用字段單拆出來
int > date、time > enum、char > varchar > blob、text 好比:tinyint和char(1),從空間上講,都佔一個字節,可是考慮到order by的時候,字符串要考慮字符集和校對集(就是排序規則) time : 定長、運算快 enum : 內部採用整形存儲 varchar : 不定長,要考慮字符集的轉換和排序時的校對集 text/blob : 沒法使用內存臨時表(排序等操做只能在磁盤上進行)
以年齡爲例,tinyint unsigned not null,能夠存儲255歲,足夠 以varchar(10)和varchar(300)爲例,雖然存的內容是同樣的,可是在表聯查時,varchar(300)須要花更多內存
NULL不利於索引,要用特殊的字符標識
索引能提升查詢、排序、分組統計的速度
myisam、innodb中都默認採用b-tree索引正則表達式
在memory引擎裏默認是hash索引算法
hash索引的問題: ① hash的結果隨機,若是是在磁盤上放數據,位置隨機性比較大 ② 沒法對範圍查詢優化 ③ 沒法引用前綴索引,好比在b-tree中,某列上「helloworld」能夠加索引查詢,則"hello"也能夠加索引查詢,可是hash索引就不行 ④ 排序也沒法優化 ⑥ 必須回行,也就是說,經過索引拿到數據位置必須回到數據表中取數據
例如:where cat_id = 3 and price > 50 誤:cat_id和price列上都加索引 緣由:只能用上cat_id或者price的索引,由於是獨立的索引,只能用上一個
誤:要知足左前綴規則 例如:a-b-c三列加聯合索引 where a = 1 and b > 7 and c = 8 a索引能用、b索引能用、c索引用不到 where a = 1 and b like 'aa%' and c = 10 a索引能用、b索引能用、c索引用不到
例:假設某個表有一個聯合索引(c1,c2,c3,c4)如下——只能使用該聯合索引的c1,c2,c3部分 A. where c1=1 and c2=2 and c4>3 and c3=4 B. where c1=1 and c2=2 and c4=3 order by c3 C. where c1=1 and c4= 2 group by c3, c2 D. where c1=1 and c4=2 order by c2, c3 E. where c1=1 and c2=2 and c4=3 order by c2, c3 解答: A. 因爲該語句會被mysql優化成 where c1=x and c2=x and c3=x and c4>x,因此全部索引均可以用到 B. c一、c2上的索引用於查詢,c3上的索引用於排序,c4的索引用不到 C. c1上的索引用於查詢,c二、c3因爲順序反了,因此索引不能用於分組,c4上的索引也用不到 D. c1上的索引用於查詢,c二、c3上的索引用於排序,c4上的索引用不到 E. c一、c2上的索引用於查詢,c3上的索引用於排序,c4上的索引用不到(c2上的查詢條件已經使用了等號,說明被嚴格限制,排序是沒必要要的,因此mysql會優化掉後面的 order by c2) 實踐: 準備數據 create table t( c1 tinyint(1) not null default 0, c2 tinyint(1) not null default 0, c3 tinyint(1) not null default 0, c4 tinyint(1) not null default 0, c5 tinyint(1) not null default 0, index c1234(c1, c2, c3, c4) ); insert into t values (1, 3, 5, 6, 7), (2, 3, 9, 8, 3), (4, 3, 2, 7, 5); A 的執行計劃 mysql> explain select * from t where c1=1 and c2=2 and c4>3 and c3=4 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: range possible_keys: c1234 key: c1234 key_len: 4 ref: NULL rows: 1 filtered: 100.00 Extra: Using index condition 建表的時候每列是1個長度,執行計劃裏key_len:4說明用到了4個字段上的索引,加起來是4 B 的執行計劃 mysql> explain select * from t where c1=1 and c2=2 and c4=3 order by c3\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: ref possible_keys: c1234 key: c1234 key_len: 2 ref: const,const rows: 1 filtered: 33.33 Extra: Using index condition C 的執行計劃 mysql> explain select c3, c4 from t where c1=1 and c4= 2 group by c3, c2 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: ref possible_keys: c1234 key: c1234 key_len: 1 ref: const rows: 1 filtered: 33.33 Extra: Using where; Using index; Using temporary; Using filesort D 的執行計劃 mysql> explain select * from t where c1=1 and c4=2 order by c2, c3 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: ref possible_keys: c1234 key: c1234 key_len: 1 ref: const rows: 1 filtered: 33.33 Extra: Using index condition E 的執行計劃 mysql> explain select * from t where c1=1 and c2=2 and c4=3 order by c2, c3 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: ref possible_keys: c1234 key: c1234 key_len: 2 ref: const,const rows: 1 filtered: 33.33 Extra: Using index condition
myisam 的數據和索引是分開的,都是一個單獨的文件,查找數據的時候先在索引樹上找,找到後再到數據上拿數據。這就是非聚簇索引 myisam 主索引和次級索引都指向行在磁盤上的位置
innodb索引的葉子節點比較大,上面有索引對應的整條記錄,因此查找數據的時候找到了索引後立馬能拿到對應的數據,不用再回行到數據文件去拿數據。這就是聚簇索引 innodb 主索引直接存該行文件,次級索引指向主鍵的引用 innodb 若是沒有主鍵(primary key),則會用unique key作主鍵,若是沒有unique key,則系統生成一個內存的rowid作主鍵 聚簇索引 優點:根據主鍵索引查詢條目比較少時,不用回行 劣勢:不規則數據插入時,形成頻繁的頁分裂
頁分裂 聚簇索引採用的是平衡二叉樹算法,並且每一個節點都保存了該主鍵所對應行的數據,假設插入數據的主鍵是自增加的,那麼根據二叉樹算法會很快的把該數據添加到某個節點下,而其餘的節點不用動;可是若是插入的是不規則的數據,那麼每次插入都會改變二叉樹以前的數據狀態。從而致使了頁分裂
若是查詢的列剛好是索引的一部分,那麼查詢只須要在索引文件上進行,不須要回行到磁盤再查找數據。這種查詢速度很是快,稱爲索引覆蓋
實例分析: create table A( id varchar(64) primary key, ver int, ... index idx_id_ver ) 1000條數據,表有幾個很大的字段——text(3000) 爲何 select id from A order by id 比較慢,而 select id from A order by id, ver比較快? 思路:inndb聚簇索引和myisam索引的不一樣,索引覆蓋 分析: 1. myisam在兩個索引上查數據的時候因爲索引覆蓋,速度不會有明顯差別 2. innodb表由於聚簇索引,主鍵索引要在磁盤上跨多個塊,致使速度慢 3. 即便innodb引擎,若是沒有那幾個大的字段,兩個語句的查詢速度也不會有明顯差別
針對列中的值,從左往右截取部分來建索引 ① 截的越短,重複讀越高,區分度越小,索引效果越很差 ② 截的越長,重複讀越低,區分度越高,索引效果越好,但帶來的影響越大——增刪改變慢,而且影響查詢速度 因此要在區分度和長度上取得一個平衡。慣用手法:截取不一樣長度,並測試其區分度 區分度公式:count(distinct col)/count(*) 通常要求join的字段上索引的區分度在0.1以上 對dict表的word字段的前N個字符加索引時的區分度統計sql: mysql> select (select count(distinct left(word, 3) from dict ) / (select count(*) from dict))
如url列: http://www.baidu.com http://www.zixue.it ① 能夠把內容倒置,這樣區分度高 ② 僞hash索引效果 在存url的時候同時存url的crc32值,crc32是一種哈希算法,能把字符串算爲32位整數,這時候對urlcrc作索引的效率會很是高 id | url | urlcrc ----|------|---- foo | foo | foo
多列索引的考慮因素:①列的查詢效率 ②列的區分度 ③實際業務場景 有時候某列A的分區度比較高,可是實際的查詢場景中另外一列B在查詢條件中要優先於A,這時候就不能只考慮區分度,而要結合實際場景
1. 對於覆蓋索引,直接在索引上查詢時就是有序的,using index 在innodb引擎中,沿着索引字段排序,是天然有序的 對於myisam引擎,若是按某索引字段排序,如id,但取出來的字段中,有未索引字段,它的作法不是 【索引->回行 …… 索引 -> 回行】,而是先取出全部行再進行排序 2. 先取出數據,造成臨時表作filesort(文件排序,文件可能在磁盤,也可能在內存中)
重複索引:在同一個列或者在順序相同的幾個列上創建索引。重複索引沒有任何用處,只會增大索引文件、拖慢更新速度,應該去掉 冗餘索引:指兩個索引覆蓋的列有重疊 例如文章與標籤表: id | artid | tag ----|--------|---- 1 | 1 | PHP 2 | 1 | JAVA 3 | 2 | MySql 4 | 2 | Oracle 在實際使用中,有 tag -> atrid 和 atrid ->tag 的查詢: select tag from tt where artid = 1; select artid from tt where tag = 'PHP'; 索引:這時候能夠分別給tag和artid單獨創建索引。可是查詢的時候會產生回行 優化:分別創建tag_artid和artid_tag兩個聯合索引,達到索引覆蓋的目的,using index
在長期的數據更改過程當中,索引文件和數據文件將產生空洞,造成碎片。咱們能夠經過nop操做來修改表 好比:表的引擎爲innodb,alter table xxx engine innodb optimize table xxx,也能夠修復 注意:修復表的數據及索引碎片,就會把全部數據文件從新整理一遍,使之對齊,這個過程,若是數據量比較大,也是很耗費資源的操做。若是一個表的update比較頻繁,能夠按周/月來修復,若是不頻繁,能夠更長的週期來修復
1. 索引列上不能使用表達式或函數 2. 前綴索引和索引列的選擇性 mysql中b-tree引擎對索引的鍵值大小是有限制的,innodb索引鍵的大小不能超過767字節,myisam索引鍵的大小不能超過1000字節。因此比較長的字符串列上創建的索引可能會比較大,進而影響到查詢效率,因此mysql支持對字符串的前綴創建索引 eg: CREATE INDEX idx_name ON tb (col_name(n)) 3. 聯合索引如何選擇索引列的順序 ① 常常會被使用的列優先 ② 區分度高的列優先 ③ 寬度小的列優先(IO小,效率高) 4. 覆蓋索引:包含了全部須要查詢的列的索引 覆蓋索引優勢: ① 優化緩存,減小磁盤io ② 減小隨機io,變隨機io操做爲順序io操做 ③ 避免innodb索引回行 ④ 避免myisam索引進行系統調用 注:innodb二級索引會自動加入主鍵,如下這種查詢會使用到索引覆蓋 mysql> explain select actor_id, last_name from actor where last_name = 'joe' \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ref possible_keys: idx_actor_last_name key: idx_actor_last_name key_len: 137 ref: const rows: 1 filtered: 100.00 Extra: Using index 5. 模擬hash索引 eg: 給 film 表中格式爲varchar(255)的 title 字段新增一個hash列 title_crc32,能夠給title_crc32加索引進行查詢 缺點:只能處理鍵值全值匹配的查詢
1. 使用索引來優化排序 ① 索引列的順序和order by子句順序徹底一致 ② 索引中全部列的方向(升序、降序)和order by子句徹底一致 ③ order by中的字段所有在關聯表中的第一張表中 2. 索引優化鎖 eg: select * from emp where emp_no = '111' for update; --排它鎖 這個sql在 emp_no 上沒有索引的時候會鎖整個表,而加上索引只鎖定當前行
查詢快 —— 索引(聯合索引的區分度、長度) 取的快 —— 索引覆蓋 傳的少 —— 傳輸更少的行和列 切分查詢 —— 例:查1000行,沒100條做爲一個單位 分解查詢 —— 按邏輯把多表關聯查詢分紅多個簡單的sql
1. statement(基於段的格式) 日質量相對較小,可是容易引發主備複製的數據不一致 2. row(基於行的日誌格式) 基於行的日誌在數據發生變更的時候會記錄全部受影響行的數據修改,基於段的格式只記錄該條語句。 所以,在主備複製的時候row只複製一行隻影響一行,而statement的執行會持續比較長的時間,因此row格式的複製效率會更高 並且記錄的日質量比較大 能夠修改參數:binlog_row_image = [FULL | MINIMAL | NOBLOB] 3. mix(集statement和row) 根據sql語句肯定使用哪一種格式
====================================================================================================================================================sql
--TYPE--------------------------------------------------------------------------------------------------------
system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,這個也能夠忽略不計數據庫
const:表示經過索引一次就找到了,const用於比較primary key或者unique索引緩存
由於只匹配一行數據,因此很快,若是將主鍵置於where列表中,mysql就能將該查詢轉換爲一個const eg:explain select * from (select * from a where id = 1) t
eq_ref:惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或惟一索引掃描session
ref:非惟一索引掃描,返回匹配某個單獨值的全部行。本質上也是一種索引訪問,它返回全部匹配某個單獨值的行mysql優化
然而,它可能會找到多個符合條件的行,因此它應該屬於查找和掃描的混合體
range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引併發
通常就是在where語句中出現between、<、>、in等的查詢 這種範圍掃描比全表掃描要好,由於它只須要開始於索引的某一點,而結束於另外一點,不用掃描所有索引
index:full index scan,index與all區別爲index類型只遍歷索引樹。這一般比all快,由於索引文件一般比數據文件小函數
也就是說,all和index都是讀全表,可是index是從索引中讀取,而all是從硬盤讀取
all:full table scan,將遍歷全表以找到匹配的行
--KEY_LEN--------------------------------------------------------------------------------------------------------
表示索引中使用的字節數,能夠經過該列計算查詢中使用的索引的長度。在不損失精度的狀況下,長度越短越好
key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度,即key_len是根據標定義計算而得,不是經過表內檢查出的
--REF------------------------------------------------------------------------------------------------------------
顯示索引的哪一列被使用了,若是可能的話,是一個常數。哪些列或常量被用於查找索引列上的值
--EXTRA----------------------------------------------------------------------------------------------------------
using filesort:說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取
mysql中沒法利用索引完成的排序操做稱爲「文件排序」 eg:create table t1(col1 varchar(20), col2 varchar(20), col3 varchar(20), key col1_col2_col3(col1, col2, col3)) explain select col1 from t1 where col1 = 'ac' order by col3 優化:select col1 from t1 where col1 = 'ac' order by col2, col3
using temporary:使用了臨時表保存中間結果,mysql在對查詢結果排序時使用臨時表
常見於order by和分組查詢group by eg:create table t1(col1 varchar(20), col2 varchar(20), col3 varchar(20), key col1_col2(col1, col2)) explain select col1 from t1 where col1 in ('aa', 'bb', 'cc') group by col2 優化:select col1 from t1 where col1 in ('aa', 'bb', 'cc') group by col1, col2
using index:表示相應的select操做中使用了覆蓋索引,避免了訪問表的數據行,效率不錯
若是同時出現using where,代表索引被用來執行索引鍵值的查找 若是沒有同時出現using where,代表索引用來讀取數據而非執行查找動做
using where:使用了where進行過濾
using join buffer:使用了鏈接buffer
impossible where:where子句的值老是false,不能用來獲取元組
range類型查詢字段後面的索引無效
left join條件用於肯定如何從右表搜索行,左邊必定都有,因此右邊是關鍵點,必定須要創建索引
小表驅動大表
在沒法保證被驅動表的join條件字段被索引且內存充足的狀況下,不要吝嗇JoinBuffer的設置
--索引失效-------------------------------------------------------------------------------------------------------
1.全值匹配我最愛
2.最佳左前綴法則
3.不在索引列上作任何操做(計算、函數、(自動or手動)類型轉換),會致使索引失效而轉向全表掃描
4.存儲引擎不能使用範圍條件右邊的列
5.儘可能使用索引覆蓋,減小select *
6.mysql在使用不等於(!=、<>)的時候沒法使用索引致使全表掃描
7.is null,is not null也沒法使用索引
8.like以通配符開頭,mysql索引失效會全表掃描。通常採用索引覆蓋的方式提升'%xxx'的查詢效率
9.字符串不加單引號索引失效
10.少用or,用它來鏈接時索引會失效
--order by 優化--------------------------------------------------------------------------------------------------
using file sort的時候會有兩種排序策略
mysql4.1之前是雙路排序,讀取行指針和order by列,而後掃描已經排好序的列表從新讀取須要的數據輸出(即:從磁盤讀取排序字段,在buffer排序,再從磁盤取出其餘字段) 單路排序。從磁盤讀取全部須要的列,按照order by列在buffer上排序,而後掃描排序後的列表輸出 優缺點:單路排序避免了第二次讀取數據,而且把隨機io變成順序io,可是它會使用更多的空間,糟糕的是,若是數據量太大,超出sort_buffer,則須要屢次進行數據讀取,會致使更大的開銷 優化:嘗試增大sort_buffer_size、max_length_for_sort_data
--group by 優化--------------------------------------------------------------------------------------------------
--慢查詢---------------------------------------------------------------------------------------------------------
long_query_time
show variables like '%slow_query_log%'
set global slow_query_log = 1 須要從新鏈接或者新開會話才能看到設置,針對當前數據庫,重啓數據庫後失效。永久生效要改my.conf文件中的slow_query_log、slow_query_log_file
show global status like '%slow_queries%'
mysqldumpslow:
s:按照何種方式排序 c:訪問次數 l:鎖定時間 r:返回記錄 t:查詢時間 al:平均鎖定時間 ar:平均返回記錄數 at:平均查詢時間 t:返回前面多少條的數據 g:後面搭配正則表達式匹配模式 獲得返回記錄集最多的10個sql: mysqldumpslow -s r -t 10 /slowlog.log 獲得訪問次數最多的10個sql: mysqldumpslow -s c -t 10 /slowlog.log 獲得按照時間排序的前10條裏面含有左連接的sql: mysqldumpslow -s t -t 10 -g "left join" /slowlog.log 另外,建議在使用一些美麗時結合|和more使用,不然可能出現爆屏狀況: mysqldumpslow -s r -t 10 /slowlog.log | more
--show profile---------------------------------------------------------------------------------------------------
show variables like 'profiling'
set profiling = ON
show profile cpu, block io for query query_id
type: all block io context switches cpu ipc 發送和接收相關開銷信息 memory page faults 頁面錯誤相關開銷 source swaps 交換次數相關開銷 若是show profile結果出現以下四種結果,說明該sql存在問題比較大: converting HEAP to MyISAM:查詢結果太大,內存都不夠用了往磁盤上搬 creating tmp table:建立臨時表(拷貝數據到臨時表,用完再刪除) copying to tmp table on disk:把內存中臨時表複製到磁盤,危險!!! locked:
--mysql 鎖-------------------------------------------------------------------------------------------------------
表鎖:偏向myisam
(加了讀鎖以後本身只能讀不能插改,也不能查詢其餘沒有鎖定的表,會報錯,其餘session也只能讀,插改的時候會阻塞) (加了寫鎖以後本身能夠進行任何操做,可是不能操做其餘沒有鎖定的表,會報錯,其餘session任何對當前表的操做都會被阻塞) 簡而言之,讀鎖只阻塞寫,寫鎖會阻塞讀寫 eg:lock mytable lock read; show open tables; 查看哪些表被鎖了 show status like 'table%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Table_locks_immediate | 162 | | Table_locks_waited | 0 | Table_locks_waited:出現表級鎖定爭用而發生的等待次數,此值高則說明存在着比較嚴重的表級鎖爭用狀況 | Table_open_cache_hits | 0 | | Table_open_cache_misses | 0 | | Table_open_cache_overflows | 0 | +----------------------------+-------+ 此外,myisam是讀寫鎖調度是寫優先,因此myisam不適合作寫爲主的表引擎
行鎖:偏向innodb引擎。開銷大加鎖慢,會出現死鎖,粒度最小,衝突機率低,併發高
innodb和myisam的最大不一樣點:一是支持事務,二是支持行級鎖 無索引行鎖升級爲表鎖
間隙鎖:當用範圍條件而不是相等條件檢索數據,並請求共享或者排它鎖時,innodb會給符合條件的已有數據記錄的索引項加行鎖
對於鍵值在條件範圍內但並不存在的記錄,叫作間隙,該間隙也會給鎖定 某些場景下會對系統性能形成很大傷害
表的讀取順序數據讀取操做的操做類型哪些索引可使用哪些索引被實際使用表以前的引用每張表有多少行被優化器查詢