sql優化的具體方法

使用索引

索引是數據庫優化最常用也是最重要的手段之一, 通過索引通常可以幫助用戶解決大多數的MySQL的性能優化問題。

create table `tb_seller` (
	`sellerid` varchar (100),
	`name` varchar (100),
	`nickname` varchar (50),
	`password` varchar (60),
	`status` varchar (1),
	`address` varchar (100),
	`createtime` datetime,
    primary key(`sellerid`)
)engine=innodb default charset=utf8mb4; 

# 創建複合索引
create index idx_seller_name_sta_addr on tb_seller(name,status,address);

避免索引失效

1). 全值匹配 ,對索引中所有列都指定具體值。該情況下,索引生效,執行效率高。

explain select * from tb_seller where name='小米科技' and status='1' and address='北京市'\G;

在這裏插入圖片描述
2). 最左前綴法則
如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始,並且不跳過索引中的列。匹配最左前綴法則,走索引:
在這裏插入圖片描述
如果符合最左法則,但是出現跳躍某一列,只有最左列索引生效
在這裏插入圖片描述
3). 範圍查詢右邊的列,不能使用索引 。
在這裏插入圖片描述
4). 不要在索引列上進行運算操作, 索引將失效。
在這裏插入圖片描述
5). 字符串不加單引號,造成索引失效。由於,在查詢時,沒有對字符串加單引號,MySQL的查詢優化器,會自動的進行類型轉換,也就是說他進行了運算操作,造成索引失效。
在這裏插入圖片描述
6). 儘量使用覆蓋索引,避免select *
儘量使用覆蓋索引(只訪問索引的查詢(索引列完全包含查詢列)),減少select * 。
在這裏插入圖片描述
所以類似於select * 這樣的查詢列,超出索引列,就會降低性能,因爲雖然走了索引查詢,但是查詢到數據後還需要進行回表查詢其他非索引字段的數據,降低了性能。
在這裏插入圖片描述

using index :使用覆蓋索引的時候就會出現
using where:在查找使用索引的情況下,需要回表去查詢所需的數據
using index condition:查找使用了索引,但是需要回表查詢數據
using index ; using where:查找使用了索引,但是需要的數據都在索引列中能找到,所以不需要回表查詢數據

7). 用or分割開的條件, 如果or前的條件中的列有索引,而後面的列中沒有索引,那麼涉及的索引都不會被用到。

示例,name字段是索引列 , 而createtime不是索引列,中間是or進行連接是不走索引的 :

explain select * from tb_seller where name='黑馬程序員' or createtime = '2088-01-01 12:00:00'\G;	

在這裏插入圖片描述
8). 以%開頭的Like模糊查詢,索引失效。
如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效。
在這裏插入圖片描述
解決方案 : 通過覆蓋索引來解決
在這裏插入圖片描述
9). 如果MySQL評估(如果評估全表掃描快,那麼就不會走索引)使用索引比全表更慢,則不使用索引。
在這裏插入圖片描述
10). is NULL , is NOT NULL 有時索引失效。其實也就是mysql自身評估(如果評估全表掃描快,那麼就不會走索引)的原因。
在這裏插入圖片描述
11). in 走索引, not in 索引失效。
在這裏插入圖片描述
12). 儘量使用複合索引,而少使用單列索引 。

create index idx_name_sta_address on tb_seller(name, status, address);
就相當於創建了三個索引 : 
	name
	name + status
	name + status + address

創建單列索引

create index idx_seller_name on tb_seller(name);
create index idx_seller_status on tb_seller(status);
create index idx_seller_address on tb_seller(address);

此時當3個索引字段全部被作爲條件查詢時,數據庫會選擇一個最優的索引(辨識度最高索引)來使用,並不會使用全部索引 。

查看索引使用情況

show status like 'Handler_read%';	
show global status like 'Handler_read%';	

在這裏插入圖片描述

  • Handler_read_first:索引中第一條被讀的次數。如果較高,表示服務器正執行大量全索引掃描(這個值越低越好)。
  • Handler_read_key:如果索引正在工作,這個值代表一個行被索引值讀的次數,如果值越低,表示索引得到的性能改善不高,因爲索引不經常使用(這個值越高越好)。
  • Handler_read_next :按照鍵順序讀下一行的請求數。如果你用範圍約束或如果執行索引掃描來查詢索引列,該值增加。
  • Handler_read_prev:按照鍵順序讀前一行的請求數。該讀方法主要用於優化ORDER BY … DESC。
  • Handler_read_rnd :根據固定位置讀一行的請求數。如果你正執行大量查詢並需要對結果進行排序該值較高。你可能使用了大量需要MySQL掃描整個表的查詢或你的連接沒有正確使用鍵。這個值較高,意味着運行效率低,應該建立索引來補救。
  • Handler_read_rnd_next:在數據文件中讀下一行的請求數。如果你正進行大量的表掃描,該值較高。通常說明你的表索引不正確或寫入的查詢沒有利用索引。

優化insert語句

當進行數據的insert操作的時候,可以考慮採用以下幾種優化方案。

  • 如果需要同時對一張表插入很多行數據時,應該儘量使用多個值表的insert語句,這種方式將大大的縮減客戶端與數據庫之間的連接、關閉等消耗。使得效率比分開執行的單個insert語句快。

    示例, 原始方式爲:

insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
# 優化後的方案爲
insert into tb_test values(1,'Tom'),(2,'Cat')(3,'Jerry');
  • 修改mysql爲手動提交事務,在事務中進行批量數據插入。
start transaction;
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
commit;
  • -數據有序插入
insert into tb_test values(4,'Tim');
insert into tb_test values(1,'Tom');
insert into tb_test values(3,'Jerry');
insert into tb_test values(5,'Rose');
insert into tb_test values(2,'Cat');
#  優化後
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
insert into tb_test values(4,'Tim');
insert into tb_test values(5,'Rose');

優化order by語句

1). 通過對返回數據進行排序,也就是通常說的 filesort 排序,所有不是通過索引直接返回排序結果的排序都叫 FileSort 排序。
在這裏插入圖片描述
2). 通過有序索引順序掃描直接返回有序數據,這種情況即爲 using index,不需要額外排序,操作效率高
在這裏插入圖片描述
多字段排序
在這裏插入圖片描述瞭解了MySQL的排序方式,優化目標就清晰了:儘量減少額外的排序,通過索引直接返回有序數據where 條件和Order by 使用相同的索引,並且Order By 的順序和索引順序相同, 並且Order by 的字段都是升序,或者都是降序。否則肯定需要額外的操作,這樣就會出現FileSort。
在這裏插入圖片描述

優化group by語句

由於GROUP BY 實際上也同樣會進行排序操作,而且與ORDER BY 相比,GROUP BY 主要只是多了排序之後的分組操作。當然,如果在分組的時候還使用了其他的一些聚合函數,那麼還需要一些聚合函數的計算。所以,在GROUP BY 的實現過程中,與 ORDER BY 一樣也可以利用到索引。

如果查詢包含 group by 但是用戶想要避免排序結果的消耗, 則可以執行order by null 禁止排序。如下 :

drop index idx_emp_age_salary on emp;
explain select age,count(*) from emp group by age;

在這裏插入圖片描述

explain select age,count(*) from emp group by age order by null;

在這裏插入圖片描述
從上面的例子可以看出,第一個SQL語句需要進行"filesort",而第二個SQL由於order by null 不需要進行 “filesort”, 而上文提過Filesort往往非常耗費時間。針對分組字段創建索引就會提高效率。
在這裏插入圖片描述

優化嵌套查詢

Mysql4.1版本之後,開始支持SQL的子查詢。這個技術可以使用SELECT語句來創建一個單列的查詢結果,然後把這個結果作爲過濾條件用在另一個查詢中。使用子查詢可以一次性的完成很多邏輯上需要多個步驟才能完成的SQL操作,同時也可以避免事務或者表鎖死,並且寫起來也很容易。但是,有些情況下,子查詢是可以被更高效的連接(JOIN)替代

示例 ,查找有角色的所有的用戶信息 :
在這裏插入圖片描述

優化or語句

對於包含OR的查詢子句,如果要利用索引,則OR之間的每個條件列都必須用到索引而且不能使用到複合索引; 如果沒有索引,則應該考慮增加索引。
在這裏插入圖片描述
建議使用 union 替換 or :
在這裏插入圖片描述
我們來比較下重要指標,發現主要差別是 type 和 ref 這兩項。
type 顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:

system > const > eq_ref > ref > fulltext > ref_or_null  > index_merge > unique_subquery > index_subquery > range > index > ALL

UNION 語句的 type 值爲 ref,OR 語句的 type 值爲 range,可以看到這是一個很明顯的差距。UNION 語句的 ref 值爲 const,OR 語句的 type 值爲 null,const 表示是常量值引用,非常快。這兩項的差距就說明UNION 要優於 OR 。

優化分頁查詢

一般分頁查詢時,通過創建覆蓋索引能夠比較好地提高性能。一個常見又非常頭疼的問題就是 limit 2000000,10 ,此時需要MySQL排序前2000010 記錄,僅僅返回2000000 - 2000010 的記錄,其他記錄丟棄,查詢排序的代價非常大 。
在這裏插入圖片描述

方式1

在索引上完成排序分頁操作,最後根據主鍵關聯回原表查詢所需要的其他列內容。
在這裏插入圖片描述

方式2

該方案適用於主鍵自增,而且主鍵自增不能出現斷層 的表,可以把Limit 查詢轉換成某個位置的查詢 。
在這裏插入圖片描述

使用SQL提示

SQL提示,是優化數據庫的一個重要手段,簡單來說,就是在SQL語句中加入一些人爲的提示來達到優化操作的目的。

USE INDEX

在查詢語句中表名的後面,添加 use index 來提供希望MySQL去參考的索引列表,就可以讓MySQL不再考慮其他可用的索引。
在這裏插入圖片描述

IGNORE INDEX

如果用戶只是單純的想讓MySQL忽略一個或者多個索引,則可以使用 ignore index 作爲 hint 。
在這裏插入圖片描述

FORCE INDEX

爲強制MySQL使用一個特定的索引,可在查詢中使用 force index 作爲hint 。

create index idx_seller_address on tb_seller(address);

在這裏插入圖片描述


本文參考www.itheima.com學習資料,只用於本人學習筆記總結,不可轉載,如有侵權請諒解。

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