能夠進行優化的層面mysql
進行優化前的數據準備linux
打開如下連接下載數據sql
http://downloads.mysql.com/docs/sakila-db.zip
打開終端,執行如下命令數據庫
# 登陸 MySQL Cli 模式 mysql -uroot -p # 建立數據庫 SOURCE /Users/LuisEdware/Downloads/sakila-db/sakila-schema.sql.sql # 填充數據到數據庫 SOURCE /Users/LuisEdware/Downloads/sakila-db/sakila-data.sql # 使用 sakila 數據庫 USE sakila;
SQL 語句和索引服務器
如何發現有問題的 SQL?答案是使用 MySQL 慢查詢日誌對有效率問題的 SQL 進行監控,執行命令以下:網絡
# 查看是否開啓慢查詢日誌 show variables like "slow_query_log"; # 查看是否設置了把沒有索引的記錄到慢查詢日誌 show variables like "log_queries_not_using_indexes"; # 查看是否設置慢查詢的 SQL 執行時間 show variables like "long_query_time"; # 查看慢查詢日誌記錄位置 show variables like "slow_query_log_file"; # 開啓慢查詢日誌 set global slow_query_log=on # 設置沒有索引的記錄到慢查詢日誌 set global log_queries_not_using_indexes=on # 設置到慢查詢日誌的 SQL 執行時間 set global long_query_time=0 # 查看慢查詢日誌(在 Linux 終端下執行) tail -50 /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log;
慢查詢日誌所包含的內容tcp
慢查詢日誌分析工具函數
mysqldumpslow /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log;
brew install brew install percona-toolkit
pt-query-digest /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log | more;
如何經過慢查詢日誌發現有問題的 SQL?工具
使用 EXPLAIN 分析 SQL 的執行計劃的例子以下:性能
EXPLAIN SELECT * FROM staff;
使用 EXPLAIN 分析 SQL 的各列參數含義以下:
分析 SQL 語句:使用 MAX() 方法查詢最後一筆交易的時間
EXPLAIN SELECT MAX(payment_date) FROM payment_date
執行結果以下:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: payment partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 16086 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
若是數據表的數據很是大,查詢頻率又很是高,那麼服務器的 IO 消耗也會很是高,因此這條 SQL 語句須要優化。能夠經過創建索引進行優化。執行代碼以下:
CREATE INDEX idx_paydate ON payment(payment_date);
而後再分析 SQL 語句,執行結果以下:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: NULL partitions: NULL type: NULL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: NULL filtered: NULL Extra: Select tables optimized away 1 row in set, 1 warning (0.01 sec)
通過優化以後,因爲索引是按順序排列的,MySQL 不須要查詢表中的數據,而是經過查詢索引最後的一個數據,就能夠得知執行結果了。並且這個時候,無論表的數據量多大,查詢 MAX() 所須要的時間都是基本固定的,這樣就儘量地減小了 IO 操做。
分析 SQL 語句:使用 COUNT() 函數在一條 SQL 中同時查出 2006 年和 2007 年電影的數量
SELECT count(release_year = '2006' OR NULL) AS '2006 年電影數量' , count(release_year = '2007' OR NULL) AS '2007 年電影數量' FROM film;
count(*) 包含空值,count(id) 不包含空值。上述語句就是優化 Count() 函數取值
分析 SQL 語句:查詢 sandra 出演的全部影片
SELECT title , release_year , LENGTH FROM film WHERE film_id IN( SELECT film_id FROM film_actor WHERE actor_id IN( SELECT actor_id FROM actor WHERE first_name = 'sandra' ) )
一般狀況下,須要把子查詢優化爲 join 查詢,但在優化時要注意關聯鍵是否有一對多的關係,要注意重複數據。
group by 可能會出現臨時表、文件排序等,影響效率。能夠經過關聯的子查詢,來避免產生臨時表和文件排序,能夠節省 IO。
group by 查詢優化前:
EXPLAIN SELECT actor.first_name , actor.last_name , Count(*) FROM sakila.film_actor INNER JOIN sakila.actor USING(actor_id) GROUP BY film_actor.actor_id;
執行結果以下:
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 200 filtered: 100.00 Extra: Using temporary; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor partitions: NULL type: ref possible_keys: PRIMARY,idx_fk_film_id key: PRIMARY key_len: 2 ref: sakila.actor.actor_id rows: 27 filtered: 100.00 Extra: Using index 2 rows in set, 1 warning (0.01 sec)
group by 查詢優化後:
EXPLAIN SELECT actor.first_name , actor.last_name , c.cnt FROM sakila.actor INNER JOIN( SELECT actor_id , count(*) AS cnt FROM sakila.film_actor GROUP BY actor_id ) AS c USING(actor_id);
執行結果以下:
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: actor partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 200 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> partitions: NULL type: ref possible_keys: <auto_key0> key: <auto_key0> key_len: 2 ref: sakila.actor.actor_id rows: 27 filtered: 100.00 Extra: NULL *************************** 3. row *************************** id: 2 select_type: DERIVED table: film_actor partitions: NULL type: index possible_keys: PRIMARY,idx_fk_film_id key: PRIMARY key_len: 4 ref: NULL rows: 5462 filtered: 100.00 Extra: Using index 3 rows in set, 1 warning (0.00 sec)
LIMIT 經常使用於分頁處理,時常會伴隨 ORDER BY 從句使用,所以大多時候會使用 Filesorts ,這樣會形成大量的 IO 問題
優化步驟1:使用有索引的列或主鍵進行 Order By 操做
優化步驟2:記錄上次返回的主鍵,在下次查詢時使用主鍵過濾(保證主鍵是自增且有索引)
例如:
SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;
上述 SQL 語句,是 index(staff_id,customer_id)
合理,仍是 index(customer_id,staff_id)
合理。執行語句以下:
SELECT count(DISTINCT customer_id) , count(DISTINCT staff_id) FROM payment; -- 結果是 599 2
因爲 customer_id 的離散度更大,因此應該使用 index(customer_id,staff_id)
之因此要找到重複和冗餘的索引,是由於過多的索引不但影響寫入,並且影響查詢,索引越多,分析越慢。那麼爲什麼重複索引、冗餘索引?概念以下:
重複索引是指相同的列以相同的順序創建的同類型的索引,以下表中 primary key 和 ID 列上的索引就是重複索引,例子以下:
CREATE TABLE test( id INT NOT NULL PRIMARY KEY , NAME VARCHAR(10) NOT NULL , title VARCHAR(50) NOT NULL , UNIQUE(id) ) ENGINE = INNODB;
UNIQUE(ID)
和 PRIMARY KEY
重複了。
冗餘索引是指多個索引的前綴列相同,或是在聯合索引中包含了主鍵的索引,例子以下:
CREATE TABLE test( id INT NOT NULL PRIMARY KEY , NAME VARCHAR(10) NOT NULL , title VARCHAR(50) NOT NULL , KEY(NAME , id) ) ENGINE = INNODB;
查找重複及冗餘索引的 SQL 語句以下:
USE information_schema; SELECT a.TABLE_SCHEMA AS '數據名' , a.table_name AS '表名' , a.index_name AS '索引1' , b.INDEX_NAME AS '索引2' , a.COLUMN_NAME AS '重複列名' FROM STATISTICS a JOIN STATISTICS b ON a.TABLE_SCHEMA = b.TABLE_SCHEMA AND a.TABLE_NAME = b.table_name AND a.SEQ_IN_INDEX = b.SEQ_IN_INDEX AND a.COLUMN_NAME = b.COLUMN_NAME WHERE a.SEQ_IN_INDEX = 1 AND a.INDEX_NAME <> b.INDEX_NAME
也可使用工具 pt-duplicate-key-checker
檢查重複索引和冗餘索引,使用例如:
pt-duplicate-key-checker -uroot -p '123456' -h 127.0.0.1 -d sakila
執行結果以下:
# ######################################################################## # Summary of indexes # ######################################################################## # Size Duplicate Indexes 118425374 # Total Duplicate Indexes 24 # Total Indexes 1439
目前 MySQL 中尚未記錄索引的使用狀況,可是在 PerconMySQL 和 MariaDB 中能夠經過 INDEX_STATISTICS 表來查看哪些索引未使用,但在 MySQL 中目前只能經過慢查詢日誌配合共組 pt-index-usage
來進行索引使用狀況的分析。
pt-index-usage -uroot -p '123456' /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log;
數據庫結構優化
略
垂直拆分,就是把原來一個有不少列的表拆分紅多個表,這解決了表的寬度問題。一般垂直拆分就能夠按如下原則進行:
當單表的數據量過大,致使增刪查改等操做過慢,這時候須要對錶進行水平拆分。水平拆分的表,每一張表的結構都是徹底一致的。
經常使用的水平拆分方法爲:
挑戰:
系統配置優化
數據庫是基於操做系統的,目前大多數 MySQL 都是安裝在Linux 系統之上,因此對於操做系統的一些參數配置也會影響到 MySQL 的性能,下面列舉一些經常使用到的系統配置。
網絡方面的配置,要修改文件 /etc/sysctl.conf
# 增長 tcp 支持的隊列數 net.ipv4.tcp_max_syn_backlog = 65535 # 減小斷開鏈接時,資源回收 net.ipv4.tcp_max_tw_buckets = 8000 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 10
打開文件數的限制,可使用 ulimit -a 查看目錄的各項限制,能夠修改文件 /etc/security/limits.conf
,增長如下內容以修改打開文件數量的限制
soft nofile 65535 hard nofile 65535
除此以外最好在 MySQL 服務器上關閉 iptables,selinux 等防火牆軟件。
MySQL 能夠經過啓動時指定配置參數和使用配置文件兩種方法進行配置,在通常狀況下,配置文件位於 /etc/my.cnf
或是 /etc/mysql/my.cnf
,MySQL 查詢配置文件的順序是能夠經過如下方法過的
經常使用參數說明
SELECT ENGINE , round( sum(data_length + index_length) / 1024 / 1024 , 1 ) AS 'Total MB' FROM information_schema. TABLES WHERE table_schema NOT IN( "information_schema" , "performance_schema" ) GROUP BY ENGINE;
percona:https://tools.percona.com/
服務器硬件優化