本文是學習《高性能 Mysql》中關於 Mysql 中查詢優化須要注意的一些要點的總結:mysql
-- 雖然 id 上創建了索引,可是沒法使用索引優化
select id from user where id + 1 =5;
複製代碼
下面是建立冗餘索引的幾個例子:算法
- 建立了索引(A,B)再建立索引(A),那後者即是冗餘索引
- 將一根索引擴展爲(A,ID),其中 ID 是主鍵,對於 InnoDB 來講主鍵已經包含在二級索引中了,因此這也是冗餘的
複製代碼
- 能夠查到使用最多或者使用最少的表和索引
- 能夠查到從未使用過的索引,考慮刪除之
- 能夠查到線程的使用狀況等等
複製代碼
- 讓單表查詢的緩存效率更高
- 拆分後用 IN() 代替關聯查詢,可讓 Mysql 按照 ID 順序去查找
- 能夠將數據分佈到不一樣的 Mysql 服務器上
複製代碼
- 若是是統計結果集的大小,請使用 COUNT(*),使用 COUNT(cloumn) 有可能某個列存在 NULL 致使統計不許確,排除 NULL 計算也是要成本的
- 對於 MyIsam 存儲引擎,若是不帶任何 WHERE 條件的狀況下, COUNT(*) 不須要計算,直接經過存儲引擎特性得到
複製代碼
-- 分頁時對於偏移量特別大的狀況下,查詢全部列分頁將很是耗時,可使用「延遲關聯」的方式,其中一個查詢中儘量的使用索引覆蓋掃描方式 LIMIT 查詢出主鍵 ID,而後再和原表作一次關聯返回須要的列:
-- 優化前
select * from user order by id limit 1000, 5;
-- 優化後
select user.* from user join (select id from user order by id limit 1000, 5) new_user on new_user.id = user.id;
-- 若是在一個位置上預先計算出了邊界,能夠將 limit 查詢轉換爲已知位置的查詢進行優化
select * from user where id between 1000 and 1005 order by id
複製代碼
若是對優化器的執行計劃不滿意可使用優化器的幾個提示來控制最終的執行計劃:sql
HIGH_PRIORITY 和 LOW_PRIORITY 對於使用表鎖的存儲引擎有效,HIGH_PRIORITY 會將當前查詢插入到全部處於表鎖等待的 SQL 隊列前面,而 LOW_PRIORITY 會將當前查詢放在全部等待表鎖的 SQL 隊列隊尾,只要隊列中還有須要訪問同一張表的 SQL, 它就被處於等待狀態。數據庫
該提示對 INSERT 和 REPLACE 有效,使用該提示後會當即返回給客戶端,而後將插入的行放入緩存區,等待表空閒時批量寫入數據。緩存
該操做致使 LAST_INSERT_ID() 函數沒法正常工做。安全
對於一些數據記錄,即便插入失敗也不影響服務正常運行,可使用該操做,及時響應客戶端,加快響應速度。bash
讓全部查詢中的的表按照語句中出現的順序進行關聯,不須要 Mysql 優化器去從新選擇關聯順序,若是能確保本身寫的關聯順序性能比較好的狀況下能夠選擇該提示,減小 Mysql 優化器自己選擇分析的時間。服務器
這兩個提示針對 select 操做,告訴優化器對 group by 或 distinct 如何使用臨時表及排序,若是 SQL_SMALL_RESULT 表示結果集很小,使用內存排序,若是是 SQL_BIG_RESULT 表示結果集很大,使用磁盤臨時表排序。網絡
這個提示告訴 Mysql 結果集是否要緩存在查詢緩存中數據結構
FOUND_ROWS 這個函數通常狀況下只會返回上一次查詢的數據集大小,可是若是加了 SQL_CALC_FOUND_ROWS 提示,那麼將返回不帶 limit 狀況下整個數據集大小,這個參數對於分頁有必定的用處,不須要屢次查詢。
該提示只對支持行級鎖的存儲引擎生效,該提示會對查詢中符合條件的數據加鎖
這兩個提示會讓 InnoDB 覆蓋索引優化失效,由於 InnoDB 須要訪問主鍵中的版本信息。
告訴優化器是否使用某個索引
- 用多個小表代替一個大表,可讓緩存失效在一個更細的粒度上進行
- 批量寫入時只作一次緩存失效,因此比單條寫入更好
- 若是沒法在數據庫或者表級別控制查詢緩存,則可使用 SQL_CACHE 和 SQL_NO_CACHE 來控制單個 select 語句是否進行緩存,而且能夠修改會話級別的 query_cache_type 來控制查詢緩存
- 對於寫密集型的應用來講,關閉查詢緩存對性能會更好
複製代碼
datadir= /var/lib/mysql # 數據的存儲位置
user= mysql # 執行 mysql 用戶運行 mysql 實例,要保證該用戶存在
port= 3306 # mysql 實例的端口號
socket: =/var/lib/mysql/mysql.sock # socket 文件存儲位置,用於 TCP/IP 套接字鏈接數據庫
pid_file = /var/lib/mysql/mysql.pid # mysql 進程 id
default_storage_engine = InnoDB # 默認的存儲引擎
innodb = FORCE # 只有在 Innodb 存儲引擎正常啓動時,服務器才能正常啓動,通常建議設置爲 FORCE,保證能夠正確使用 InnoDB 存儲引擎
innodb_buffer_pool_size = <value> # InnoDB 存儲引擎可使用的緩存大小
innodb_log_file_size = <value> # 設置重作日誌大小,過小寫入日誌須要頻繁的刷新磁盤,使寫入變慢,太大奔潰恢復時間變慢,要合理設置
innodb_thread_concurrency = 0 # 它能夠限制一次性有多少線程進入內核,0 表示不限制。通常建議設置爲:CPU 數量 * 磁盤數量 * 2
innodb_thread_sleep_delay = 10000 # 爲了減小由於操做系統調度引發的上下文切換,線程第一次沒法進入內核會休眠 innodb_thread_sleep_delay 秒之後再嘗試
# 若是再次沒法進入內核,則放入線程等待隊列,讓操做系統來處理
innodb_file_per_table = 1 # 是否讓每一張表使用一個獨立文件存儲,使得刪除表變的簡單,而且容易分散到不一樣的磁盤上,可是會致使空間的浪費
innodb_flush_method = 0_DIRECT # 控制 InnoDB 如何和文件系統相互做用,控制將數據刷新到磁盤的方式,要不要使用磁盤緩存等
key_buffer_size = <value> # MyISAM 存儲引擎分配的鍵緩存大小,該值對使用 MyISAM 存儲引擎的數據庫很是重要
# 即便是使用 InnoDB 存儲引擎也應該分配必定空間(32M),由於 Mysql 中一些系統表會使用 MyISAM 存儲引擎
# Group by 建立臨時表時也可能使用 MyISAM 存儲引擎
sort_buffer_size = <value> # 該參數會在查詢使用內存排序時分配內存,一旦須要排序就會指定這麼大的內存,無論是否須要這麼大的內存
# 通常建議把 sort_buffer_size 修改的小一點,若是某個查詢確實須要很大內存排序,能夠在會話級臨時調大該值
log_error=/var/lib/mysql/mysql-error.log # 錯誤日誌存放位置
slow_query_log=/var/lib/mysql/mysql-show.log# 慢查詢日誌存放位置
tmp_table_size/max_heap_table_size = 32M # 這兩個變量用於控制使用內存臨時表(Memory存儲引擎)的大小,若是超過這個值,將使用磁盤臨時表(MyISAM存儲引擎)
# 經過 show status 觀察 Created_tmp_disk_tables 和 create_tmp_tables 的變化來調整這兩個參數
query_cache_type = 0 # 控制查詢緩存功能的開啓和關閉,0 表示關閉,1 表示開啓,2 表示只有 select 中明確指定 SQL_CACHE 才緩存
query_cache_size = 0 # 設置查詢緩存的大小
max_connections = <value> # 最大鏈接數,默認是 100,每每過小,若是過小會報太多鏈接被拒絕的錯誤,觀察 Max_used_connections 狀態變量來設置該參數
thread_cache_size = <value> # 指定 Mysql 能夠保存在緩存中的線程數,通常經過觀察 Thread_connected 變量的大小來調整該值的大小
table_cache_size = 1000 # 設置表緩存大小,設置足夠的大小以免老是須要從新打開並從新解析表定義
open_files_limit = 65535 # 這個參數能夠儘可能設置大,由於打開句柄的開銷很小,不然會出現「too many open files」
expire_logs_days = 10 # 服務器在指定的天數後清理二進制日誌
max_connect_errors = 100 # 允許某個應用最大錯誤次數,若是超過該值,將被加入黑名單,除非刷新主機緩存
複製代碼
- connect_timeout
在獲取鏈接階段(authenticate)起做用,獲取 MySQL 鏈接是屢次握手的結果,除了用戶名和密碼的匹配校驗外,還有 IP->HOST->DNS->IP 驗證,任何一步均可能由於網絡問題致使線程阻塞。
爲了防止線程浪費在沒必要要的校驗等待上,超過 connect_timeout 的鏈接請求將會被拒絕,默認值 10 秒。
- interactive_timeout 和 wait_timeout
在鏈接空閒階段(sleep)起做用,即便沒有網絡問題,也不能容許客戶端一直佔用鏈接。
對於保持 sleep 狀態超過了 wait_timeout(或 interactive_timeout,取決於 client_interactive 標誌)的客戶端,MySQL 會主動斷開鏈接,默認值是 8 小時。
- net_read_timeout 和 net_write_timeout
則是在鏈接繁忙階段(query)起做用,即便鏈接沒有處於 sleep 狀態,即客戶端忙於計算或者存儲數據,MySQL 也選擇了有條件的等待。
在數據包的分發過程當中,客戶端可能來不及響應(發送、接收、或者處理數據包太慢)。
爲了保證鏈接不被浪費在無盡的等待中,MySQL 也會選擇有條件(net_read_timeout和net_write_timeout)地主動斷開鏈接。默認是 30 秒。
- innodb_lock_wait_timeout
innodb 使用這個參數可以有效避免在資源有限的狀況下產生太多的鎖等待,指的是事務等待獲取資源時等待的最長時間,超過這個時間還未分配到資源則會返回應用失敗。
參數的時間單位是秒,最小可設置爲1s(通常不會設置得這麼小),最大可設置1073741824秒(34年),默認安裝時這個值是 50 s。
超過這個時間會報 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction。
- innodb_rollback_on_timeout
默認狀況下 innodb_lock_wait_timeout 超時後只是超時的 sql 執行失敗,整個事務並不回滾,也不作提交。
如須要事務在超時的時候回滾,則須要設置 innodb_rollback_on_timeout=ON,該參數默認爲 OFF。
- lock_wait_timeout
和 innodb_lock_wait_timeout 的區別是前者是 Innodb 的 DML 操做的行級鎖的等待時間,後面是數據結構 DDL 操做的鎖的等待時間。
- innodb_flush_log_at_timeout
參數 innodb_flush_log_at_trx_commit = 1 時,此超時參數不起做用。當 innodb_flush_log_at_trx_commit=0/2 時才起做用。
表示每 innodb_flush_log_at_timeout 秒進行一次的頻率刷新 redo log(在 5.6.6 版本以前是固定每秒一次刷新 redo log,5.6.6 版本以後刷新頻率能夠經過這個參數設置,固然,這個參數自己默認值也是 1S)
複製代碼