MySQL多方面優化

目錄

一、如何發現SQL語句是否有性能問題?

(一)開啓慢查詢日誌
(二)慢查詢分析工具

二、如何通過分析工具查出具體哪條SQL語句有問題?

三、如何對找到性能不好的SQL語句進行分析?

(一)explain執行計劃

四、SQL語句具體優化

五、索引的建立與優化

最左前綴原則
(一)索引優化選擇
(二)索引的維護

六、數據庫結構上的優化

(一)選擇合適的數據類型
(四)表的範式化和反範式化
(三)表的垂直和水平拆分

七、數據庫系統配置的優化

(一)操作系統配置優化
(二)數據庫配置優化

八、服務器硬件的優化


一、如何發現SQL語句是否有性能問題?

開啓MySQL慢查詢日誌對有效率問題的SQL進行監控。
->
但使用慢查日誌可能導致日誌文件大量增加。
->
使用慢查日誌分析工具

(一)開啓慢查詢日誌

開啓慢查詢日誌開啓,配置.cnf文件

//開啓
slow_query_log          = 1
//位置
slow_query_log_file     = /var/log/mysql/mysql-slow.log
//查詢超過x時間將寫入
long_query_time = 0
//未使用索引也記錄
log-queries-not-using-indexes = 1
//不包括用戶主機信息和查詢時間
#log-short-format = 1
//將慢管理語句也記錄(alter、optimize等)
log-slow-admin-statements = 1

//重啓mysql服務器
service mysql restart

(二)慢查詢分析工具

1、mysqldumpslow(mysql自帶),查詢結果少,包行信息不充足

例子:
查詢前三條日誌

mysqldumpslow -t 3 /慢日誌位置 | more
2、pt-query-digest
安裝

Ubuntu下安裝pt-query-digest工具

sudo apt-get install update //更新源
sudo apt-get install percona-toolkit  //安裝,pt-query-digest工具是percona-toolkit工具集合中的一個

關於percona-toolkit包含工具有哪些
官網:https://www.percona.com/doc/percona-toolkit/3.0/index.html


比較詳細的pt-query-digest參數說明

https://blog.csdn.net/seteor/article/details/24017913

所使用版本所有參數

pt-query-digest --help
輸出方式:
  • 輸出到文件
    pt-query-digest slowlog > slow_log.report;

  • 輸出到數據庫表
    pt-query-digest slow.log -review
    h=127.0.0.1,D=‘數據庫名’,p=用戶名,u=密碼,t=表名
    –create-review-table
    –review-history t=表名

例子
查詢日誌

pt-query-digest 慢日誌位置 | more

pt-query-digest查詢結果內容介紹

第一部分:總體統計結果
Orverall:總共多少條查詢
unique:唯一查詢量,對查詢條件進行參數化後總共多少條查詢
Time range:查詢時間範圍
total min max avg(平均數) 95%(重點,類似去掉最小最大值) median(中位數,排序取中)
exec time 查詢時間
lock time 鎖定時間
rows sent 發送行數
rows examine 掃描行數
query size 查詢行數

第二部分:查詢分組統計結果。
對查詢進行參數化並分分組,分析。結果按總執行時長,從大到小排序
Rank:所有語句的排名,默認按查詢時間降序排列,通過–order-by指定
Query ID :語句的ID,(去掉多餘空格和文本字符,計算hash值)
Response time:響應時間,佔所有響應時間的百分比
Calls:查詢執行次數
R/Call:平均響應時間
V/M:響應時間Variance-to-mean的比率
Item:查詢語句一部分

第三部分:每一條查詢的詳細分析結果
最上面的表格列出了執行次數、最大、最小、平均、95%等各項目的統計。
Databases: 庫名
Users: 各個用戶執行的次數(佔比)
Query_time distribution : 查詢時間分佈, 長短體現區間佔比
Tables: 查詢中涉及到的表
Explain: 示例

二、如何通過分析工具查出具體哪條SQL語句有問題?

1.查詢次數多且每次查詢佔用時間長的SQL

通常爲pt-query-digest分析的前幾條

2. IO大的SQL

注意pt-query-digest分析中農的Rows examine項。掃描行數越多IO越大

3. 未命中索引的SQL

注意pt-query-digest分析中Rows examineRows Send的對比。如果掃描行數遠遠大於發送行數,說明索引的命中率並不高,判定基本使用了索引掃描或者是表掃描的方式進行查詢。

三、如何對找到性能不好的SQL語句進行分析?

(一)使用explain從句進行分析,執行計劃解讀↓

例如:explain select customer_id,first_name,last_name from customer;

(二)explain執行計劃結果介紹

  • table:顯示這一行的數據是關於哪張表的
  • type:這是重要的列,顯示連接使用了何種類型。從最好—>最差的連接類型是:const、eq_reg、ref、range、index和ALL
null:優化過程中就已經得到結果,不再訪問表或索引
system:系統查詢
const:常量查詢,一般爲主鍵、唯一索引查找
eq_reg:一般主鍵或者唯一索引的範圍查找。常見關聯主鍵查詢
ref:查找條件列使用了索引但不是主鍵或唯一索引。常見連接查詢。
range:有範圍的索引掃描,相對於index有範圍限制
index:另一種形式的全表掃描,只不過它的掃描順序是按照索引的順序。
all:全表掃描
  • possible_keys:顯示可能應用在這張表中的索引。
  • key:實際使用的索引。如果爲NULL,則沒有使用索引
  • key_len:使用的索引的長度,在不損失精確性的情況下,長度越短越好。
  • ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數
  • rows:MySQL認爲必須檢查的用來返回請求數據的行數
  • extra:包含不適合在其他列中顯示但十分重要的額外信息。
  • Only index:這意味着信息只用索引樹中的信息檢索出的,這比掃描整個表要快。
  • using where: 使用上了where限制,表示MySQL服務器在存儲引擎受到記錄後進行“後過濾”(Post-filter),如果查 詢未能使用索引,Using where的作用只是提醒我們MySQL將用where子句來過濾結果集。
  • impossible where:表示用不着where,一般就是沒查出來啥。
出現下面其中一條信息一般說明sql是需要優化的。
  • Using filesort:(MySQL中無法利用索引完成的排序操作稱爲“文件排序”)當我們試圖對一個沒有索引的字段進行排 序時,就是filesoft。它根據連接類型以及存儲排序和匹配條件的全部行的行指針來排序全部行。
  • Using temporary:(表示MySQL需要使用臨時表來存儲結果集,常見於排序和分組查詢)WHERE和ORDER BY的索引經常無 法兼顧,如果按照WHERE來確定索引,那麼在ORDER BY時,就必然會引起 Using filesort,這就要看是先過濾再排序劃 算,還是先排序再過濾划算。

四、SQL語句具體優化

Count和Max的優化方法
1、Max查找日期支付日期最大值(覆蓋索引)
//未使用索引連接類型爲all
explain select max(payment_date) from payment;
type:all

//創建索引
create index idx_padate on payment(payment_date);

//使用索引後,連接類型爲null直接得到結果
explain select max(payment_date) from payment;
type:null
覆蓋索引:SQL只需要通過索引就可以返回查詢所需要的數據,索引包含了查詢所有的列,而不必通過二級索引查到主鍵之後再去查詢數據。當SQL查詢頻率高,查詢所包含的列比較少,則通過覆蓋索引方式進行優化
2、Count同時查找2006和2007年電影的數量

Count(*)計數包括Null列,而Count(列名)當此列值爲null會跳過不計數

Select count(release_year='2006' or null) as '2006',count(release_year='2007' or null) as '2007' from film;
3、子查詢的優化

通常情況下,需要把子查詢優化成join查詢(假如數據大的話或者分佈式則另說),但在優化時要注意關聯鍵是否有一對多的關係,要注意重複數據。

一對多使用子查詢結果不包括重複,而使用join則會返回重複數據(可用instinct去除)

4、group by的優化

前:

explain select actor.first_name,actor.last_name,count(*)
from sakila.film_action
inner join sakila.actor using(actor_id)
group by film_actor.actor;

此條語句將對錶掃描,因爲沒有使用where限制條件,同時會使用臨時表temporary和文件排序filesort方式。

後:

沒有建立臨時表,節省了大量的IO。一對多的關係,演員表姓名唯一,而電影表存在多個姓名。
explain select a.first_name,a.last_name,c.cnt
from actor join(
    select actor_id,count(*) as cnt from film_actor group by actor_id
) as c
using(actor_id);
5、limit優化

limit常用於分頁,時常會伴隨order by從句使用,因此大多會使用filesort文件排序,造成大量的IO問題
前:

select film_id,description
from film
order by title
limit 50,5;

優化步驟1:使用有索引的列或主鍵進行Order by操作
改用主鍵film_id

select film_id,description
from film
order by film_id
limit 50,5;

優化步驟2.1:針對連續id

select film_id,description
from film
where film_id>55
and film_id<=60
order by film_id;

優化步驟2.2:針對不連續id(貼合實際)

select film_id,description
from film f1
inner join
(select film_id from film limit 55,5) f2
using(film_id)

五、索引的建立與優化

最左前綴原則:顧名思義,就是最左優先。當我們創建了(a,b,c)多列索引,相當於創建了單列索引(a)聯合索引(a,b)以及聯合索引(a,b,c)。


當where c=1時和此索引均不匹配。在where and查詢中可以是:a,ab,abc。因爲數據庫的多列索引是先按a->b->c排序的。

當where a=1 and b>2 and c = 3時,只會走(a,b)索引,因爲b>2時整個排序已被打亂。此時使用就算繼續索引c也沒意義了,因爲索引就是按照順序查找的。更多在下面

(一)索引優化選擇
  1. 在where從句,group by從句,order by從句,on從句中出現的列
  2. 索引字段越小越好
  3. 離散度大的列放到聯合索引的前面(唯一值越多,離散值越高,可選擇性越高)
  sselect * 
  from payment
  where staff_id = 2
  and customer_id = 584;
  例如去重後staff_id計數爲2而customer_id爲584則
  index(customer_id,staff_id)
(二)索引的維護

建立索引可以優化查詢效率,但是會降低寫入效率(寫改刪)

1、重複索引

主鍵約束創建後一定包含唯一索引約束,則不需要對此列建立唯一索引

2、冗餘索引

多個索引的前綴列相同,或是聯合索引中包含了主鍵的索引(innoDB特性會在每個索引後面附加主鍵)

3、查找重複及冗餘索引
(1)手動查找
//切換數據庫,因爲會用到此庫的一些表
use information_schema;

SELECT a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, 
a.INDEX_NAME AS 'index1', b.INDEX_NAME AS 'index2'
FROM information_schema.STATISTICS a 
JOIN information_schema.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
(2)pt-duplicate-key-checker工具查找(percona工具集已裝)

//此工具會找出重複和冗餘索引並提供更改的SQL語句建議

pt-duplicate-key-checker -uroot -p '密碼' -h 127.0.0.1
4、刪除不用到的索引(mysql沒有關於index的統計表,智能通過慢查詢加pt-index-usage工具分析)

//過程比較慢

pt-index-usage -uroot -p '密碼' -h 127.0.0.1
5、使用索引時失效的情況
(1)對索引列進行運算導致索引失效,我所指的對索引列進行運算包括(+,-,*,/,! 等)

錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;

(2)以下使用會使索引失效,應避免使用;

a. 使用 <> 、not in 、not exist、!=
b. like “%_” 百分號在前(可採用在建立索引時用reverse(columnName)這種方法處理)
c. 單獨引用複合索引裏非第一位置的索引列.應總是使用索引的第一個列,如果索引是建立在多個列上, 只有在它的第一個列被where子句引用時,優化器纔會選擇使用該索引。
d. 字符型字段爲數字時在where條件裏不添加引號.
e. 當變量採用的是times變量,而表的字段採用的是date變量時.或相反情況。

https://blog.csdn.net/ustcyy91/article/details/81001669

六、數據庫結構上的優化

(一)選擇合適的數據類型
  1. 使用可以存下數據的最小的數據類型。例如存儲時間數據類型大小:int=timestamp<varchar
  2. 使用簡單的數據類型。例如在mysql中int要比varchar
  3. 儘可能使用Not null約束字段。因innoDB存儲特性,當爲非not null約束字段可能需要額外字段進行存儲,增加I/O開銷。
  4. 儘量少使用text類型,非用不可時組好考略分表(這樣當查詢非字段時查詢速度不會被大數據字段影響)

例如:
使用int來存儲日期類型,利用from_unixtime將int類型時間戳轉換爲日期時間格式,而unix_timestamp將日期格式轉換成int類型

//建表
create table(
    id int auto_increment not null,
    timestr int,
    primary key(id)
);
//插入
insert into test(timestr) values(unix_timestamp('2019-07-04 11:11:11'));
//查詢
select from_unixtime(timestr) from test;
(二)表的範式化和反範式化
(1)範式化
範式化是指數據庫設計的規範,目前說道的範式化一般是指第三範式,也就是要求數據表中不存在非關鍵字段對任意候選字段的傳遞函數依賴則符合第三範式

例如:

分表前

在這裏插入圖片描述

上圖的關鍵字段爲商品名稱,而後面兩個字段存在以下傳遞函數依賴關係:
(商品名稱)->(分類)->(分類描述)

不符合第三範式要求的表存在下列問題:
  1. 數據冗餘(例如上圖中,分類和分類描冗餘字段,關於描述字段往往比較長,一般單獨分一張表則只需一條描述記錄)
  2. 數據的插入異常(例如上圖,每次都需要插入一次分類和分類描述)
  3. 數據的更新異常(例如上圖,單獨更新商品描述信息同時則需要更新所有商品對應的描述)
  4. 數據的刪除異常(例如上圖,當刪除所有商品時,對應的分類和描述也隨之刪除。此分類也就不復存在)
分表後:

在這裏插入圖片描述

(2)反範式化
爲符合第三範式一般採取分表的方式。但在現代網站開發中,連表查詢往往效率低下。甚至當出現分庫情況時還可能需要跨庫查詢。所以採用反範式化適當的增加冗餘,採取以空間(表的數據冗餘)換取時間(查詢時間)的方式優化查詢效率。

例如:

修改前

在這裏插入圖片描述

上圖雖然符合範式化,但是查詢性能比較慢,查詢訂單信息需要採用多表查詢

在這裏插入圖片描述

適當冗餘後:

在這裏插入圖片描述

上圖採用反範式化設計,查詢訂單信息不再需要連表查詢,單表操作即可完成。

在這裏插入圖片描述

(三)表的拆分
(1)垂直拆分(表的寬度問題,一般在表設計時就)

所謂的垂直拆分,就是把原來一個有很多列的表拆分成多個表,這解決了表的寬度問題。通常垂直拆分可以按照以下原則進行:

  1. 把不常用的字段存放在一個表中
  2. 把存儲數據量大的字段獨立存放在一個表中。
  3. 把經常聯合使用的字段放在一起。

例如一張表有字段:
id、name、score、question、answer
其中question和answer爲比較大字段,其餘爲小字段
則可分爲:
表1:id、name、score
表2:id、question、answer
這樣拆分的好處:當只查詢分數時,表掃描則不會掃描到大字段,提高了查詢速度

垂直拆分的優點
  • 簡單明瞭,規則明確
  • 應用程序模塊清晰明確,整合容易
  • 數據維護方便易行,容易定位
垂直拆分的缺點
  • 部分表關聯無法再庫級別完成,需在程序中完成
  • 對訪問及其頻繁且數據量超大的表仍然存在性能瓶頸
  • 事物處理相對更爲複雜
  • 拆分達到一定程度之後,擴展性會遇到限制
  • 過度切分可能會帶來系統過渡複雜而難以維護
(2)水平拆分(表的數據量問題)

表的水平拆分是未了解決單標的數據量過大的問題,水平拆分的表的每一個表的結構都是完全一致的。
例如有一張10億的用戶表表:
拆分成user0-user9或者user0-user99表

水平拆分的優點:
  • 表關聯基本能夠在數據庫端全部完成;
  • 不會存在某些超大型數據量和高負載的表遇到瓶頸的問題
  • 應用程序端整體架構改動相對較少
  • 事務處理相對簡單
  • 只要切分規則能夠定義好,基本上較難遇到擴展性限制
水平拆分的缺點:
  • 切分規則相對更爲複雜,很難抽象出一個能夠滿足整個數據庫的切分規則
  • 後期數據的維護難度有所增加,人爲手工定位數據更困難
  • 應用系統各模塊耦合度較高,可能會對後面數據的遷移拆分造成一定的困難

七、數據庫系統配置的優化

(一)操作系統配置優化

數據庫是基於操作系統的,目前大多數的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

除此之外在大型網站開發時最好使用硬件防火牆,而關閉iptables,selinux等防火牆軟件,因爲軟件防火牆也會影響性能。

更多系統優化參數 https://blog.csdn.net/qq_33936481/article/details/61194213
(二)數據庫配置優化
手動配置
配置文件位置,如果存在多個位置存在配置文件,則後面的會覆蓋前面的

查找所有配置文件命令:

/usr/sbin/mysqld --verbose --help |grep -A 1 'Default options '
常用innoDB參數項說明:
  • innodb_buffer_pool_size
    非常重要的參數,用於配置innodb的緩衝池如果數據庫中只有innodb表,則推薦配置量爲總內存的75%。緩衝池最小配置應大於所有innoDB表及索引的合。innodb_buffer_pool_size >=Total MB
//檢測語句,檢測出每一種引擎表總的大小是多少:
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;
  • innodb_buffer_pool_instances
    MySQL5.5中新增參數,可以控制緩衝池的個數,默認情況下只有一個。
    有些資源是需要獨佔使用的,如果只有一個緩衝池可能會增加阻塞的頻率。如存成多個可以增加查詢的併發性。
    例如配成4或5個,等於緩衝池總大小/4||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。根據cpu的核數來分別調整讀和寫的數量。

  • innodb_file_per_table
    關鍵參數,控制innodb中每一個表使用獨立的表空間,默認爲OFF,也就是所有表都會建立在共享表空間。建議設爲on
    (1)如果所有的innoDB表都使用共享表空間,則共享表空間的I/O會成爲一個瓶頸,併發寫入時效率大大降低。
    (2)innoDB的共享表空間無法單獨收縮,如果刪除一個很大的日誌或不使用的表,則需要導出數據再進行收縮
    (3)開啓後可以快速回收磁盤空間。同時多文件可提高併發的讀寫效率。

  • innodb_stats_on_metadata
    決定了MySQL在什麼情況下會刷新innodb表的統計信息。過高會影響性能。

八、服務器硬件的優化

1、如何選擇CPU?
選擇單核更快的CPU而不是核數更多的CPU
(1)MySQL有些工作只能使用到單核CPU。

Replicate,SQL等

(2)MySQL5.5版對CPU核數的支持不超過32核,並不是越多越好。
2、Disk IO優化

一般建議數據庫使用RAID1+0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章