有時候咱們會遇到:在查詢sql的時候,假若有100w條數據,會出現慢sql告警,這個時候你就應該處處sql日誌來查找緣由了。這裏頗有可能的主要緣由就是沒有命中索引和沒有分頁處理(緣由有不少種,主要分析你的日誌)。那接下來咱們就得去優化sql了。如何優化呢?下面咱們來談談有關的問題。mysql
談到sql性能優化,那咱們就離不開談到大數據量和併發數,MySQL沒有限制單表的最大記錄數,它只是取決於操做系統對文件大小的限制。看錶:算法
從表中咱們能夠看出,若是單錶行數超過500萬行或者單表容量超過2GB,才推薦分庫分表。性能由綜合因素決定,拋開業務複雜度,影響程度依次是硬件配置、MySQL配置、數據表設計、索引優化。500萬這個值僅供參考,並不是鐵律。sql
假如你在操做過超過4億行數據的單表,你能夠用分頁查詢,分頁查詢最新的20條記錄耗時0.6秒,SQL語句大體是select field_1,field_2 from table where id < #{prePageMinId} order by id desc limit 20,prePageMinId是上一頁數據記錄的最小ID。數據庫
這個查詢出來的查詢速度還湊合,不過隨着數據不斷增加,有朝一日一定不堪重負。因此分庫分表是個週期長而風險高的大活兒,應該儘量在當前結構上優化,好比升級硬件、遷移歷史數據等等,實在沒轍了再分。性能優化
最大併發數bash
併發數是指同一時刻數據庫能處理多少個請求,由max_connections和max_user_connections決定。max_connections是指MySQL實例的最大鏈接數,上限值是16384,max_user_connections是指每一個數據庫用戶的最大鏈接數。
MySQL會爲每一個鏈接提供緩衝區,意味着消耗更多的內存。若是鏈接數設置過高硬件吃不消,過低又不能充分利用硬件。通常要求二者比值超過10%,計算方法以下:網絡
max_used_connections / max_connections * 100% = 3/100 *100% ≈ 3%
複製代碼
查看最大鏈接數與響應最大鏈接數以下:併發
show variables like '%max_connections%';
show variables like '%max_user_connections%';
複製代碼
在配置文件my.cnf中你能夠修改最大鏈接數數據庫設計
[mysqld]
max_connections = 100
max_used_connections = 20
複製代碼
查詢耗時0.5秒函數
建議將單次查詢耗時控制在0.5秒之內,0.5秒是個經驗值,源於用戶體驗的3秒原則。若是用戶的操做3秒內沒有響應,將會厭煩甚至退出。響應時間=客戶端UI渲染耗時+網絡請求耗時+應用程序處理耗時+查詢數據庫耗時,0.5秒就是留給數據庫1/6的處理時間。
相比datetime,timestamp佔用更少的空間,以UTC的格式儲存自動轉換時區。
避免空值
MySQL中字段爲NULL時依然佔用空間,會使索引、索引統計更加複雜。從NULL值更新到非NULL沒法作到原地更新,容易發生索引分裂影響性能。儘量將NULL值用有意義的值代替,也能避免SQL語句裏面包含is not null的判斷。
text類型優化
因爲text字段儲存大量數據,表容量會很早漲上去,影響其餘字段的查詢性能。建議抽取出來放在子表裏,用業務主鍵關聯。
索引的分類有哪些?
1 普通索引:最基本的索引
2 組合索引:多個字段上創建的索引,可以加速複合查詢條件的檢索。
3 惟一索引:與普通索引相似,但索引列的值必須惟一,容許有空值
4 組合惟一索引:列值的組合必須惟一
5 主鍵索引:特殊的惟一索引,用於惟一標識數據表中的某一條記錄,不容許有空值,通常用primary key約束。
6 全文索引:用於海量文本的查詢,MySQL5.6以後的InnoDB和MyISAM均支持全文索引。因爲查詢精度以及擴展性不佳,更多的企業選擇Elasticsearch。
索引優化
1 分頁查詢很重要,若是查詢數據量超過30%,MYSQL不會使用索引。
2 單表索引數不超過5個、單個索引字段數不超過5個。
3 字符串可以使用前綴索引,前綴長度控制在5-8個字符。
4 字段惟一性過低,增長索引沒有意義,如:是否刪除、性別。
合理使用覆蓋索引,以下所示:
select login_name, nick_name from member where login_name = ?複製代碼
update status=0 FROM `coupon` WHERE expire_date <= #{currentDate} and status=1;
複製代碼
若是大量優惠券須要更新爲不可用狀態,執行這條SQL可能會堵死其餘SQL,分批處理僞代碼以下:
int pageNo = 1;
int PAGE_SIZE = 100;
while(true) {
List<Integer> batchIdList = queryList('select id FROM `coupon` WHERE expire_date <= #{currentDate} and status = 1 limit #{(pageNo-1) * PAGE_SIZE},#{PAGE_SIZE}');
if (CollectionUtils.isEmpty(batchIdList)) {
return;
}
update('update status = 0 FROM `coupon` where status = 1 and id in #{batchIdList}')
pageNo ++;
}
複製代碼
操做符<>優化
一般<>操做符沒法使用索引,舉例以下,查詢金額不爲100元的訂單:
select id from orders where amount != 100;
複製代碼
若是金額爲100的訂單極少,這種數據分佈嚴重不均的狀況下,有可能使用索引。鑑於這種不肯定性,採用union聚合搜索結果,改寫方法以下:
(select id from orders where amount > 100)
union all
(select id from orders where amount < 100 and amount > 0)
複製代碼
OR優化
在Innodb引擎下or沒法使用組合索引,好比:
select id,product_name from orders where mobile_no = '13421800407' or user_id = 100;
複製代碼
OR沒法命中mobile_no + user_id的組合索引,可採用union,以下所示:
(select id,product_name from orders where mobile_no = '13421800407')
union
(select id,product_name from orders where user_id = 100);
複製代碼
此時id和product_name字段都有索引,查詢才最高效。
IN優化
IN適合主表大子表小,EXIST適合主表小子表大。因爲查詢優化器的不斷升級,不少場景這二者性能差很少同樣了。
嘗試改成join查詢,舉例以下:
select id from orders where user_id in (select id from user where level = 'VIP');複製代碼
採用JOIN以下所示:
select o.id from orders o left join user u on o.user_id = u.id where u.level = 'VIP';複製代碼
不作列運算
一般在查詢條件列運算會致使索引失效,以下所示:
查詢當日訂單
select id from order where date_format(create_time,'%Y-%m-%d') = '2019-07-01';複製代碼
date_format函數會致使這個查詢沒法使用索引,改寫後:
select id from order where create_time between '2019-07-01 00:00:00'
and '2019-07-01 23:59:59';
複製代碼
避免Select all
若是不查詢表中全部的列,避免使用SELECT *,它會進行全表掃描,不能有效利用索引。
Like優化
like用於模糊查詢,舉個例子(field已創建索引):
SELECT column FROM table WHERE field like '%keyword%';
複製代碼
這個查詢未命中索引,換成下面的寫法:
SELECT column FROM table WHERE field like 'keyword%';
複製代碼
去除了前面的%查詢將會命中索引,可是產品經理必定要先後模糊匹配呢?全文索引fulltext能夠嘗試一下,但Elasticsearch纔是終極武器。
Join優化
join的實現是採用Nested Loop Join算法,就是經過驅動表的結果集做爲基礎數據,經過該結數據做爲過濾條件到下一個表中循環查詢數據,而後合併結果。若是有多個join,則將前面的結果集做爲循環數據,再次到後一個表中查詢數據。
驅動表和被驅動表儘量增長查詢條件,知足ON的條件而少用Where,用小結果集驅動大結果集。
被驅動表的join字段上加上索引,沒法創建索引的時候,設置足夠的Join Buffer Size。
禁止join鏈接三個以上的表,嘗試增長冗餘字段。
Limit優化
limit用於分頁查詢時越日後翻性能越差,解決的原則:縮小掃描範圍,以下所示:
select * from orders order by id desc limit 100000,10
複製代碼
select * from orders order by id desc limit 1000000,10
複製代碼
select * from orders where id > (select id from orders order by id desc
limit 1000000, 1) order by id desc limit 0,10
複製代碼
select id from orders where id between 1000000 and 1000010 order by id desc複製代碼
若是以上方案依然很慢呢?只好用遊標了。