1.使用命令 mysql
show variables like 'slow_query_log';linux
查看到當前沒有開啓慢查詢sql
2.使用命令數據庫
show variables like '%log%';windows
也沒有開啓log_queries_not_using_indexs安全
3.set global log_queries_not_using_indexes=on;服務器
4.show variables like 'long_query_time';網絡
查看到long_query_time的值爲10,意思是慢查詢日誌中會記錄超過十秒的記錄;session
默認狀況下,MySQL是不會記錄超過必定執行時間的SQL語句的。要開啓這個功能,咱們須要修改MySQL的配置文件,windows下修改my.ini,Linux下修改my.cnf文件,在[mysqld]最後增長以下命令:數據結構
複製代碼 代碼以下:
slow_query_log
long_query_time = 1
5.開啓慢查詢日誌
set global slow_query_log=on;
{
導入mysql官方提供的sakila數據庫:
將文件解壓,獲得三個文件,
打開cmd:使用命令 mysql -uroot -p < "sakila-schema.sql所在的路徑"
使用命令 mysql -uroot -p < "sakila-data.sql所在的路徑"
}
6.mysql 慢查詢日誌分析工具
*mysqldumpslow
*pt-query-digest
7.如何經過慢查詢日誌發現有問題的SQL
* 查詢次數多且每次查詢所佔用的時間長的SQL
一般爲pt-query-digest分析的前幾個查詢
*IO大的SQL
注意pt-query-digest分析中的Rows exammine項
*未命中索引的SQL
注意pt-query-digest分析中的Rows examine和Rows Send的對比
8.使用explain查詢SQL的執行計劃
explain返回各列的含義
table:顯示這一行的數據是關於哪張表的
type:這是重要的列,顯示鏈接使用了何種類型。從最好到最差的連續類型爲const,eq_reg,ref,range,index和ALL(,沒有where從句,表掃描)。
possible_keys:顯示可能應用在這張表中的索引。若是爲空,沒有可能的索引
key:實際使用的索引。若是爲NULL,則沒有使用索引
key_len:使用的索引的長度。在不損失精確性的狀況下,長度越短越好
ref:顯示索引的哪一列被使用了,若是可能的話,是一個常數
rows:mysql認爲必須檢查的用來返回請求數據的行數
9.Max()的優化方法
*查詢最後支付時間----優化MAX()函數
explain select max(payment_date) from payment;
能夠查看到:type:ALL是表掃描操做;沒有任何索引,rows很是大,IO效率很是低。
如何優化這個SQL:一般狀況下創建索引:
create index idx_paydate on payment(payment_date);
這樣在次執行explain select max(payment_date) from payment;
能夠看到Extra:select tables optimized away;能夠經過索引進行操做,大大減小了IO操做
10.Count()的優化方法
*在一條SQL中同時查出2006年和2007年電影的數量----優化count()函數
錯誤方式:
SELECT COUNT(release_year='2006' OR release_year='2007') FROM film;
沒法分開計算2006和2007年的電影數量
select count(*) FROM film WHERE release_year='2006' AND release_year='2007';
release_year不可能同時爲2006和2007,所以有邏輯錯誤
正確的方式:
SELECT COUNT(release_year='2006' OR NULL) AS '2006年電影數量',COUNT(release_year='2007' OR NULL) AS '2007年電影數量' FROM film;
{
如今說說count(*)和count(id)的區別:
新建一張表:create table t(id int);
插入數據:insert into t values (1),(2),(null);
當咱們使用命令:select count(*),count(id) from t;
顯示出count(*)爲3,而count(id)爲2;
說明count(*)包含了null的,count(id)不包含值爲null的
}
11.子查詢的優化
*一般狀況下,須要把子查詢優化爲join查詢,但在優化時要注意關聯鍵是否有一對多的關係,要注意重複數據。
*咱們新建一張表t1
create table t1(tid int);
插入一條數據insert into t1 values(1);
進行子查詢:select * from t where t.id in (select * from t1.tid from t1);
返回t表id在t1表中的數據
*優化成join的形式:
select t.id from t join t1 on t.id=t1.tid;
這兩種形式返回的結果是同樣的
*須要注意的是:
若是在t1表中,添加一條數據:insert into t1 values(1);
而後在分別執行這兩種形式的查詢:
發現使用select * from t where t.id in (select * from t1.tid from t1);查詢出來的結果是一條數據。而select t.id from t join t1 on t.id=t1.tid;是兩條數據,說明有重複數據,咱們可使用distinct去重
select distinct t.id from t join t1 on t.id=t1.tid;這樣就返回一條記錄了
12.優化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;
{
using可用在join語句相同字段鏈接,起到和ON相同做用,inner join 和left join中均可以使用
示例:LEFT JOIN 正常寫法:
SELECT t1.id,t2.name FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE ....
其實也能夠這麼寫:
SELECT t1.id,t2.name FROM t1 LEFT JOIN t2 USING(id) WHERE ....
}
上面的explain執行結果,能夠看到extra爲Using temporary,Using filesort 用到了臨時表,文件排序的方式對錶進行了全表掃描.
咱們應該避免這種方式。改寫以下:
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);
13.優化limit查詢
*limit經常使用於分頁處理,時常會伴隨order by從句使用,所以大多時候會使用Filesorts,這樣會形成大量的IO問題
SELECT film_id,description FROM sakila.film ORDER BY title LIMIT 50,5;
*這個語句進行explain操做,發現會進行全表掃描,而且有文件排序的方式
*優化方式:
步驟1.使用有索引的列或主鍵進行Order By操做
SELECT film_id,description FROM sakila.film ORDER BY film_id LIMIT 50,5
使用這種方式能夠獲得相同的結果,可是explain執行計劃卻徹底不一樣,type=index,rows=55
這種方式並非最優的,好比將limit 50,5改成limit 500,5;rows=505,rows會隨着limit而改變,若是有上萬條數據,那麼響應速度回變慢
步驟2.記錄上次返回的主鍵,在下次查詢時使用主鍵過濾
SELECT film_id,description FROM sakila.film WHERE film_id>55 and film_id<=60 ORDER BY film_id LIMIT 1,5;
這種方式rows=5,而且不會隨着limit的增加而增加
14.索引優化
*在where從句,group by從句,order by 從句,on從句中出現的列
*索引字段越小越好
*離散度越大的列放到聯合索引的前面
SELECT * FROM payment WHERE staff_id=2 AND customer_id =584;
是index(staff_id,customer_id)好?仍是index(customer_id,staff_id)好?
首先咱們先看一下payment表的數據結構是什麼樣的
desc payment;
而後咱們使用語句:select count(distinct customer_id),count(distinct staff_id) from payment;這樣咱們能夠看出他們的離散程度。他們的惟一值越多,說明他們的離散度越好,他們的可選擇性就越高。
這個查詢能夠看出 customer_id=599,而staff_id=2;說明customer_id離散度更高一些,可選擇性更高,所以要創建聯合索引時就把customer_id放在最前面。
因此應該使用index(customer_id,staff_id)
15.如何選擇合適的列創建索引?
索引不是越多越好。
一般狀況下創建索引能夠優化咱們的查詢效率,可是會下降咱們的寫入效率,也就是說創建索引會加強查詢,會影響insert,update,delete這種寫入操做。可是每每不是這樣的,過多的索引不但會影響咱們的寫入效率,同時也會影響查詢,這是因爲數據庫進行查詢分析的時候,首先要選擇哪一個索引進行查詢,若是索引越多,分析的過程就越慢,這樣就會影響查詢的效率。所以咱們要維護和刪除索引。
*索引的維護及優化---重複及冗餘索引
重複索引是指相同的列以相同的順序創建同類型的索引,以下表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;
冗餘索引是指多個索引的前綴列相同,或者是在聯合索引中包含了主鍵的索引,下面這個例子中key(name,id)就是一個冗餘索引。
create table test(id int null primary key,name varchar(50) not null,title varchar(50) not null,key(name,id))engine=innodb;
*查找重複及冗餘索引
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工具
*刪除不用的索引,目前mysql中只能使用慢查詢日誌配合pt-index-usage工具來進行索引使用狀況的分析。
16.數據庫結構優化:
*選擇合適的數據類型
數據類型的選擇,重點在於"合適"二字,如何肯定選擇的數據類型是否合適?
a)使用能夠存下你的數據的最小的數據類型
b)使用簡單的數據類型。Int要比varchar類型在mysql處理上簡單
c)儘量的時候not null定義字段
d)儘可能少用text類型,非用不但是最好考慮分表
使用bigint來存儲ip地址,利用INET_ATON(),INET_NTOA()兩個函數來進行轉換
CREATE TABLE sessions(id INT AUTO_INCREMENT NOT NULL,ipaddress BIGINT,PRIMARY KEY(id));
INSERT INTO session(ipaddress) VALUES(INET_ATON('192.168.0.1'));
SELECT INET_NTOAA(ipaddress) FROM sessions;
表的範式化和反範式化
範式化是指數據庫設計的規範,目前說到範式化通常是指第三範式,也就是要求數據庫中不存在非關鍵字段對任意候選關鍵字段的傳遞函數依賴則符合第三範式
反範式化是指爲了查詢效率的考慮把本來符合第三範式的表適當的增長冗餘,已達到優化查詢效率的目的,反範式化是一種以空間來換取時間的操做。
表的垂直拆分
所謂的垂直拆分,就是把原來一個有不少列的表拆分紅多個表,這解決了表的寬度問題。一般垂直拆分能夠按如下原則進行:
a)把不經常使用的字段單獨存放到一個表中。
b)把大字段獨立存放到一個表中。
c)把常常一塊兒使用的字段放到一塊兒。
表的水平拆分
表的水平拆分是爲了解決單表的數據量過大的問題,水平拆分的表每個表的結構都是完整一致的。
經常使用的水平拆分方法爲:
1.對id進行hash運算,若是要拆分紅5個表則使用mod(id,5)取出0-4個值
2.針對不一樣的hashID把數據存到不一樣的表中。
挑戰:1.快分區表進行數據查詢2.統計及後臺報表操做
17.操做系統配置優化
數據庫是基於操做系統的,目前大多數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服務器上關閉iptable,selinux等防火牆軟件
18.mysql配置文件
Mysql能夠經過啓動時指定配置文件參數和使用配置文件兩種方法進行配置,在大多數狀況下配置文件位於/etc/my.cnf或是/etc/mysql/my.cnf,在windows系統配置文件能夠是位於C:/windows/my.ini文件,Mysql查找配置文件的順序能夠經過如下方法得到
$/usr/sbin/mysqld --verbose --help|grep -A l 'Default option'
注意:若是存在多個位置存在配置文件,則後面的會覆蓋前面的
Mysql配置文件--經常使用參數說明
innodb_buffer_pool-size:很是重要的一個參數,用於配置Innodb的緩衝池,若是數據庫中個只有Innodb表,則推薦配置量爲總內存的75%.
innodb_buffer_pool_instances:Mysql5.5中新增長參數,能夠控制緩衝池的個數,默認狀況下只有一個緩衝池
Innodb_log_buffer_size:innodb log 緩衝的大小,因爲日誌最長每秒鐘就會刷新因此通常不用太大
innodb_flush_log_at_trx_commit:關鍵參數,對innodb的IO效率影響很大。默認值爲1,能夠取0,1,2三個值,通常建議設爲2,但若是數據安全性要求比較高則使用默認值1.
innodb_read_io_threads,innodb_write_io_threads:決定了Innodb讀寫的IO進程數,默認爲4
innodb_file_per_table:關鍵參數,控制Innodb每個表使用獨立的表空間,默認爲off,也就是全部表都會創建在共享表空間中
innodb_stats_on_metadata:決定了Mysql在什麼狀況下會刷新innodb表的統計信息
19.第三方工具:Percon Configuration Wizard
20.服務器硬件優化:
如何選擇CPU:是選擇單核更快的CPU仍是選擇核數更多的cup?
1.MySQL有一些工做只能用到單核CPU
2.Mysql對CPU核數的支持並非越多越快
mysql5.5使用的服務器不要超過32核
磁盤IO優化:
經常使用RAID級別介紹:
RAID0:也稱爲條帶,就是把多個磁盤連接成一個硬盤使用,這個級別的IO最好,缺點就是一個磁盤壞掉那麼全部數據就會丟失
RAID1:也稱爲鏡像,要求至少有兩個磁盤,每組磁盤存儲的數據相同,IO效果不如RAID0,安全性好
RAID5:也是把多個(最少3個)硬盤合併成一個邏輯盤使用,數據讀寫時會創建奇偶校驗信息,而且奇偶校驗信息和相對應的數據分別存儲於不一樣的磁盤上。當RAID5的一個磁盤數據發生損壞後,利用剩下的數據和相應的奇偶校驗信息去恢復被損壞的數據。
通常的OLTP型的數據庫使用RAID1+0:就是RAID1和RAID0的結合。同時具備兩個級別的優缺點。通常建議數據庫使用這個級別。
SNA和NAT是否合適數據庫:
1.經常使用於高可用解決方案
2.順序讀寫效率高,可是隨即讀寫不如人意
3.數據庫隨即讀寫比率很高。