MYSQL優化

MYSQL優化是一個很是大的課題,這篇文章主要介紹了跟MYSQL相關的4個方面,若是想深刻研究能夠查下相關資料。mysql

 


 

1、服務器級別優化web

2、操做系統級別優化sql

3、MYSQL級別優化數據庫

4、SQL級別優化服務器

 


 

1、服務器級別優化併發

1. 服務器選型app

SUN小型機、DELL730xd、HPDL380、IBM3850、雲服務等ide

2. CPU個數、內存大小函數

大內存,高IO,是現代基於web的數據庫的必備工具

3. 磁盤:SAS、SSD、FIO卡

減少尋道時間、旋轉時間、傳輸時間

4. RAID卡電池,RAID級別

WriteBack, ReadAheadNone,Direct,NoWrite Cache if Bad BBU

5. 其餘:網卡等

2、操做系統級別優化

1. I/O調度策略

NOOP、CFQ、Deadline、Anticipatory

臨時生效:echo 「dadline」 >/sys/block/sda/queue/scheduler

永久生效:/etc/grub.conf中kernel後加elevator=deadline(須要重啓)

2. SWAP使用策略

echo"vm.swappiness=10">>/etc/sysctl.conf

https://www.percona.com/blog/2014/04/28/oom-relation-vm-swappiness0-new-kernel/

3. 文件系統

ext三、ext4仍是使用xfs

4.  避免NUMA問題

numactl --interleave=all便是容許全部的處理器能夠交叉訪問全部的內存

5. /tmp分區

tmpfs  /dev/shm  tmpfs  defaults  00

設置tmpdir=/tmp以後,某些習慣性把文件寫到tmp下的人要改一改習慣了,由於這些文件佔用的是內存不是磁盤,並且若是不重啓的話是一直佔用

6. CPU

關閉服務器的節能模式

查看kondemand進程運行狀況:

ps -ef |grepkondemand

3、MYSQL級別優化

1. 版本的選擇,除官方版本外


2.   最重要的參數選項調整

    default-storage-engine=innodb

    innodb_buffer_pool_size、key_buffer_size

    innodb_flush_log_at_trx_commit、sync_binlog

    innodb_file_per_table

    long_query_time

    max_connection

3. Schema設計規範及SQL使用 

設計自增列作主鍵

字段屬性儘可能都加上NOT NULL約束

儘量不使用TEXT/BLOB類型

多表聯接查詢時,結果集小的做爲驅動表

複合索引的選擇

4. 其餘建議(pt-toolkit、orzdba等工具使用)

pt-duplicate-key-checker檢查並刪除重複的索引

pt-index-usage檢查並刪除使用頻率很低的索引

pt-query-digest進行慢查詢分析

pt-kill殺掉超長時間的SQL請求

pt-online-schema-change來完成大表的ONLINE DDL需求

pt-table-checksum、pt-table-sync來檢查並修復mysql主從複製的數據差別

4、Sql級別優化

案例一:URL列索引優化

T_VIDEO表的SQL操做緩慢,出現性能問題,抓取慢查詢,發現主要由大量以下相似的SQL語句執行緩慢:

     select … … (這裏是表的全部字段)

      fromT_VIDEO video0_  where        video0_.VIDEO_PATH='http://www.youtube.com/watch?v=ZjxzF3fNQuI'limit 1;

諮詢開發同窗,這個是爲了確認某條數據是否已經存在,須要查詢所有字段並逐一比較。而且表中只有ID列主鍵,無其餘索引。

 

那麼如何緩解這種狀況呢?如何確認某條數據是否存在?

 

制定方案:

1)經過「主鍵(或者惟一約束)」來判斷該行數據是否存在,存在的話直接覆蓋更新。

2)堅定不建議逐個字段查詢出來一一比較!由於首先,查詢語句執行時的Sending Data的時間會加長,當數據量達到必定程度的時候還會產生大量的臨時表;其次須要消耗CPU和時間來作比較,性價比不高。

存在問題:

1)存儲的URL前n位基本相同或者只有幾種,其次URL可能會很長;

2)若是仍是使用傳統的B-tree索引的話,索引會變得很是大且效率不高

解決方案:

1)你們知道hash索引性能要比B-tree索引好,且基於數字類型的索引性能要比基於字符串的索引好,那麼若是咱們將URL作一個hash而後在這個hash值上作索引,查詢的時候將URL和hash做爲where條件,既實現了基於索引的查詢,又下降了索引的大小。

 

2)咱們可使用CRC32函數來實現。

在數據庫中創建冗餘列URL_CRC,用於存儲URL的hash值,這裏在插入的時候使用CRC32(「……」)函數,返回值是數字類型

 

3)在這一列上創建索引

查詢的時候使用WHEREURL_CRC=CRC32(「……」)  AND URL=」……」,查詢優化器會自動使用索引列URL_CRC,即便有重複值,還能夠經過URL列二次篩選

案例二:百萬級數據分頁

項目中數據量已經動輒百萬,且會使用到分頁。

開發同窗在代碼中進行分頁通常會這麼寫:

select *from `table` order by iddesc limit 1000000,50;

但是當數據量到達百萬、千萬或者更多的時候,極可能會出現分頁查詢性能降低明顯的狀況,可能從以前的毫秒到如今的幾秒或者幾十秒。這是爲何呢?


select * from `table` order by id desc limit100,50;         0.016秒
select * from `table` order by id desc limit1000,50;       0.047秒
select * from `table` order by id desc limit10000,50;      0.094秒

select *from `table` order by iddesc limit 100000,50;    0.43秒

select *from `table` order by iddesc limit 1000000,50;  2.23秒

 

其實limit在實際執行的時候是「查詢1000050行數據,而後丟掉前面的1000000行,返回剩下的50行」,是否是發現了很驚悚的問題了呢?! 浪費了大量的I/O性能啊。

 

如何優化?

        

代碼級:

程序裏維護一個變量,用於記錄當前要顯示的頁的數據起始值,SQL語句中使用這個變量的值; 

數據庫級(SQL級)

利用覆蓋索引

selectid fromFROM `tablle`  order by id desclimit 1000000,50;

或者

SELECT* FROM`table` WHERE id <= (SELECT id FROM `table` ORDER BY id desc LIMIT1000000,1) ORDER BY id desc LIMIT 50;

或者

select* FROM`table` AS t1 JOIN (SELECT id FROM `table` ORDER BY id desc LIMIT1000000,1) ASt2 WHERE t1.id<=t2.id order by t1.id desc limit 50;

原理就是記錄住當前頁id的最大值和最小值,計算跳轉頁面和當前頁相對偏移,因爲頁面相近,這個偏移量不會很大,這樣的話大大減小掃描的行數。

或者

select* from`table` where id between 1000001 and 1000050;

原理和上面相似,直接定位須要掃描的數據(頁),可是若是這個跨度區間內的ID有缺失,那麼查詢出的數據就小於50條了,這一點必定要注意。

案例三:使用簡單SQL去完成複雜功能

原來的執行腳本:

INSERTINTOT_APP_APK_ID_DOWNLOAD

(APK_ID,APP_UPDATE_TIME,DOWNLOAD_NUM)

selecta.APK_ID,a.UPDATE_TIME,IFNULL(b.TOTAL_NUM,0)

from

(selectMAX(id)id,max(UPDATE_TIME) UPDATE_TIME,APK_ID from T_APP GROUP BY APK_ID) as a

LEFTJOIN

T_APP_DOWNLOAD_STATIbon a.id=b.APP_ID;

 

4000W數據,所需時間15min+

 

簡化SQL語句的重要方法就是採用臨時表暫存中間結果,可是,臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,後面的查詢就在temptable中了,這能夠避免程序中屢次掃描主表,也大大減小了程序執行中「共享鎖」阻塞「更新鎖」,減小了阻塞,提升了併發性能。

 

分拆後執行計劃步驟:

1. 創建中間表

CREATETABLE `T_APP_TMP` (

`ID`int(11) NOT NULL AUTO_INCREMENTCOMMENT '主鍵',

`APP_ID`int(11) NOT NULL DEFAULT'0' COMMENT 'APK 惟一標識',

`UPDATE_TIME`datetime NOT NULLDEFAULT '2000-01-01 00:00:00' COMMENT 'APK更新時間',

`APK_ID`varchar(150) NOT NULLDEFAULT '' COMMENT 'APK 惟一標識',

   PRIMARY KEY (`ID`),

KEY`idx_app_appid_code` (`APP_ID`)

)ENGINE=InnoDBAUTO_INCREMENT=1DEFAULT CHARSET=utf8 COMMENT='應用表';

2. 將數據插入中間表

INSERTINTOT_APP_TMP(APP_ID,UPDATE_TIME,APK_ID) select MAX(id)id,max(UPDATE_TIME)UPDATE_TIME,APK_ID from T_APP GROUP BY APK_ID;

3. 將最終結果插入結果表

INSERT INTOT_APP_APK_ID_DOWNLOAD (APK_ID,APP_UPDATE_TIME,DOWNLOAD_NUM)

selecta.APK_ID,a.UPDATE_TIME,IFNULL(b.TOTAL_NUM,0)

fromT_APP_TMP as a LEFT JOINT_APP_DOWNLOAD_STATI b

ona.APP_ID=b.APP_ID;

4. 將中間表刪除

DROP TABLET_APP_TMP;

 

按照這個步驟執行,總共不超過5min鍾。

相關文章
相關標籤/搜索