欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mysql中的sql優(yōu)化實(shí)踐詳解

 更新時(shí)間:2023年09月09日 14:43:32   作者:這是一條海魚  
這篇文章主要介紹了Mysql中的sql優(yōu)化實(shí)踐詳解,SQL優(yōu)化是指通過改進(jìn)SQL語句的編寫和查詢計(jì)劃的設(shè)計(jì)來提高數(shù)據(jù)庫查詢性能的過程,在優(yōu)化SQL時(shí),可以采取一些措施來提升查詢性能,需要的朋友可以參考下

1、order by優(yōu)化

MySql中有2種排序方式Using filesort和Using index

Using index:通過有序索引順序掃描直接返回有序數(shù)據(jù),這種情況即為using index,不需要額外排序,操作效率高。

Using filesort:通過表的索引或全表掃描,讀取滿足條件的數(shù)據(jù)行,然后在排序緩沖區(qū)sort bufferr中完成排序操作。它低層分為單路排序和雙路排序

  • 單路排序:是一次性取出滿足條件行的所有字段,然后在sort buffer中進(jìn)行排序;用trace工具可 以看到sort_mode信息里顯示 < sort_key, additional_fields >或者< sort_key, packed_additional_fields >
  • 雙路排序:又叫回表排序模式,是首先根據(jù)相應(yīng)的條件取出相應(yīng)的排序字段和可以直接定位行數(shù)據(jù)的行 ID,然后在 sort buffer 中進(jìn)行排序,排序完后需要再次取回其它需要的字段;用trace工具 可以看到sort_mode信息里顯示< sort_key, rowid >
  • MySQL 通過比較系統(tǒng)變量 max_length_for_sort_data(默認(rèn)1024字節(jié)) 的大小和需要查詢的字段總大小來 判斷哪種排序模式。 如果 字段的總長度小于max_length_for_sort_data ,那么使用 單路排序模式; 如果 字段的總長度大于max_length_for_sort_data ,那么使用 雙路排序模?式。
create index n_a_p_index on person (name, age, phone);

根據(jù)排序字段建立合適的索引,多字段排序時(shí),也遵循最左前綴法則。

explain select name from person order by age

在這里插入圖片描述

注意:判斷order by性能,我們主要根據(jù)Extra中的只來進(jìn)行判斷,雖然key顯示我們使用了索引,但是他是因?yàn)槲覀儾樵儠r(shí)使用了索引,而在Extra中,出現(xiàn)Using filesort,這說明我們排序沒有使用到索引。

優(yōu)化如下

explain select name from person order by name,age

在這里插入圖片描述

盡量使用覆蓋索引。

explain select * from person order by name,age

在這里插入圖片描述

多字段排序,一個(gè)升序一個(gè)降序,此時(shí)需要注意聯(lián)合索引在創(chuàng)建時(shí)的規(guī)則(ASC/DESC)。

explain select name,age from person order by name,age desc

在這里插入圖片描述

重新創(chuàng)建索引

create index n_a_p_index on person (name asc , age DESC , phone asc);
explain select name,age from person order by name,age desc

在這里插入圖片描述

如果不可避免的出現(xiàn)filesort,大數(shù)據(jù)量排序時(shí),可以適當(dāng)增大排序緩沖區(qū)大小sort buffer size(默認(rèn)256k)。

2、group by優(yōu)化

在分組操作時(shí),可以通過索引來提高效率。分組操作時(shí),索引的使用也是滿足最左前綴法則的。

create index n_a_p_index on person (name, age, phone);
explain select age ,count(*) from person group by age

在這里插入圖片描述

Using temporary,可以看到group by時(shí),使用到了臨時(shí)表,性能不好,可以做如下優(yōu)化

explain select age ,count(*) from person where name = '小明' group by age

在這里插入圖片描述

3、limit 優(yōu)化

一個(gè)常見又非常頭疼的問題就是limit2000000,10,此時(shí)需要MySQL排序前2000010記錄,僅僅返回2000000-2000010 的記錄,其他記錄丟棄,查詢排序的代價(jià)非常大。

select * from person limit 10000,10;

這段sql可以優(yōu)化成這樣

 select * from person p
 inner join(select id from person limit 10000,10) temp on temp.id = p.id

4、count(*)優(yōu)化

EXPLAIN select count(1) from employees;
EXPLAIN select count(id) from employees; 
EXPLAIN select count(name) from employees; 
EXPLAIN select count(*) from employees;

注意:根據(jù)某個(gè)字段count不會(huì)統(tǒng)計(jì)字段為null值的數(shù)據(jù)行

四個(gè)sql的執(zhí)行計(jì)劃一樣,說明這四個(gè)sql執(zhí)行效率應(yīng)該差不多

  • 字段有索引:count(*)≈count(1)>count(字段)>count(主鍵 id) //字段有索引,count(字段)統(tǒng)計(jì)走二級(jí)索引,二 級(jí)索引存儲(chǔ)數(shù)據(jù)比主鍵索引少,所以count(字段)>count(主鍵 id)
  • 字段無索引:count(*)≈count(1)>count(主鍵 id)>count(字段) //字段沒有索引count(字段)統(tǒng)計(jì)走不了索引, count(主鍵 id)還可以走主鍵索引,所以count(主鍵 id)>count(字段)

count(1)跟count(字段)執(zhí)行過程類似,不過count(1)不需要取出字段統(tǒng)計(jì),就用常量1做統(tǒng)計(jì),count(字段)還需要取出 字段,所以理論上count(1)比count(字段)會(huì)快一點(diǎn)。

count() 是例外,mysql并不會(huì)把全部字段取出來,而是專門做了優(yōu)化,不取值,按行累加,效率很高,所以不需要用 count(列名)或count(常量)來替代 count()。 為什么對(duì)于count(id),mysql最終選擇輔助索引而不是主鍵聚集索引?因?yàn)槎?jí)索引相對(duì)主鍵索引存儲(chǔ)數(shù)據(jù)更少,檢索 性能應(yīng)該更高,mysql內(nèi)部做了點(diǎn)優(yōu)化(應(yīng)該是在5.7版本才優(yōu)化)。

常見優(yōu)化方法

1、查詢mysql自己維護(hù)的總行數(shù) 對(duì)于myisam存儲(chǔ)引擎的表做不帶where條件的count查詢性能是很高的,因?yàn)閙yisam存儲(chǔ)引擎的表的總行數(shù)會(huì)被 mysql存儲(chǔ)在磁盤上,查詢不需要計(jì)算 對(duì)于innodb存儲(chǔ)引擎的表mysql不會(huì)存儲(chǔ)表的總記錄行數(shù)(因?yàn)橛蠱VCC機(jī)制,后面會(huì)講),查詢count需要實(shí)時(shí)計(jì)算

2、show table status 如果只需要知道表總行數(shù)的估計(jì)值可以用如下sql查詢,性能很高

show table status like 'person'

在這里插入圖片描述

3、將總數(shù)維護(hù)到Redis里 插入或刪除表數(shù)據(jù)行的時(shí)候同時(shí)維護(hù)redis里的表總行數(shù)key的計(jì)數(shù)值(用incr或decr命令),但是這種方式可能不準(zhǔn),很難 保證表操作和redis操作的事務(wù)一致性

4、增加數(shù)據(jù)庫計(jì)數(shù)表 插入或刪除表數(shù)據(jù)行的時(shí)候同時(shí)維護(hù)計(jì)數(shù)表,讓他們?cè)谕粋€(gè)事務(wù)里操作

5、Join關(guān)聯(lián)優(yōu)化

mysql的表關(guān)聯(lián)常見有兩種算法 :Nested-Loop Join和 Block Nested-Loop Join 算法

嵌套循環(huán)連接

Nested-Loop Join(NLJ) 算法 一次一行循環(huán)地從第一張表(稱為驅(qū)動(dòng)表)中讀取行,在這行數(shù)據(jù)中取到關(guān)聯(lián)字段,根據(jù)關(guān)聯(lián)字段在另一張表(被驅(qū)動(dòng) 表)里取出滿足條件的行,然后取出兩張表的結(jié)果合集。

 select * from t1 inner join t2 on t1.a= t2.a;

在這里插入圖片描述

從執(zhí)行計(jì)劃中可以看到這些信息:

  • 驅(qū)動(dòng)表是 t2,被驅(qū)動(dòng)表是 t1。先執(zhí)行的就是驅(qū)動(dòng)表(執(zhí)行計(jì)劃結(jié)果的id如果一樣則按從上到下順序執(zhí)行sql);優(yōu)化器一般會(huì)優(yōu)先選擇小表做驅(qū)動(dòng)表。所以使用 inner join 時(shí),排在前面的表并不一定就是驅(qū)動(dòng)表。
  • 當(dāng)使用left join時(shí),左表是驅(qū)動(dòng)表,右表是被驅(qū)動(dòng)表,當(dāng)使用right join時(shí),右表時(shí)驅(qū)動(dòng)表,左表是被驅(qū)動(dòng)表, 當(dāng)使用join時(shí),mysql會(huì)選擇數(shù)據(jù)量比較小的表作為驅(qū)動(dòng)表,大表作為被驅(qū)動(dòng)表。
  • 使用了 NLJ算法。一般 join 語句中,如果執(zhí)行計(jì)劃 Extra 中未出現(xiàn) Using join buffer 則表示使用的 join 算法是 NLJ。

上面sql的大致流程如下:

  1. 從表 t2 中讀取一行數(shù)據(jù)(如果t2表有查詢過濾條件的,會(huì)從過濾結(jié)果里取出一行數(shù)據(jù));
  2. 從第 1 步的數(shù)據(jù)中,取出關(guān)聯(lián)字段 a,到表 t1 中查找;
  3. 取出表 t1 中滿足條件的行,跟 t2 中獲取到的結(jié)果合并,作為結(jié)果返回給客戶端;
  4. 重復(fù)上面 3 步。

整個(gè)過程會(huì)讀取 t2 表的所有數(shù)據(jù)(掃描100行),然后遍歷這每行數(shù)據(jù)中字段 a 的值,根據(jù) t2 表中 a 的值索引掃描 t1 表 中的對(duì)應(yīng)行(掃描100次 t1 表的索引,1次掃描可以認(rèn)為最終只掃描 t1 表一行完整數(shù)據(jù),也就是總共 t1 表也掃描了100 行)。因此整個(gè)過程掃描了 200 行。

如果被驅(qū)動(dòng)表的關(guān)聯(lián)字段沒索引,使用NLJ算法性能會(huì)比較低(下面有詳細(xì)解釋),mysql會(huì)選擇Block Nested-Loop Join 算法。

基于塊的嵌套循環(huán)連接

Block Nested-Loop Join(BNL)算法

把驅(qū)動(dòng)表的數(shù)據(jù)讀入到 join_buffer 中,然后掃描被驅(qū)動(dòng)表,把被驅(qū)動(dòng)表每一行取出來跟 join_buffer 中的數(shù)據(jù)做對(duì)比。

select * from t1 inner join t2 on t1.b&#61; t2.b;

在這里插入圖片描述Extra 中 的Using join buffer (Block Nested Loop)說明該關(guān)聯(lián)查詢使用的是 BNL 算法。 上面sql的大致流程如下:

  1. 把 t2 的所有數(shù)據(jù)放入到 join_buffer 中
  2. 把表 t1 中每一行取出來,跟 join_buffer 中的數(shù)據(jù)做對(duì)比
  3. 返回滿足 join 條件的數(shù)據(jù)

整個(gè)過程對(duì)表 t1 和 t2 都做了一次全表掃描,因此掃描的總行數(shù)為10000(表 t1 的數(shù)據(jù)總量) + 100(表 t2 的數(shù)據(jù)總量) = 10100。并且 join_buffer 里的數(shù)據(jù)是無序的,因此對(duì)表 t1 中的每一行,都要做 100 次判斷,所以內(nèi)存中的判斷次數(shù)是 100 * 10000= 100 萬次。

這個(gè)例子里表 t2 才 100 行,要是表 t2 是一個(gè)大表,join_buffer 放不下怎么辦呢? join_buffer 的大小是由參數(shù) join_buffer_size 設(shè)定的,默認(rèn)值是 256k。如果放不下表 t2 的所有數(shù)據(jù)話,策略很簡(jiǎn)單, 就是分段放。

比如 t2 表有1000行記錄, join_buffer 一次只能放800行數(shù)據(jù),那么執(zhí)行過程就是先往 join_buffer 里放800行記錄,然 后從 t1 表里取數(shù)據(jù)跟 join_buffer 中數(shù)據(jù)對(duì)比得到部分結(jié)果,然后清空 join_buffer ,再放入 t2 表剩余200行記錄,再 次從 t1 表里取數(shù)據(jù)跟 join_buffer 中數(shù)據(jù)對(duì)比。所以就多掃了一次 t1 表。

被驅(qū)動(dòng)表的關(guān)聯(lián)字段沒索引為什么要選擇使用 BNL 算法而不使用 Nested-Loop Join 呢? 如果上面第二條sql使用 Nested-Loop Join,那么掃描行數(shù)為 100 * 10000 = 100萬次,這個(gè)是磁盤掃描。 很顯然,用BNL磁盤掃描次數(shù)少很多,相比于磁盤掃描,BNL的內(nèi)存計(jì)算會(huì)快得多。 因此MySQL對(duì)于被驅(qū)動(dòng)表的關(guān)聯(lián)字段沒索引的關(guān)聯(lián)查詢,一般都會(huì)使用 BNL 算法。如果有索引一般選擇 NLJ 算法,有 索引的情況下 NLJ 算法比 BNL算法性能更高

對(duì)于關(guān)聯(lián)sql的優(yōu)化

  • 關(guān)聯(lián)字段加索引,讓mysql做join操作時(shí)盡量選擇NLJ算法
  • 小表驅(qū)動(dòng)大表,寫多表連接sql時(shí)如果明確知道哪張表是小表可以用straight_join寫法固定連接驅(qū)動(dòng)方式,省去 mysql優(yōu)化器自己判斷的時(shí)間

straight_join解釋:straight_join功能同join類似,但能讓左邊的表來驅(qū)動(dòng)右邊的表,能改表優(yōu)化器對(duì)于聯(lián)表查詢的執(zhí) 行順序。

比如:select * from t2 straight_join t1 on t2.a = t1.a; 代表指定mysql選著 t2 表作為驅(qū)動(dòng)表。

  • straight_join只適用于inner join,并不適用于left join,right join。(因?yàn)閘eft join,right join已經(jīng)代表指 定了表的執(zhí)行順序)
  • 盡可能讓優(yōu)化器去判斷,因?yàn)榇蟛糠智闆r下mysql優(yōu)化器是比人要聰明的。使用straight_join一定要慎重,因?yàn)椴糠智闆r下人為指定的執(zhí)行順序并不一定會(huì)比優(yōu)化引擎要靠譜。

對(duì)于小表定義的明確 在決定哪個(gè)表做驅(qū)動(dòng)表的時(shí)候,應(yīng)該是兩個(gè)表按照各自的條件過濾,過濾完成之后,計(jì)算參與 join 的各個(gè)字段的總數(shù)據(jù) 量,數(shù)據(jù)量小的那個(gè)表,就是“小表”,應(yīng)該作為驅(qū)動(dòng)表。

到此這篇關(guān)于Mysql中的sql優(yōu)化實(shí)踐詳解的文章就介紹到這了,更多相關(guān)sql優(yōu)化實(shí)踐內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論