MYSQL優化淺談,工具及優化點介紹,mysqldumpslow,pt-query-digest,explain等

MYSQL優化淺談html

msyql是開發經常使用的關係型數據庫,快速、穩定、開源等優勢就不說了。 
我的認爲,項目上線,標誌着一個項目真正的開始。從運維,到反饋,到再分析,再版本迭代,再優化… 這是一個漫長且考驗耐心的過程。在這個過程當中,做爲數據存儲的關鍵–>數據庫的優化起到尤其重要的做用。 
語文學的很差,廢話也很少說,下面結合實例我們說說MYSQL須要從哪些方面進行優化。 
在說優化以前先聲明一下環境java

名稱 版本 備註
系統1 WIN 7——x64 SP1 我的電腦
系統2 centos6.4 我的電腦
數據庫 5.5.22 MySQL Community Server (GPL) 社區版
內存 8GB  
CPU i7-3520 四線程

優化須要考慮的幾個方面 
1. SQL優化 
2. 索引優化 
3. 數據庫結構優化 
4. 系統配置優化 
5. 硬件優化mysql

SQL優化

本次使用我本身的庫LMS爲優化對象linux

mysql> use lms
Database changed
mysql> show tables; +------------------------+ | Tables_in_lms | +------------------------+ | lm_a_mresource | | lm_a_mresourcerole | | lm_a_role | | lm_a_user | | lm_a_userrole | | lm_c_dept部門 | | lm_c_suppliers | | lm_c_usecomp | | lm_d_applyforpayment | | lm_d_arrival | | lm_d_billing | | lm_d_cgcontract | | lm_d_cgcontractcredit | | lm_d_dlcontract | | lm_d_payed | | lm_d_plan需求計劃 | | lm_d_planchangedetails | | lm_d_procurementsource | | lm_d_received | | lm_d_xscontract | +------------------------+ 20 rows in set (0.01 sec)

 

開啓MYSQL慢查詢日誌,監控有效率問題的SQL

如何發現有問題的SQL呢? 
1. 經過設置slow_query_log開啓慢查詢日誌git

mysql> show variables like 'slow_query_log'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | slow_query_log | OFF | +----------------+-------+ 1 row in set (0.00 sec) mysql> set global slow_query_log=on; Query OK, 0 rows affected (0.07 sec)
  1. 經過設置log_queries_not_using_indexes開啓爲使用索引的監控
mysql> show variables like 'log_queries_not_using_indexes'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | log_queries_not_using_indexes | ON | +-------------------------------+-------+ 1 row in set (0.00 sec) mysql> set global log_queries_not_using_indexes=on; Query OK, 0 rows affected (0.00 sec)

 

  1. 經過long_query_time設置監控閥值,也就是超過多少秒就記錄,單位是秒,此處設置爲0.1也就是100毫秒
mysql> set global long_query_time=0.1; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'long_query_time'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | long_query_time | 0.100000 | +-----------------+----------+ 1 row in set (0.00 sec)

 

 

經過以上設計,基本上能夠開始優化工做了。 
set global 只是全局session生效,重啓後失效,若是須要以上配置永久生效,須要在mysql.ini(linux mysql.cnf)中配置正則表達式

[mysqld]
slow_query_log  = 1 log_queries_not_using_indexes = 1 long_query_time = 0.1 slow_query_log_file = c:\mysql\log\mysqlslowquery.log

 

 

我在配置文件中設置了。因此我重啓讓配置生效。 
ps:slow_query_log_file這個路徑要有效且有權限,不然重啓後沒法寫入logsql

C:\Users\sjm>net stop mysql
MySQL 服務正在中止..
MySQL 服務已成功中止。
C:\Users\sjm>net start mysql
MySQL 服務正在啓動 ...
MySQL 服務已經啓動成功。

 

若是是linux,就經過如下命令或service等其餘方式重啓shell

啓動:/etc/init.d/mysqld(mysql) start
中止:/etc/init.d/mysqld(mysql) stop
重啓:/etc/init.d/mysqld(mysql) restart

最後查看慢查詢日誌所在的位置,經過查看slow_query_log_file數據庫

mysql> show variables like 'slow_query_log_file' \G *************************** 1. row *************************** Variable_name: slow_query_log_file Value: c:\mysql\log\mysqlslowquery.log 1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5

測試json

mysql> select sleep(1); +----------+ | sleep(1) | +----------+ | 0 | +----------+ 1 row in set (1.00 sec)

經過以上超出0.1秒的測試,slow_query_log_file就會記錄該信息,格式以下:

//Time int類型時間戳 加 時間
# Time: 150610 10:24:07
//執行SQL的主機信息
# User@Host: root[root] @ localhost [::1]
//SQL執行信息依次是 執行時間,鎖定時間,返回記錄數,掃描行數
# Query_time: 1.000057  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
//SQL執行用時
SET timestamp=1433903047; //SQL內容 select sleep(1);

 

分析工具之一mysqldumpslow查看並分析慢查詢日誌

如下是windows環境須要安裝其餘支持軟件的步驟,非windows直接忽略 
windows要執行mysqldumpslow須要安裝ActivePerl由於windows上這是一個perl腳本,須要安裝ActivePerl才能執行,linux下就能夠直接執行mysqldumpslow了。 
安裝ActivePerl的步驟我就很少說了。安裝好後會在環境變量PATH中找到perl的bin目錄。我重啓了電腦讓環境變量生效。 
驗證ActivePerl是否安裝成功

C:\Users\sjm>perl -v

This is perl 5, version 20, subversion 2 (v5.20.2) built for MSWin32-x86-multi-thread-64int (with 1 registered patch, see perl -V for more detail) Copyright 1987-2015, Larry Wall

 

若是打印和我差很少的就安裝成功了。 
找到mysqldumpslow的目錄。也就是mysql的bin目錄(linux用戶可直接運行mysqldumpslow) 
windows下驗證mysqldumpslow可用運行 
mysqldumpslow經常使用命令說明:

C:\Program Files\MySQL\MySQL Server 5.5\bin>perl mysqldumpslow.pl -help Usage: mysqldumpslow [ OPTS... ] [ LOGS... ] Parse and summarize the MySQL slow query log. Options are --verbose verbose 查看版本 --debug debug 調試 --help write this text to standard output -v verbose 查看版本 -d debug 調試 -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default 經過下面的方式排序日誌↓↓↓ al: average lock time 平均鎖定時間排序 ar: average rows sent 平均發送行數排序 at: average query time 平均查詢時間排序 c: count 執行次數排序 l: lock time 鎖表時間排序 r: rows sent 總結果行數排序 t: query time 總查詢時間排序 -r reverse the sort order (largest last instead of first) 正序排序,即從小到大排序 -t NUM just show the top n queries 查看前X條日誌 -a don't abstract all numbers to N and strings to 'S' 顯示出數字和字符串,默認數字爲 N 字符串爲 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN grep: only consider stmts that include this string 過濾字符串,後接正則表達式,如'10$' 以10爲結尾的條件,大小寫不敏感 -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l don't subtract lock time from total time

windows支持軟件安裝完成

優化開始… … 
查詢lm_d_plan全表條數

mysql> use lms
Database changed
mysql> select count(*) from lm_d_plan;
+----------+
| count(*) |
+----------+
|    57106 | +----------+ 1 row in set (1.94 sec)

 

超過0.1秒 記錄到日誌中,並查看

命令解析:排序(-s)按執行次數(c)倒序(-a)
C:\Program Files\MySQL\MySQL Server 5.5\bin>perl mysqldumpslow.pl -s c -a c:\mysql\log\mysqlslowquery.log Reading mysql slow query log from c:\mysql\log\mysqlslowquery.log 執行次數 Count: 3 執行時間Time=0.27s (0s) 鎖定時間Lock=1.67s (1s) 發送行數Rows=1.0 (1), 執行地址root[root]@localhost 內容:select count(*) from lm_d_plan Count: 1 Time=0.00s (0s) Lock=0.00s (0s) Rows=0.0 (0), 0users@0hosts C:\Program Files\MySQL\MySQL Server 5.5\bin\mysqld, Version: 5.5.22-log (MySQL Community Server (GPL)). started with: TCP Port: 3306, Named Pipe: (null)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

分析工具之二pt-query-digest

pt-query-digest是用於分析mysql慢查詢的一個工具,它能夠分析binlog、General log、slowlog,也能夠經過SHOWPROCESSLIST或者經過tcpdump抓取的MySQL協議數據來進行分析。能夠把分析結果輸出到文件中,分析過程是先對查詢語句的條件進行參數化,而後對參數化之後的查詢進行分組統計,統計出各查詢的執行時間、次數、佔比等,能夠藉助分析結果找出問題進行優化。 
pt-query-digest只有linux版本,因此只能用linux了。同理設置/etc/my.cnf

[mysqld]
slow_query_log  = 1 log_queries_not_using_indexes = 1 long_query_time = 0.1 #這個目錄必定要有權限,最好就設置到mysql目錄而且經過設置用戶權限受權目錄及文件可讀寫 slow_query_log_file = /var/lib/mysql/log/slow.log

 

順便帖一下受權命令,目的是讓log目錄及文件對mysql組的mysql用戶可讀寫,已受權就能夠無視。

[root@localhost mysql]# cd /var/lib/mysql [root@localhost mysql]# mkdir log [root@localhost mysql]# chown mysql log [root@localhost mysql]# chgrp mysql log [root@localhost mysql]# chmod ug+rwx log [root@localhost mysql]# cd log/ [root@localhost log]# vi slow.log [root@localhost log]# chown mysql slow.log [root@localhost log]# chgrp mysql slow.log [root@localhost log]# chmod 777 slow.log 

 

安裝pt-query-digest 
地址:https://www.percona.com/doc/percona-toolkit/2.2/pt-query-digest.html#downloading 
選擇 wget percona.com/get/percona-toolkit.rpm(也能夠選擇其餘類型安裝) 
安裝過程會出現:

[root@localhost ~]# rpm -ivh percona-toolkit-2.2.14-1.noarch.rpm warning: percona-toolkit-2.2.14-1.noarch.rpm: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY error: Failed dependencies: perl(DBI) >= 1.13 is needed by percona-toolkit-2.2.14-1.noarch perl(DBD::mysql) >= 1.0 is needed by percona-toolkit-2.2.14-1.noarch perl(Time::HiRes) is needed by percona-toolkit-2.2.14-1.noarch perl(IO::Socket::SSL) is needed by percona-toolkit-2.2.14-1.noarch perl(Term::ReadKey) is needed by percona-toolkit-2.2.14-1.noarch
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

缺乏依賴庫 
經過命令yum -y install perl perl-IO-Socket-SSL perl-DBD-MySQL perl-Time-HiRes安裝 
個人安裝過程比較曲折,安裝perl-DBD-MySQL的時候須要安裝依賴庫mysql-libs-5.1.73-3.el6_5.x86_64可是我安裝的是mysql5.6.20,因此報錯**file** /usr/share/mysql/ukrainian/errmsg.sys **from install of** mysql-libs-5.1.73-3.el6_5.x86_64 **conflicts with file from package** MySQL-server-advanced-5.6.20-1.el6.x86_64,就是說安裝的這個mysql-libs依賴庫版本與我自己的mysql版本衝突,他的版本低,並且確定不能刪除本地數據庫,因此須要安裝MySQL-shared-compat-5.6.20-1.linux_glibc2.5.x86_64.rpm (與本地數據庫版本匹配才能夠)來解決mysql-libs的問題。 
還有經過yum安裝的時候最好添加國內的源,我添加是163的,詳情以下

來源:http://blog.chinaunix.net/uid-23683795-id-3477603.html 網易(163)yum源是國內最好的yum源之一 ,不管是速度仍是軟件版本,都很是的不錯,將yum源設置爲163yum,能夠提高軟件包安裝和更新的速度,同時避免一些常見軟件版本沒法找到。具體設置方法以下: 1,進入yum源配置目錄 cd /etc/yum.repos.d 2,備份系統自帶的yum源 mv CentOS-Base.repo CentOS-Base.repo.bk 下載163網易的yum源: wget http://mirrors.163.com/.help/CentOS6-Base-163.repo 3,更新玩yum源後,執行下邊命令更新yum配置,使操做當即生效 yum makecache 4,除了網易以外,國內還有其餘不錯的yum源,好比中科大和搜狐的,你們能夠根據本身需求下載 中科大的yum源: wget http://centos.ustc.edu.cn/CentOS-Base.repo sohu的yum源 wget http://mirrors.sohu.com/help/CentOS-Base-sohu.repo 理論上講,這些yum源redhat系統以及fedora也是能夠用 的,可是沒有通過測試,須要的站長能夠本身測試一下。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

這樣pt-query-digest安裝告一段落 
經過命令pt-query-digest --help能夠查看幫助文檔,同時說明安裝是沒問題的 
語法及重要選項 
pt-query-digest [OPTIONS] [FILES] [DSN] 
–create-review-table 當使用–review參數把分析結果輸出到表中時,若是沒有表就自動建立。 
–create-history-table 當使用–history參數把分析結果輸出到表中時,若是沒有表就自動建立。 
–filter 對輸入的慢查詢按指定的字符串進行匹配過濾後再進行分析 
–limit限制輸出結果百分比或數量,默認值是20,即將最慢的20條語句輸出,若是是50%則按總響應時間佔比從大到小排序,輸出到總和達到50%位置截止。 
–host mysql服務器地址 
–user mysql用戶名 
–password mysql用戶密碼 
–history 將分析結果保存到表中,分析結果比較詳細,下次再使用–history時,若是存在相同的語句,且查詢所在的時間區間和歷史表中的不一樣,則會記錄到數據表中,能夠經過查詢同一CHECKSUM來比較某類型查詢的歷史變化。 
–review 將分析結果保存到表中,這個分析只是對查詢條件進行參數化,一個類型的查詢一條記錄,比較簡單。當下次使用–review時,若是存在相同的語句分析,就不會記錄到數據表中。 
–output 分析結果輸出類型,值能夠是report(標準分析報告)、slowlog(Mysql slow log)、json、json-anon,通常使用report,以便於閱讀。 
–since 從什麼時間開始分析,值爲字符串,能夠是指定的某個」yyyy-mm-dd [hh:mm:ss]」格式的時間點,也能夠是簡單的一個時間值:s(秒)、h(小時)、m(分鐘)、d(天),如12h就表示從12小時前開始統計。 
–until 截止時間,配合—since能夠分析一段時間內的慢查詢。

直接分析: 
pt-query-digest /var/lib/mysql/log/slow.log

第一部分:整體統計結果
# 160ms user time, 10ms system time, 22.46M rss, 199.00M vsz # Current date: Wed Jun 10 18:09:50 2015 # Hostname: localhost.localdomain # Files: /var/lib/mysql/log/slow.log # Overall: 1 total(一共多少條sql), 1 unique(查詢條件進行參數化之後,總共有多少個不一樣的查詢少), 0 QPS, 0x concurrency ______________________ # Time range(日誌的時間範圍): all events occurred at 2015-06-10 14:31:21 # Attribute total min max avg 95% stddev median # ============ ======= ======= ======= ======= ======= ======= ======= # Exec time(執行時間) 23ms 23ms 23ms 23ms 23ms 0 23ms # Lock time(鎖定時間) 253us 253us 253us 253us 253us 0 253us # Rows sent(返回條數) 8 8 8 8 8 0 8 # Rows examine(掃描條數)5.51k 5.51k 5.51k 5.51k 5.51k 0 5.51k # Query size() 50 50 50 50 50 0 50 第二部分:查詢分組統計結,這部分對查詢進行參數化並分組,而後對各種查詢的執行狀況進行分析,結果按總執行時長,從大到小排序 # Profile # Rank Query ID Response time Calls R/Call V/M Item # ==== ================== ============= ===== ====== ===== =============== # 1 0x9E3ACC745D5A7770 0.0228 100.0% 1 0.0228 0.00 SELECT lm_d_plan Response 總的響應時間 time 該查詢在本次分析中總的時間佔比 calls: 執行次數,即本次分析總共有多少條這種類型的查詢語句。 R/Call: 平均每次執行的響應時間。 Item : 查詢對象 # 第三部分:每一種查詢的詳細統計結果 # **Query 1**: 0 QPS, 0x concurrency, ID 0x9E3ACC745D5A7770 at byte 0 ________ # This item is included in the report because it matches --limit. # Scores: V/M = 0.00 # Time range: all events occurred at 2015-06-10 14:31:21 # Attribute pct total min max avg 95% stddev median # ============ === ======= ======= ======= ======= ======= ======= ======= # Count 100 1 # Exec time 100 23ms 23ms 23ms 23ms 23ms 0 23ms # Lock time 100 253us 253us 253us 253us 253us 0 253us # Rows sent 100 8 8 8 8 8 0 8 # Rows examine 100 5.51k 5.51k 5.51k 5.51k 5.51k 0 5.51k # Query size 100 50 50 50 50 50 0 50 # String: # Hosts localhost # Users root # Query_time distribution # 1us # 10us # 100us # 1ms # 10ms ################################################################ # 100ms # 1s # 10s+ # Tables # SHOW TABLE STATUS LIKE 'lm_d_plan'\G # SHOW CREATE TABLE `lm_d_plan`\G # EXPLAIN /*!50100 PARTITIONS*/ select itemsName from lm_d_plan group by FK_deptNo\G 查詢的詳細統計結果,最上面的表格列出了執行次數、最大、最小、平均、95%等各項目的統計。 Databases: 庫名 //這個示例中沒有 Users: 各個用戶執行的次數(佔比) Query_time distribution : 查詢時間分佈, 長短體現區間佔比,本例中1s-10s之間查詢數量是10s以上的兩倍。 Tables: 查詢中涉及到的表 Explain: 示例

 

用法示例 
(1)直接分析慢查詢文件: 
pt-query-digest [參數] /var/lib/mysql/log/slow.log > slow.report 
(2)分析最近12小時內的查詢: 
pt-query-digest --since=12h /var/lib/mysql/log/slow.log > slow_report2.log 
(3)分析指定時間範圍內的查詢: 
pt-query-digest /var/lib/mysql/log/slow.log --since '2014-04-17 09:30:00' --until '2014-04-17 10:00:00'> > slow_report3.log 
(4)分析指含有select語句的慢查詢 
pt-query-digest--filter '$event->{fingerprint} =~ m/^select/i' /var/lib/mysql/log/slow.log> slow_report4.log 
(5) 針對某個用戶的慢查詢 
pt-query-digest--filter '($event->{user} || "") =~ m/^root/i' /var/lib/mysql/log/slow.log> slow_report5.log 
(6) 查詢全部全部的全表掃描或full join的慢查詢 
pt-query-digest--filter '(($event->{Full_scan} || "") eq "yes") ||(($event->{Full_join} || "") eq "yes")' slow.log> slow_report6.log
(7)把查詢保存到query_review表 
pt-query-digest --user=root –password=abc123 --review h=localhost,D=test,t=query_review--create-review-table /var/lib/mysql/log/slow.log
(8)把查詢保存到query_history表 
pt-query-digest --user=root –password=abc123 --review h=localhost,D=test,t=query_ history--create-review-table /var/lib/mysql/log/slow.log
pt-query-digest --user=root –password=abc123--review h=localhost,D=test,t=query_history--create-review-table /var/lib/mysql/log/slow.log
(9)經過tcpdump抓取mysql的tcp協議數據,而後再分析 
tcpdump -s 65535 -x -nn -q -tttt -i any -c 1000 port 3306 > mysql.tcp.txt 
pt-query-digest --type tcpdump mysql.tcp.txt> slow_report9.log 
(10)分析binlog 
mysqlbinlog mysql-bin.000093 > mysql-bin000093.sql 
pt-query-digest --type=binlog mysql-bin000093.sql > slow_report10.log 
(11)分析general log 
pt-query-digest --type=genlog localhost.log > slow_report11.log

如何經過慢查詢日誌找出有問題的SQL

  1. 查詢次數多且每次查詢時間較長的sql 
    一般爲pt-query-digest分析的前個查詢
  2. IO大的SQL(數據庫的瓶頸之一是IO) 
    注意pt-query-digest分析結果中 Rows examine項
  3. 未命中索引的SQL 
    注意pt-query-digest分析結果中 Rows examine很大 可是Rows Sent很小

經過explain分析SQL

數據庫會先進行計劃分析,再進行查詢。執行計劃從側面反映了SQL的執行效率,那麼經過explain查看並分析執行計劃是很是有必要的

mysql> explain select * from lm_d_plan group by fk_deptno; +----+-------------+-----------+-------+---------------+-----------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+-------+---------------+-----------+---------+------+------+-------+ | 1 | SIMPLE | lm_d_plan | index | FK_deptNo | FK_deptNo | 99 | NULL | 5416 | NULL | +----+-------------+-----------+-------+---------------+-----------+---------+------+------+-------+ 1 row in set (0.00 sec)

 

explain返回的各列含義 
select_type 
SIMPLE:簡單SELECT(不使用UNION或子查詢等) 
PRIMARY:個人理解是最外層的select 
UNION : UNION中的第二個或後面的SELECT語句 
DEPENDENT UNION : UNION中的第二個或後面的SELECT語句,取決於外面的查詢 
UNION RESULT : UNION的結果 
SUBQUERY : 子查詢中的第一個SELECT 
DEPENDENT SUBQUERY : 子查詢中的第一個SELECT,取決於外面的查詢 
DERIVED:派生表的SELECT(FROM子句的子查詢)

table: 顯示這條sql涉及到的那些表,有時不是真實的表名字,看到的是derivedx(x是個數字,個人理解是第幾步執行的結果) 
type : 這是重要的列,顯示鏈接是那種類型,從最好到最壞依次是:const(常數查找,主鍵或惟一索引查找),system:(這是const聯接類型的一個特例。表僅有一行知足條件),eq_reg: (範圍查找,主鍵或者索引範圍的查找),ref(常見鏈接查詢,一個表是基於某個索引的查找),range(基於索引範圍的查找),index(索引的掃描),all(表掃描) 
possible_keys : 顯示可能用到的索引。若是爲空,沒有能夠用到的索引 
key : 實際使用的索引,若是爲空則沒有使用索引 
key_len : 使用索引的長度,在不損失精確性的狀況下,長度越短越好 
ref: 顯示索引的哪一列被使用了。若是可能的話,是一個常數。 
rows : mysql認爲必須檢查的用來返回數據的行數 
Extra : 擴展列,須要注意的返回值: 
Using filesort 看到這個就須要優化了。mysql須要經過額外的步驟發現如何對返回行進行排序,他須要根據鏈接類型以及存儲排序鍵值和匹配條件的全部行的行指針進行全部行排序,一般發生order by操做上。 
Using temporary 看到這個就須要優化了,mysql須要創建臨時表來存儲結果,這一般發生在對不一樣的列集進行order 
by操做上,而不是group by操做。

具體的實例MAX()優化

案例1:咱們須要lm_d_plan的最後建立時間是什麼

mysql> select max(createtime) from lm_d_plan; +-----------------+ | max(createtime) | +-----------------+ | 20150528143338 | +-----------------+ 1 row in set (0.11 sec) mysql> explain select max(createtime) from lm_d_plan; +----+-------------+-----------+------+---------------+------+---------+------+-------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------+------+---------+------+-------+-------+ | 1 | SIMPLE | lm_d_plan | ALL | NULL | NULL | NULL | NULL | 55562 | NULL | +----+-------------+-----------+------+---------------+------+---------+------+-------+-------+ 1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

select_type 這是一個簡單的查詢 
table 查詢的是lm_d_plan 表 
type ALL說明是全表掃描 
rows 須要掃描的行數55562 
經過以上信息,顯然是一個低效的sql,隨着數據量增大,它的IO也隨之增大,極可能拖慢整個服務器,說明確定是須要優化的。 
策略:一般狀況下在 createtime上創建索引

mysql> create index createime on lm_d_plan(createtime); Query OK, 0 rows affected (0.65 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select max(createtime) from lm_d_plan; +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ 1 row in set (0.00 sec) mysql> select max(createtime) from lm_d_plan; +-----------------+ | max(createtime) | +-----------------+ | 20150528143338 | +-----------------+ 1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Extra 返回 Select tables optimized away ,意思就是並不須要查詢具體的表,直接經過查詢索引就能夠完成。由於索引是順序排列的,經過索引的統計信息就能夠得到最大的createime。這也是個覆蓋索引,徹底經過索引的信息就能夠完成查詢。

案例2:咱們須要找出各部門lm_c_dept的提報的需求計劃lm_d_plan是多少

mysql> SELECT d.deptName,count(*) from lm_d_plan p INNER JOIN lm_c_dept d ON d.deptNo = p.FK_deptNo GROUP BY p.FK_deptNo; +--------------------------------+----------+ | deptName | count(*) | +--------------------------------+----------+ | xx一部 | 1573 | | xx板塊業務部 | 282 | | yy板塊業務二部 | 781 | | aa塊業務部 | 4753 | | cc塊業務部 | 12252 | | dd板塊業務部 | 6827 | | xx設備業務部 | 4442 | | cccc物資業務部 | 26196 | +--------------------------------+----------+ 8 rows in set (0.09 sec) mysql> explain SELECT d.deptName,count(*) from lm_d_plan p INNER JOIN lm_c_dept d ON d.deptNo = p.FK_deptNo GROUP BY p.FK_deptNo\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: d(多是表中間有下劃線顯示有問題lm_c_dept ) type: ALL possible_keys: deptNo key: NULL key_len: NULL ref: NULL rows: 11 Extra: Using temporary; Using filesort select_type 這是一個簡單的查詢 table 查詢的是lm_c_dept 表 type ALL說明是全表掃描,由於沒有where條件,表掃描也能夠說是正常的。 possible_keys 可能用到的索引是deptNo rows 須要掃描的行數11 Extra 既使用了臨時表,也用了文件排序,這是效率極低的。 *************************** 2. row *************************** id: 1 select_type: SIMPLE table: p(多是表中間有下劃線顯示有問題lm_d_plan ) type: ref possible_keys: FK_deptNo key: FK_deptNo key_len: 99 ref: lms.d.deptNo(這裏應該是lms.lm_c_dept.deptNo) rows: 3968 Extra: Using index select_type 這是一個簡單的查詢 table 查詢的是lm_d_plan 表 type ref常見鏈接查詢,一個表是基於索引的查找 possible_keys 可能用到的索引是FK_deptNo key 使用的索引是FK_deptNo key_len 索引的長度是99 ref lms.lm_c_dept.deptNo這列索引被使用了 rows 須要掃描的行數3968 Extra 使用索引 2 rows in set (0.00 sec)

 

策略:經過以上分析,顯然最須要優化的地方是臨時表和文件排序了,由於這種低效率查詢會直接對服務器產生極大的資源消耗,同時影響整個服務器性能。經過優化sql語句來改善這種狀況。

mysql> explain SELECT d.deptName,c.ct FROM lm_c_dept d INNER JOIN(SELECT p.FK_deptNo,count(*) ct from lm_d_plan p GROUP BY p.FK_deptNo) c ON d.deptNo = c.FK_deptNo\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: d(多是表中間有下劃線顯示有問題lm_c_dept ) type: ALL possible_keys: deptNo key: NULL key_len: NULL ref: NULL rows: 11 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> type: ref possible_keys: <auto_key0> key: <auto_key0> key_len: 99 ref: lms.d.deptNo rows: 555 Extra: NULL *************************** 3. row *************************** id: 2 select_type: DERIVED table: p(多是表中間有下劃線顯示有問題lm_d_plan ) type: index possible_keys: FK_deptNo key: FK_deptNo key_len: 99 ref: NULL rows: 55562 Extra: Using index 3 rows in set (0.00 sec)

 

  • 34
  • 35

經過執行計劃能夠看出Extra已經沒有Using filesort 和 Using temporary臨時表和使用文件排序已經改善。執行結果也加從0.09 sec 加快到0.02 sec,若是有條件,最好在子查詢裏增長過濾,而不是外查詢。

索引優化

如何選擇合適的列創建索引

  1. 從where從句中,group by從句中,order by從句中,on從句中,select列中,還能夠創建覆蓋索引(也就是指索引包含全部查詢的列,直接查索引就能夠完成任務的),
  2. 索引字段越小越好,由於mysql存儲是已頁爲單位的,若是從單頁中獲取更多的結果,減小IO操做,那麼就起到了積極的做用。
  3. 離散程度越高的列放在聯合索引的前面,由於離散度越大的列可選擇性越高,能夠經過count統計函數查看離散度
mysql> select count(distinct cartCode),count(distinct cartlineNum) from lm_d_plan; +--------------------------+-----------------------------+ | count(distinct cartCode) | count(distinct cartlineNum) | +--------------------------+-----------------------------+ | 40782 | 341 | +--------------------------+-----------------------------+ 1 row in set (0.14 sec)

 

因而可知,若是將cartCode和cartlineNum創建聯合索引,cartCode應該放到前面。 
create index cartCode_cartlineNum on lm_d_plan(cartCode,cartlineNum);

SQL及索引優化

索引的維護和優化之重複及冗餘索引 
重複索引是最同一列屢次創建索引,好比在primay key列創建惟一索引,由於primay key已是主鍵索引了。再創建惟一索引就索引重複了。 
冗餘索引是指多個索引的前綴列相同,或者聯合索引中包含的主鍵索引。(Innodb會在每一個索引的後面附加主鍵索引。) 
經常使用索引操做 
查看索引 
show index from tablename; 
刪除索引,其中,在前面的兩條語句中,都刪除了table_name中的索引index_name。而在最後一條語句中,只在刪除PRIMARY KEY索引中使用,由於一個表只可能有一個PRIMARY KEY索引,所以不須要指定索引名。

drop index index_name on table_name ; alter table table_name drop index index_name ; alter table table_name drop primary key ;
  • 1
  • 2
  • 3

建立索引:

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [USING index_type] ON tbl_name (index_col_name,...) index_col_name: col_name [(length)] [ASC | DESC]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用alter的方法建立索引

alter table table_name add index index_name (column_list) ; alter table table_name add unique (column_list) ; alter table table_name add primary key (column_list) ;
  • 1
  • 2
  • 3

對於CHAR和VARCHAR列,只用一列的一部分就可建立索引。建立索引時,使用col_name(length)語法,對前綴編制索引。前綴包括每列值的前length個字符。BLOB和TEXT列也能夠編制索引,可是必須給出前綴長度。

此處展現的語句用於建立一個索引,索引使用列名稱的前5個字符。 
create index itemsCode on lm_d_plan(itemsCode(5)); 
由於多數名稱的前10個字符一般不一樣,因此此索引不會比使用列的全名建立的索引速度慢不少。另外,使用列的一部分建立索引可使索引文件大大減少,從而節省了大量的磁盤空間,有可能提升INSERT操做的速度。

前綴最長爲255字節。對於MyISAM和InnoDB表,前綴最長爲1000字節。注意前綴的限長以字節計,而CREATE INDEX語句中的前綴長度指的是字符的數目。對於使用多字節字符集的列,在指定列的前綴長度時,要考慮這一點。

在MySQL中: 
· 只有當您正在使用MyISAM, InnoDB或BDB表類型時,您能夠向有NULL值的列中添加索引。 
· 只有當您正在使用MyISAM, BDB或InnoDB表類型時,您能夠向BLOB或TEXT列中添加索引。

一個index_col_name規約能夠以ASC或DESC爲結尾。這些關鍵詞未來能夠擴展,用於指定遞增或遞減索引值存儲。目前,這些關鍵詞被分析,可是被忽略;索引值均以遞增順序存儲。

部分儲存引擎容許在建立索引時指定索引類型。index_type指定語句的語法是USING type_name。不一樣的儲存引擎所支持的type_name值已顯示在下表中。若是列有多個索引類型,當沒有指定index_type時,第一個類型是默認值。

存儲引擎 容許的索引類型
MyISAM BTREE
InnoDB BTREE
MEMORY/HEAP HASH, BTREE

示例:

CREATE TABLE lookup (id INT) ENGINE = MEMORY; CREATE INDEX id_index USING BTREE ON lookup (id);
  • 1
  • 2

TYPE type_name能夠做爲USING type_name的同義詞,用於指定索引類型。可是,USING是首選的格式。另外,在索引規約語法中,位於索引類型前面的索引名稱不能使用TYPE。這是由於,與USING不一樣,TYPE不是保留詞,所以會被認爲是一個索引名稱。 
若是您指定的索引類型在給定的儲存引擎中不合法,可是有其它的索引類型適合引擎使用,而且不會影響查詢功能,則引擎應使用此類型。 
FULLTEXT索引只能對CHAR, VARCHAR和TEXT列編制索引,而且只能在MyISAM表中編制。 
SPATIAL索引只能對空間列編制索引,而且只能在MyISAM表中編制。 
索引的維護和優化之pt-duplicate-key-checker 
使用方法:pt-duplicate-key-checker -uroot -p 'xxxx' -h 127.0.0.1 
[root@localhost ~]# pt-duplicate-key-checker -uroot -p ‘123456’ -h localhost

# ########################################################################
# lms.lm_d_plan                                                           
# ########################################################################

# oldPlanNum is a left-prefix of oldPlanNum_oldPlanlineNum
oldPlanNum索引 已是oldPlanNum_oldPlanlineNum聯合索引的左前綴了。也就是說查詢oldPlanNum能夠直接命中oldPlanNum_oldPlanlineNum聯合索引,而毋需再建立oldPlanNum 索引了。
# Key definitions:
關鍵的定義
#   KEY `oldPlanNum` (`oldPlanNum`),
#   KEY `oldPlanNum_oldPlanlineNum` (`oldPlanNum`,`oldPlanlineNum`),
# Column types:
列信息
#     `oldplannum` varchar(20) default null comment '原始號'
#     `oldplanlinenum` int(4) default null comment '原始行號'
# To remove this duplicate index, execute:
建議是remove這個重複的索引,執行如下命令
ALTER TABLE `lms`.`lm_d_plan` DROP INDEX `oldPlanNum`; 一樣的錯誤類型以下: # cartCode is a left-prefix of cartCode_cartlineNum # Key definitions: # KEY `cartCode` (`cartCode`), # KEY `cartCode_cartlineNum` (`cartCode`,`cartlineNum`), # Column types: # `cartcode` varchar(20) default null comment '?????' # `cartlinenum` int(4) default null comment '?????' # To remove this duplicate index, execute: ALTER TABLE `lms`.`lm_d_plan` DROP INDEX `cartCode`; # ######################################################################## # Summary of indexes # ######################################################################## # Size Duplicate Indexes 7000812 # Total Duplicate Indexes 2 # Total Indexes 155

 

經過pt-dulication-key-checker能夠高效的優化數據庫的索引。 
索引的維護和優化之刪除無用索引pt-index-usage 
pt-index-usage有空補充,你們有興趣能夠本身研究一下。

數據庫結構優化

選擇合適的數據類型

  1. 能夠存下咱們數據的最小數據類型
  2. 使用簡單的數據類型,int類型要比varchar類型,mysql處理更簡單
  3. 儘可能使用not null字段,由於innodb存儲類型,儘可能設置默認值
  4. 儘可能少用text類型,非用不可的話須要考慮是否分表的方式解決。 
    範例以下: 
    用int來存儲時間,from_unixtime(),unix_timestamp()兩個函數進行轉換。
insert into tablename(timestr) values( unix_timestamp('2015-05-05 20:20:00')); select from_unixtime(timestr) from tablename;
  • 1
  • 2

**用bigint存儲ip地址,利用inet_aton(),inet_ntoa()兩個函數進行轉換。

insert into tablename(ipaddr) values( inet_aton('192.168.1.1')); select inet_ntoa(ipaddr) from tablename;
  • 1
  • 2

表的範式優化

表的範式化

範式化是指數據庫設計遵循的設計規範,目前說的範式化通常是指第三設計範式,也就是要求表中非關鍵字段對任意候選關鍵字段不存在傳遞函數依賴則符合第三範式。 
示例:

商品名稱 價格 重量 有效期 分類 分類描述
可樂 3.5 600ml 2015-09-09 酒水飲料 碳酸飲料
黃瓜 1.0 100g   生鮮食品 蔬菜

存在如下傳遞函數依賴管理 
商品名稱—>分類—>分類描述 
很顯然若是商品名稱是關鍵字段(任意候選關鍵字段),那麼非關鍵字段分類描述依賴分類依賴商品名稱。也就是說分類描述傳遞函數依賴關鍵字段商品名稱。 
進行標準第三範式化修改後: 
Table 1

商品名稱 價格 重量 有效期
可樂 3.5 600ml 2015-09-09
黃瓜 1.0 100g  

Table 2

分類 分類描述
酒水飲料 碳酸飲料
生鮮食品 蔬菜

Table 3

商品名稱 分類
可樂 酒水飲料
黃瓜 生鮮食品

表的反範式化

反範式化是爲了查詢效率的考慮,把本來符合第三範式的表適當的增長冗餘,以達到優化查詢的目的,反範式化是一種以空間換取時間的策略。 
示例:

訂單表:訂單ID,訂單編號,用戶ID,下單時間,訂單狀態
用戶表:用戶ID,姓名,電話,地址
訂單商品表:訂單ID,商品ID,訂單數量,商品價格
商品表:商品ID,名稱,描述
  • 1
  • 2
  • 3
  • 4

需求描述:須要給客戶展示訂單的總體概況信息,包括訂單,價格,商品信息,構造的SQL以下:

SELECT a.訂單編號,b.姓名,b.電話,b.地址,SUM(c.訂單數量 * c.商品價格) as 訂單價格 FROM 訂單表 a LEFT JOIN 用戶表 b ON a.用戶ID = b.用戶ID LEFT JOIN 訂單商品表 c ON a.訂單ID = c.訂單ID
  • 1
  • 2
  • 3
  • 4
  • 5

咱們要查詢這些信息,至少關聯三張表,表結構就是這樣的。 
若是進行一些反範式化設計,將會大大改善上述查詢效率極低的SQL。

訂單表:訂單ID,訂單編號,用戶ID,下單時間,訂單狀態,訂單價格,姓名,電話,地址
用戶表:用戶ID,姓名,電話,地址
訂單商品表:訂單ID,商品ID,訂單數量,商品價格
商品表:商品ID,名稱,描述
  • 1
  • 2
  • 3
  • 4

一樣的需求,構造SQL以下:

SELECT a.訂單編號,a.姓名,a.電話,a.地址,a.訂單價格 FROM 訂單表 a
  • 1
  • 2
  • 3

顯然,在優化數據庫的時候,進行一些適當的反範式化,會在很大程度上提高執行效率。

表的垂直拆分

所謂垂直拆分就是把原來一個不少列的表拆分紅多個表,解決表的寬度問題,一般垂直拆分能夠按如下原則進行: 
1. 把不經常使用的字段單獨存放在一張表中 
2. 把大字段獨立存放在一張表中 
3. 把常用的字段存放到一塊兒

表的水平拆分

水平拆分是爲了解決單表數據量過大的問題,水平拆分每個表結構都是同樣的。 
如何將數據平均插入到N張表呢? 
經常使用的水平拆分的方法: 
1. 對ID進行hash運算,若是要拆分紅5個表,則使用mod(ID,5)(取模)取出0-4個值,取模後0插0表,取1插1表 
2. 針對不一樣的hashID,把數據存儲到不一樣的表中 
拆分後所面臨的挑戰: 
1. 跨分區進行查詢 
2. 統計及後臺報表操做。 
建議:先後臺表進行拆開,前臺考慮效率因此進行水平拆分,後臺考慮方便完整的數據統計,單獨生成統計及報表所用的表

系統配置優化

操做系統配置優化

數據庫是基於操做系統之上的,目前大多數mysql都運行在linux上,因此對操做系統的一些配置也會影響到mysql 的性能,下面就列出經常使用的優化配置。 
網絡方面的配置,要修改/etc/sysctl.conf文件 
1. 增長TCP支持的隊列數 
net.ipv4.tcp_max_syn_backlog=65535 
2. 減小斷開鏈接時間,資源回收,因爲TCP/IP是有三個狀態的,爲了加快timewait狀態回收,優化如下參數。

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
  • 1
  • 2
  • 3
  • 4

優化打開文件數,由於mysql的表都是已文件格式存儲的,對innodb類型的查詢都會打開表文件,因此增長文件打開數在必定程度上提高了mysql查詢效率。可使用ulimit -a查看目錄的各限制,能夠修改/etc/security/limits.conf 文件,增長如下內容增長文件打開數限制

* soft nofile 65535 * hard nofile 65535
  • 1
  • 2

修改先後對比:

[root@localhost ~]# ulimit -a ... open files (-n) 1024 ... [root@localhost ~]# ulimit -a ... open files (-n) 65535 ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

除此之外最好在mysql所在服務器關閉iptables selinux等防火牆軟件,經過硬件防火牆等方式解決安全問題。

MYSQL配置優化

mysql能夠經過啓動時指定配置參數和使用配置文件進行配置,在大多數狀況下配置文件存儲在/etc/my.cnf或是/etc/mysql/my.cnf。windows通常在mysql目錄中my.ini文件。mysql查找配置文件的順序能夠經過如下命令獲取。 
$ /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options' 
注意,若是多個位置存在配置文件,則後面的會覆蓋前面的配置。

mysql經常使用配置優化

innodb_buffer_poor_size: 
很是重要的參數,用來配置Innodb的緩衝池,若是數據庫中只有innodb表,則推薦配置爲總內存的75%。

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; +--------+----------+ | ENGINE | Total MB | +--------+----------+ | CSV | 0.0 | | InnoDB | 79.3 | | MyISAM | 0.7 | +--------+----------+ 3 rows in set (0.59 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

innodb_buffer_poor_size >= Total MB(數據+索引大小),若是達不到要求,設置儘量的大就能夠。

innodb_buffer_poor_instances:(mysql5.5+以上纔有) 
能夠控制緩衝池個數,默認狀況下只有一個緩衝池,mysql中有些操做須要獨佔使用緩衝池,那麼就可能形成阻塞。若是分紅多分能夠,通常能夠分爲4到8份,大小=總緩衝大小/分數。 
innodb_log_buffer_size: 
innodb log 緩衝大小,因爲日誌最長每秒種就會刷新,因此不須要配置多大,只要可以存下1秒鐘事務數據就能夠了。 
innodb_flush_log_at_trx_commit:(聽說很管用) 
關鍵參數,對innodb磁盤IO影響很大,默認爲1,能夠設置0,1,2三個值,通常建議爲2,但若是數據安全性較高,則設置爲1.

抱怨Innodb比MyISAM慢 100倍?那麼你大概是忘了調整這個值。默認值1的意思是每一次事務提交或事務外的指令都須要把日誌寫入(flush)硬盤,這是很費時的。特別是使用電 池供電緩存(Battery backed up cache)時。設成2對於不少運用,特別是從MyISAM錶轉過來的是能夠的,它的意思是不寫入硬盤而是寫入系統緩存。日誌仍然會每秒flush到硬 盤,因此你通常不會丟失超過1-2秒的更新。設成0會更快一點,但安全方面比較差,即便MySQL掛了也可能會丟失事務的數據。而值2只會在整個操做系統 掛了時纔可能丟數據。

innodb_read_io_threads 和 innodb_write_io_threads :(mysql5.5+以上可調整) 
決定innodb讀寫IO的進程數量,默認爲4。可根據服務器具體狀況設置 
innodb_file_per_table:建議設爲ON。 
關鍵參數,控制innodb每一個表獨立使用的表空間,默認爲OFF,也就是全部表都會創建在共享表空間中。共享表空間IO成爲瓶頸,順序寫入的時候,共享表空間只有一個文件,併發寫入的時候效率很低。共享表空間是沒法單獨收縮的,當刪除一個很大的數據後,以後把全部數據導出後導入才能夠收縮。 
innodb_stats_on_metadata:默認爲ON,建議OFF 
決定mysql在什麼狀況下刷innodb表的統計信息。優化器在工做中須要經過統計信息獲取索引等系想你,保證優化器使用到正確的索引。可是若是設爲ON,在進行show,select等操做時都會進行沒必要要的刷新,因此建議在修改數據庫後人爲進行刷新。

第三方配置工具

PERCONA CONFIGURATION WIZARD FOR MYSQL 
http://tools.percona.com/wizard 
介紹略,你們能夠本身研究

服務器硬件優化

如何選擇CPU

  1. mysql不少操做都使用單核cpu
  2. mysql對cpu核數支持並非越多越快,建議不要超過32核。 
    建議選擇單核頻率高的cpu

Disk IO 優化

建議RAID1+0 :就是RAID1和RAID0結合,兼顧性能和數據安

相關文章
相關標籤/搜索