mysql 良好的安全鏈接,自帶查詢解析,sql語句優化,使用讀寫鎖(細化到行),事務隔離和多版本併發控制提升併發,完備的事務日誌記錄,強大的存儲引擎提供高效查詢(表記錄可達百萬級)。若是是innerdb還可在崩潰後進行完整的恢復。優勢很是多,可是仍是須要優化mysql
1,SQL優化的通常步棸算法
1.1,查看sql執行頻率sql
show status like 'Com_%' ; // Com_select :執行select操做的次數,一次累計加1。其餘相似數據庫
如下只針對InnoDB存儲引擎,累加算法略有不一樣 show status like 'innodb_rows_%' windows
Innodb_rows_read:select 查詢操做插入的行數安全
Innodb_rows_inserted/updated/deleted:執行insert/update/delete 操做的行數服務器
經過以上的參數,能夠了解數據庫應用是以查詢爲主仍是寫入數據爲主。session
對於事務性的應用,經過Com_commit和Com_rollback能夠了解事務的提交和回滾狀況,對於回滾操做很是的頻繁的數據庫,可能意味着應用編寫的存在問題。併發
基本狀況瞭解:函數
Connections:試圖連接數據庫服務的次數
Uptime:服務器工做時間
slow_queries:慢查詢次數
1.2,定位執行效率比較低的sql語句
經過慢查詢日誌定位慢的SQl語句,用--slow_query_log_file[=file_name](mysql5.6以後) 選項啓動中,mysqld會寫一個全部執行時間超過long_query_time秒的SQL語句的日誌文件。
使用SHOW FULL PROCESSLIST:查看當前Mysql在進行的線程,同時對一些鎖表操做進行優化
slow_query_log_file=/var/lib/mysql/Dragonwake-slow.log // # 將查詢執行時間較慢的語句進行記錄 mysql配置文件 /etc/my.cnf 。 若是未指定file_name,默認爲host_name-slow.errmysql5.6以前 log-slow-queries
long_query_time=1 # 執行超過1秒的sql會被log下來
日誌查詢 show variables like 'log_%'
二進制日誌數目:show master logs; // 必須使用二進制日誌纔可使用查詢
1.3,經過explain分析慢SQLi
explain SQl語句
結果:
--select_type:表示select的類型,常見的類型是SIMPLE (既不是用錶鏈接或子查詢),PRIMARY(主查詢,即外層的查詢),UNION(Union中的第二個或後面的查詢語句),SUBQUERY(子查詢中的第一個select查詢)等
--table:輸出結果的表名
--type:表示MYSQL在表中找到行的方式,或者叫訪問類型
常見的有 ALl index range ref eq_ref const system NUll 從左到右性能由差到好
type=all 全表掃描
type=index 索引全掃描,mysql遍歷全部索引來查詢
type=range:索引範圍掃描,常見於<,<= ,> , >= between
type=ref:使用非惟一索引掃描或惟一索引的前綴掃描,返回匹配某個單獨值得匹配
type=eq_ref 相似ref 就在於使用得索引是惟一索引,對於每一個索引鍵值,表中只有一條記錄匹配,簡單來講,就是多表鏈接中使用primary key(主鍵)和unique index(惟一索引)做爲關聯條件
type=const/system 但表中最多有一個匹配行,查詢起來很是迅速,通常主鍵primary key或惟一索引unique index進行得查詢,經過惟一索引ek_email訪問得時候,類型type爲const;而從咱們構造的僅有一條記錄得a表中檢索時,類型type爲system
type=null :mysql不用訪問表或索引,就獲得就結果
類型type還有其餘值,如ref_or_null(與ref類似,區別在於條件包含對null的查詢),index_merge(索引合併優化),unique_subquery( in 後面是一個查詢主鍵字段的子查詢 ),index_subquery(與unique_subquery類似,區別在於 in 後面時一個查詢非惟一索引字段的子查詢)
--possile-key:表示查詢時可能使用的索引
--key:實際使用的索引
--key-len:使用到索引字段的長度
--rows :掃描行的數量
--Extra:執行狀況的說明和描述,包含不適合在其餘列中顯示可是對執行計劃很是重要的額外信息
Using where :表示優化器除了利用索引來加速訪問以外,還須要索引回表查詢數據
1.4,經過show profile 分析SQL
查看當前mysql是否支持profile
默認profile是關閉的,能夠經過set語句在session級別開啓profiling, set profiling=1
使用方法:
-執行統計查詢
查詢上面的SQl query ID
查詢上述執行過程當中每一個線程的狀態和消耗時間
Sending data 表示mysql線程開始訪問數據並行把結果返回客戶端,而不只僅是返回結果給客戶端。因爲在Sending data 狀態下,mysql線程每每須要大量的磁盤讀取操做,因此是整個查詢中耗時最長的。
查看詳細細節並排序
SELECT
STATE,
SUM(DURATION) AS TR,
ROUND(
100 * SUM(DURATION) / (
SELECT
SUM(DURATION)
FROM
information_schema.PROFILING
WHERE
QUERY_ID = 190
),
2
) AS PR,
COUNT(*) AS Calls,
SUM(DURATION) / COUNT(*) AS "R/Call"
FROM
information_schema.PROFILING
WHERE
QUERY_ID = 190
GROUP BY
STATE
ORDER BY
TR DESC;
進一步獲取all,cpu,block io , context switch , page faults 等明細來查看mysql在使用什麼資源時耗費了過多的時間
此時可經過獲取Sending data 時間消耗在cpu
注:InnoDB引擎count(*)沒有myISAM 執行速度快,就是由於InnoDB經歷Sending data狀態,存在訪問數據的過程,而MyISAM引擎的表在executing(執行)以後就直接結束查詢,徹底不須要訪問數據。
2,索引問題
索引是數據庫裏最多見也是最重要的手段之一,經過索引能夠幫助用戶解決大部分的SQL性能問題
2.1,存儲引擎的分類
- B-Tree索引:最多見的索引類型,大部分引擎都支持B樹索引
- Hash 索引:只有Memory引擎支持
- R-Tree索引:空間索引是MyISAM引擎的一個特殊索引類型,主要用於地理空間數據類型
- Full-text :全文索引是MyISAM的一種特殊的索引類型,主要用於全文索引,InnoDB從mysql5.6開始支持
Mysql不支持函數索引,可是能對列的前面一部分進行索引,例如:字段title,能夠只取title字段前10個字符進行索引,只是在order by和group by 操做的時候沒法使用。前綴索引建立例子:create index idx_title on film(title(10))
經常使用的索引是B-Tree和Hash。Hash是隻有memory和Heap引擎支持。適用於key-value查詢,一般Hash比B-Tree更迅速,Hash不使用範圍索引。Memory和Heap只有在 = 條件下才能使用索引
2.2 如何使用索引
建立一個複合索引:alter table tb_name add index ids_rental_data(rental_data,inventory_id,customer_id)
2.2.1 mysql可以使用索引的典型場景
- 匹配全值,對索引中全部列都指定具體值,既是對全部列都有等值匹配條件。
好比上述的建立, ids_rental_data 包含rental_data,inventory_id,customer_id,若是此時where自句中包含三者,則爲全值匹配
- 匹配的值進行範圍查找,對索引的值能進行範圍查找
-- 匹配最左前綴,意思是,在複合索引中,索引是從左邊第一個查找的,不會跨過第一個從第二個查找。好比一個聯合索引包含(c1 ,c2 ,c3),可不能唄c2和c2+c3利用到
添加索引 alter table payment add index idx_payment_data(payment_date,amount,last_update) ; 此時第一個字段是payment-date
若是查詢條件包含索引的第一個 payment_date ,可以使用複合索引idx_payment_data 進行過濾
若是使用第二個 amount沒有使用第一個payment_date,則 不會使用索引
-- 僅僅對索引字段進行查詢,意思是查詢的字段都在索引字段中,查詢的效率更高。
那麼直接訪問索引就能夠獲取所需的數據,不須要索引回表。此時的Extra 就變成了Using index。Using index表明覆蓋索引掃描
- 匹配列前綴 ,僅僅使用索引中的第一列,而且只包含索引第一列的開頭一部分進行查找;
例如:建立索引 alter table yk_book add index idx_book_diata(book(5),type(3));
查詢能夠看到idx_book_diata被使用,Using where 表示優化器須要索引回表來查詢數據
-匹配部分精準,其餘部分範圍匹配
指定數量 ,不一樣的客戶編號
類型type爲range說明優化器選擇範圍查詢 ,索引key爲idx_book_data表示優化器選擇索引idx_book_data幫助加速查詢,同時所查詢的字段在索引中,索引Extra中能看到Using index
- 列名 is null 此種狀況下會使用索引。
2.2.2存在索引但不能使用的典型場景
- 以 %開頭的like查詢,不能使用B-Tree索引
B-Tree索引,以%開頭的like查詢不能使用索引,咱們能夠經過全文索引(Fulltext)來解決問題,或者使用InnoDB上的二級索引,首先獲取知足條件的列表id,以後再根據主鍵回表去檢索記錄
- 數據類型出現隱式轉換不會使用索引,有些列是字符串,再寫where條件時,須要將常量值用引號括起來
- 複合索引查詢條件必須包含最左部分,不然不會使用索引。即leftmost
- mysql 執行語句時會有優化器選擇的過程,當全表掃描的代價小於索引掃描的代價時,會使用全表掃描,因此須要更換一個篩選性更高的條件
- 用or分開的條件,若是or前的條件有索引,or後的條件沒有索引,則不會使用索引
2.3查看索引的使用狀況
若是索引正在工做,Handle_read_key 的值將會很高,這個值表明了一行被索引值讀的次數。若是很低說明增長索引增長的性能不高,由於索引並無被常用。
Handle_read_rnd_next的值高說明查詢運行低效,而且應該創建索引補救。這個值的含義是在數據文件中讀取下一行的請求數。若是值比較大,說明正在進行大量的表掃描,則一般說明表索引不正確或寫入的查詢沒有利用索引。
3,常見的SQL優化
3.1 大批量插入數據
-- MyISAM
- 打開或關閉MyISAM表 非惟一索引的更新,能夠提升導入效率(導入數據到非空MyISAM表)
步棸:一步:alter table tb_name disable key ;//disable關閉表的非惟一索引的使用
二步:導入數據
三步: alter table tb_name enable key // enable 開啓表的非惟一索引的使用
導入數據到一個空的MyISAM表,默認先導入數據在建立索引,因此不用設置
-- InnoDB
- 由於InnoDB類型的表按照主鍵順序保存的,因此導入的數據是按照主鍵的順序排列,能夠有效的提升導入數據的效率
- 關閉惟一性效應,set unique_checks = 0,導入數據後再恢復
- 若是使用的是自動提交的方式,再導入前使用set AUTOCOMMIT = 0,導入結束後在恢復
3.2 優化 insert 語句
- 同一客戶端插入不少行,應儘可能使用多個值的insert語句。例如,insert into tb_name (),(),(),();
- 不一樣客戶端插入不少行,可使用 insert delayed , delayed含義是讓insert語句放置到內存的隊列中,並無寫入磁盤。
low_priority是在全部其餘用戶對讀寫完成後才進行插入。
low_priority 低優先級 update [low_priority] tb_name set col_name = exp1 ,col_name2 = exp2........ //mysql中update 用low_priority 不鎖定表
LOW_PRIORITY 關鍵字應用於 delete ,insert ,load data , update 和replace
HIGH_PRIORITY 關鍵字應用於 select 和insert語句
delayed 應用於insert和replace語句
- 將索引文件和數據文件分別放置在不一樣的磁盤
- MyISAM 若是進行批量插入,增長bulk_insert_buffer_size的值
- 從文件裝載一個表時,使用Load data infile , 比insert語句快20倍
3.3 優化 order by 語句
3.3.1 mysql排序方式
- 經過有序索引順序掃描直接返回有序數據。
在book表上有索引 uid 指向字段 uid
查詢表的索引信息
此時使用uid排序時,Extra 時Using index 無需進行回表操做,不須要額外的排序,操做效率高
-- 經過fileSort 排序,不經過索引直接返回排序結果的排序都叫作fileSort排序。mysql服務器對排序參數的設置和須要排序數據的大小來決定排序操做是否使用磁盤文件或臨時表。
fileSort是經過算法,將取得的數據在sort_buffer_size系統變量設置的內存排序區進行排序,若是內存裝不下,就會將磁盤上的數據進行分塊,在對各個數據塊進行排序,而後合併。sort_buffer_size的排序區爲線程獨佔,可能同時存在多個
好比經過uid排序全部客戶記錄時,此時爲全表掃描,而且使用了filesort
通常優化方式:減小額外的排序,經過索引直接返回有序數據。儘可能使where條件和order by使用相同的索引,而且order by順序和索引數據相同,而且order by的字段都是升序或者降序,不然確定會出現fileSort
-- 不會使用索引的狀況:
- order by的字段混合使用asc和desc select * from tb_name order p_key1 desc,p_key2 asc
- 對於查詢的關鍵字和order by所使用的不一樣 select * from tb_name where key1=content order by key2
- 對不一樣的關鍵字使用order by : select * from tb_name order by key1,key2
3.3.2 優化fileSort
fileSort 有兩種排序算法
- 兩次掃描算法:首先根據條件取出排序字段和行指針信息,以後再排序區sort buffer中排序。若是排序區sort buffer不夠,則在臨時表Temporary Table中存儲排序結果,完成排序後根據行指針回表讀取記錄。須要兩次訪問數據,第一次獲取排序字段和行指針信息,第二次根據行指針獲取記錄,第二次讀取操做可能致使大量隨機I/O操做,優勢是排序的時候內存開銷比較少。
- 一次掃描算法:一次性取出知足條件行的全部字段,而後再排序區 sort buffer 中排序後直接輸出結果集。排序的內存開銷比較大,但排序效率比兩次掃描算法高
mysql 經過比較系統變量max_length_for_sort_data 的大小和query語句取出的字段總大小來判斷使用哪一種算法,max_length_for_sort_data大使用第二種,不然使用第一種
適當加大系統變量max_length_for_sort_data的值,可以讓mysql選擇更優化的fileSort的排序算法。可是過大會引發cpu的利用率太低和磁盤I/O太高
適當加大sort_buffer_size排序區,儘可能讓排序再內存區完成,而不是經過建立臨時表放在文件中完成;該大小須要考慮數據庫活動鏈接數和服務器內存的大小來適當設置排序區。由於這個參數是每一個線程獨佔的,若是設置過大,會致使服務器swap嚴重。儘可能使用必要的字段而不是 select *
3.3.3 優化group by
默認狀況下,mysql對全部的group by的字段進行排序。若是查詢包括group by 可是用戶想要排除掉排序結果的消耗,則能夠指定order by null禁止排序
select xxx from tb_name group by xxx order by null
3.3.4 優化嵌套查詢
使用子查詢能夠一次性完成不少邏輯上須要多個步棸才能完成的sql操做,同時能夠避免事務或者表鎖死。子查詢能夠被更有效的鏈接 join 代替
3.3.5 優化or條件
對於含有or的查詢子句,若是要利用索引,若是要利用索引,則or之間的每一個條件都要用到索引
3.3.6 優化分頁索引
通常分頁查詢時,經過建立覆蓋索引可以比較好的提升性能,可是當分頁爲1000 20時,此時排序前1020條記錄後返回1001到1020條記錄,前1000條記錄會被拋棄,查詢和排序的代價很是高
- 第一種優化思路:從索引完成排序分頁的操做,最後根據主鍵回表查詢所需的其餘列內容
例如:直接查詢
按照索引分頁後回表方式改寫sql
- 第二種優化思路:把limit查詢轉換爲某個位置的查詢
假如須要查詢第100頁,則能夠記錄第99頁最後一行的id(倒序或正序),而後查詢時where 條件大於或者小於99頁最後一行的id,而後直接limit n就能夠了,n爲每頁顯示的行數
比較
3.3.7 使用sql提示
- USE INDEX
提示mysql參考使用的索引,可讓mysql不在考慮其餘可用索引
好比:select count(1) from tb_name use index(index_name ) where xxxx ; 此時使用了index_name索引,其餘索引忽略。
- ignore index
提示mysql忽略一個可用索引
好比: select count(1) from tb_name ignore index(index_name); 此時忽略index_name索引
- force index
強制mysql使用某個索引,使用狀況,當where自句取id>1的值,由於數據庫中大部分庫表都是大於1的,因此會全表掃描,此時使用use index不可用,因此使用force index
好比; select * from tb_name force index(index_name) where id>1;
4,經常使用的sql技巧
4.1 正則的使用
序列 | 序列說明 |
^ | 在字符串的開始處進行匹配 |
$ | 在字符串的末尾處進行匹配 |
. | 匹配任意單個字符,包括換行符 |
[...] | 匹配出括號內的任意字符 |
[^...] | 匹配不出括號內的任意字符 |
a* | 匹配零個或多個a(包括空串) |
a+ | 匹配一個或多個a (不包括空串) |
a? | 匹配一個或零個a |
a1|a2 | 匹配a1或a2 |
a(m) | 匹配m個a |
a(m,) | 匹配m個或多個a |
a(m,n) | 匹配m個到n個a |
a(,n) | 匹配0到n個a |
(....) | 將模式元素組成模式元素 |
- ^ 在字符串開頭匹配
匹配是否以a開頭 regexp
- $在字符串的末尾處匹配
- . 匹配單個任意字符,包括換行符
- [...] 匹配出括號內的任意字符
- [^...] 不匹配[]中的任意字符
4.2 利用rand()提取隨機行
隨機抽取n條數據:select * from tb_name order by rand() limit n
4.3 group by 的with rollup
WITH ROLLUP 能夠檢索出更多的分組聚合信息
好比:統計每日的商品數量 ,不加入with rollup
加入with rollup
with rollup 反應的是一種OLAP思想,能夠知足用戶想要獲得的任何一個分組以及分組組合的聚合信息。with rollup 統計了每日的總數和全部的總數。注意:with rollup 不能和order by一塊兒使用,且limit在with rollup後面
4.4 數據庫名和表名大小寫問題
因爲windows,mac Os,Unix對庫表名以及查詢使用的大小寫敏感不一致,因此最好將庫表規範保存,且查詢語句也規範使用
mysql經過存儲引擎取 Innodb 索引目前有兩種BTREE索引和HASH索引。