欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
全網(wǎng)最全的 MySQL 索引優(yōu)化方案


一、分頁(yè)查詢(xún)優(yōu)化

很多時(shí)候我們業(yè)務(wù)系統實(shí)現分頁(yè)功能可能會(huì )用如下sql實(shí)現:

select * from employees limit 10000,10;

表示從表 employees 中取出從 10001 行開(kāi)始的 10 行記錄??此浦徊樵?xún)了 10 條記錄,實(shí)際這條 SQL 是先讀取 10010條記錄,然后拋棄前 10000 條記錄,然后讀到后面 10 條想要的數據。因此要查詢(xún)一張大表比較靠后的數據,執行效率是非常低的。這是典型的深度分頁(yè)問(wèn)題。

分頁(yè)場(chǎng)景優(yōu)化技巧

1、根據自增且連續的主鍵排序的分頁(yè)查詢(xún)

首先來(lái)看一個(gè)根據自增且連續主鍵排序的分頁(yè)查詢(xún)的例子:

select * from employees limit 90000,5;

該 SQL 表示查詢(xún)從第 90001開(kāi)始的五行數據,沒(méi)添加單獨 order by,表示通過(guò)主鍵排序。我們再看表 employees ,因為主鍵是自增并且連續的,所以可以改寫(xiě)成按照主鍵去查詢(xún)從第 90001開(kāi)始的五行數據,如下:

select * from employees where id > 90000 limit 5;

查詢(xún)的結果是一致的。我們再對比一下執行計劃:

EXPLAIN select * from employees limit 90000,5;
EXPLAIN select * from employees where id > 90000 limit 5;

顯然改寫(xiě)后的 SQL 走了索引,而且掃描的行數大大減少,執行效率更高。

但是,這條改寫(xiě)的SQL 在很多場(chǎng)景并不實(shí)用,因為表中可能某些記錄被刪后,主鍵空缺,導致結果不一致,如下圖試驗所示(先刪除一條前面的記錄,然后再測試原 SQL 和優(yōu)化后的 SQL):

兩條 SQL 的結果并不一樣,因此,如果主鍵不連續,不能使用上面描述的優(yōu)化方法。

另外如果原 SQL 是 order by 非主鍵的字段,按照上面說(shuō)的方法改寫(xiě)會(huì )導致兩條 SQL 的結果不一致。所以這種改寫(xiě)得滿(mǎn)足以下兩個(gè)條件:

  • 主鍵自增且連續
  • 結果是按照主鍵排序的

2、根據非主鍵字段排序的分頁(yè)查詢(xún)

再看一個(gè)根據非主鍵字段排序的分頁(yè)查詢(xún),SQL 如下:

select * from employees ORDER BY name limit 90000,5;
EXPLAIN select * from employees ORDER BY name limit 90000,5;

發(fā)現并沒(méi)有使用 name 字段的索引(key 字段對應的值為 null),具體原因是:掃描整個(gè)索引并查找到?jīng)]索引的行(可能要遍歷多個(gè)索引樹(shù))的成本比掃描全表的成本更高,所以?xún)?yōu)化器放棄使用索引。

知道不走索引的原因,那么怎么優(yōu)化呢?

其實(shí)關(guān)鍵是讓排序時(shí)返回的字段盡可能少,即考慮使用覆蓋索引進(jìn)行優(yōu)化,所以可以讓排序和分頁(yè)操作先查出主鍵,然后根據主鍵查到對應的記錄,SQL改寫(xiě)如下:

select * from employees e inner join (select id from employees order by name limit 90000,5) ed on e.id = ed.id;

需要的結果與原 SQL 一致,執行時(shí)間減少了一半以上,此時(shí)查詢(xún)和排序都是在覆蓋索引樹(shù)上進(jìn)行的,所以效率較高。我們再對比優(yōu)化前后sql的執行計劃:

原 SQL 使用的是 file sort 排序,而優(yōu)化后的 SQL 使用的是索引排序。

  • 根據id判斷,會(huì )先執行id = 2的sql,此時(shí)使用了覆蓋索引,排序和查詢(xún)都是在索引樹(shù)上完成的。
  • 然后執行id=1的sql,這里使用了eq_ref,即主鍵索引。
  • 最后執行join關(guān)聯(lián)的那張表,因為此時(shí)的table是derived, 是前面兩張表的關(guān)聯(lián)表,總共有5條記錄,所以即使全表掃描,也是比較快的。

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現的后臺管理系統 + 用戶(hù)小程序,支持 RBAC 動(dòng)態(tài)權限、多租戶(hù)、數據權限、工作流、三方登錄、支付、短信、商城等功能。

項目地址:https://github.com/YunaiV/ruoyi-vue-pro

二、Join關(guān)聯(lián)查詢(xún)優(yōu)化

示例表:

‐‐ 示例表:
 CREATE TABLE `t1` (
 `id` int(11NOT NULL AUTO_INCREMENT,
 `a` int(11DEFAULT NULL,
 `b` int(11DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_a` (`a`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 create table t2 like t1;

 ‐‐ 插入一些示例數據
 ‐‐ 往t1表插入1萬(wàn)行記錄
 drop procedure if exists insert_t1;
 delimiter ;;
 create procedure insert_t1()
 begin
 declare i int;
 set i=1;
 while(i<=10000)do
 insert into t1(a,b) values(i,i);
 set i=i+1;
 end while;
 end;;
 delimiter ;
 call insert_t1();
 
 ‐‐ 往t2表插入100行記錄
 drop procedure if exists insert_t2;
 delimiter ;;
 create procedure insert_t2()
 begin
 declare i int;
 set i=1;
 while(i<=100)do
 insert into t2(a,b) values(i,i);
 set i=i+1;
 end while;
 end;;
 delimiter ;
 call insert_t2();

mysql的表關(guān)聯(lián)常見(jiàn)有兩種算法

  • Nested-Loop Join 算法
  • Block Nested-Loop Join 算法
1、 嵌套循環(huán)連接 Nested-Loop Join(NLJ) 算法

一次一行循環(huán)地從第一張表(稱(chēng)為驅動(dòng)表)中讀取行,在這行數據中取到關(guān)聯(lián)字段,根據關(guān)聯(lián)字段在另一張表(被驅動(dòng)表)里取出滿(mǎn)足條件的行,然后取出兩張表的結果合集。

EXPLAIN select * from t1 inner join t2 on t1.a= t2.a;  -- // a字段有索引

從執行計劃中可以看到這些信息:

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

上面sql的大致流程如下:

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

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

2、 基于塊的嵌套循環(huán)連接 Block Nested-Loop Join(BNL)算法

把驅動(dòng)表的數據讀入到 join_buffer 中,然后掃描被驅動(dòng)表,把被驅動(dòng)表每一行取出來(lái)跟 join_buffer 中的數據做對比。

EXPLAIN select * from t1 inner join t2 on t1.b= t2.b; -- // b字段沒(méi)有索引

Extra 中 的Using join buffer (Block Nested Loop)說(shuō)明該關(guān)聯(lián)查詢(xún)使用的是 BNL 算法。

上面sql的大致流程如下:

  • 把 t2 的所有數據放入到 join_buffer
  • 把表 t1 中每一行取出來(lái),跟 join_buffer 中的數據做對比
  • 返回滿(mǎn)足 join 條件的數據

整個(gè)過(guò)程對表 t1 和 t2 都做了一次全表掃描,因此掃描的總行數為10000(表 t1 的數據總量) + 100(表 t2 的數據總量) =10100。并且 join_buffer 里的數據是無(wú)序的,因此對表 t1 中的每一行,都要做 100 次判斷,所以?xún)却嬷械呐袛啻螖凳?code>100 * 10000= 100 萬(wàn)次。

這個(gè)例子里表 t2 才 100 行,要是表 t2 是一個(gè)大表,join_buffer 放不下怎么辦呢?

join_buffer 的大小是由參數 join_buffer_size 設定的,默認值是 256k。如果放不下表 t2 的所有數據話(huà),策略很簡(jiǎn)單,就是分段放。

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

被驅動(dòng)表的關(guān)聯(lián)字段沒(méi)索引為什么要選擇使用 BNL 算法而不使用 Nested-Loop Join 呢?

如果上面第二條sql使用 Nested-Loop Join,那么掃描行數為 100 * 10000 = 100萬(wàn)次,這個(gè)是磁盤(pán)掃描。

很顯然,用BNL磁盤(pán)掃描次數少很多,相比于磁盤(pán)掃描,BNL的內存計算會(huì )快得多。因此MySQL對于被驅動(dòng)表的關(guān)聯(lián)字段沒(méi)索引的關(guān)聯(lián)查詢(xún),一般都會(huì )使用 BNL 算法。如果有索引一般選擇 NLJ 算法,有索引的情況下 NLJ 算法比 BNL算法性能更高.

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

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

straight_join解釋?zhuān)?code>straight_join功能同join類(lèi)似,但能讓左邊的表來(lái)驅動(dòng)右邊的表,能改表優(yōu)化器對于聯(lián)表查詢(xún)的執行順序。

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

  • straight_join只適用于inner join,并不適用于left join,right join。(因為left join,right join已經(jīng)代表指定了表的執行順序)
  • 盡可能讓優(yōu)化器去判斷,因為大部分情況下mysql優(yōu)化器是比人要聰明的。使用straight_join一定要慎重,因為部分情況下人為指定的執行順序并不一定會(huì )比優(yōu)化引擎要靠譜。
對于小表定義的明確:

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

基于微服務(wù)的思想,構建在 B2C 電商場(chǎng)景下的項目實(shí)戰。核心技術(shù)棧,是 Spring Boot + Dubbo 。未來(lái),會(huì )重構成 Spring Cloud Alibaba 。

項目地址:https://github.com/YunaiV/onemall

三、in和exsits優(yōu)化

原則:小表驅動(dòng)大表,即小的數據集驅動(dòng)大的數據集。

In是In后的表先執行(適用于B表小于A(yíng)表):

select * from A where id in ( select id from B)

Exists是Exists前面的表先執行(適用于A(yíng)表小于B表):

select * from A where id in ( select id from B)
in:當B表的數據集小于A(yíng)表的數據集時(shí),in優(yōu)于exists
select * from A where id in (select id from B)

// #等價(jià)于:
 for(select id from B){
 select * from A where A.id = B.id
 }
exists:當A表的數據集小于B表的數據集時(shí),exists優(yōu)于in

將主查詢(xún)A的數據,放到子查詢(xún)B中做條件驗證,根據驗證結果(true或false)來(lái)決定主查詢(xún)的數據是否保留.

select * from A where exists (select 1 from B where B.id = A.id)

 // # 等價(jià)于:
 for(select * from A){
 select * from B where B.id = A.id
 }

 // # A表與B表的ID字段應建立索引
總結:

1、EXISTS (subquery)只返回TRUE或FALSE,因此子查詢(xún)中的SELECT * 也可以用SELECT 1替換,官方說(shuō)法是實(shí)際執行時(shí)會(huì )忽略SELECT清單,因此沒(méi)有區別

2、EXISTS子查詢(xún)的實(shí)際執行過(guò)程可能經(jīng)過(guò)了優(yōu)化而不是我們理解上的逐條對比

3、EXISTS子查詢(xún)往往也可以用JOIN來(lái)代替,何種最優(yōu)需要具體問(wèn)題具體分析

四、count(*)查詢(xún)優(yōu)化

 -- 臨時(shí)關(guān)閉mysql查詢(xún)緩存,為了查看sql多次執行的真實(shí)時(shí)間
 set global query_cache_size=0;
 set global query_cache_type=0;

 EXPLAIN select count(1from employees;
 EXPLAIN select count(idfrom employees;
 EXPLAIN select count(namefrom employees;
 EXPLAIN select count(*) from employees;
 -- 注意:以上4條sql只有根據某個(gè)字段count不會(huì )統計字段

經(jīng)過(guò)測試發(fā)現:四個(gè)sql的執行計劃一樣,說(shuō)明這四個(gè)sql執行效率應該差不多

1、字段有索引: count(*)count(1)>count(字段)>count(主鍵 id)

字段有索引,count(字段)統計走二級索引,二級索引存儲數據比主鍵索引少,所以count(字段)>count(主鍵 id)

2、字段無(wú)索引: count(*)count(1)>count(主鍵 id)>count(字段)

字段沒(méi)有索引count(字段)統計走不了索引,count(主鍵 id)還可以走主鍵索引,所以count(主鍵 id)>count(字段)

count(1)count(字段) 執行過(guò)程類(lèi)似,不過(guò)count(1)不需要取出字段統計,就用常量1做統計,count(字段)還需要取出字段,所以理論上count(1)count(字段)會(huì )快一點(diǎn)。

count(*) 是例外,mysql并不會(huì )把全部字段取出來(lái),而是專(zhuān)門(mén)做了優(yōu)化(5.7版本),不取值,按行累加,效率很高,所以不需要用count(列名)count(常量)來(lái)替代 count(*)。

為什么對于count(id),mysql最終選擇輔助索引而不是主鍵聚集索引?因為二級索引相對主鍵索引存儲數據更少,檢索性能應該更高,mysql內部做了點(diǎn)優(yōu)化(應該是在5.7版本才優(yōu)化)。

常見(jiàn)優(yōu)化方法

當表中數據量非常大的時(shí)候,count這種通過(guò)計算統計的都會(huì )很慢,所以需要一些優(yōu)化手段。

1、查詢(xún)mysql自己維護的總行數

對于myisam存儲引擎的表做不帶where條件的count查詢(xún)性能是很高的,因為myisam存儲引擎的表的總行數會(huì )被mysql存儲在磁盤(pán)上,查詢(xún)不需要計算.

對于innodb存儲引擎的表mysql不會(huì )存儲表的總記錄行數(因為有MVCC機制,后面會(huì )講),查詢(xún)count需要實(shí)時(shí)計算.

2、show table status

如果只需要知道表總行數的估計值可以用如下sql查詢(xún),性能很高

3、將總數維護到Redis里

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

4、增加數據庫計數表

插入或刪除表數據行的時(shí)候同時(shí)維護計數表,讓他們在同一個(gè)事務(wù)里操作

五、阿里MySQL規范解讀

(一) 建表規約

  • 【強制】表達是與否概念的字段,必須使用is_xxx的方式命名,數據類(lèi)型是unsigned tinyint(1表示是,0表示否)。說(shuō)明:任何字段如果為非負數,必須是unsigned。注意:POJO類(lèi)中的任何布爾類(lèi)型的變量,都不要加is前綴,所以,需要在設置從is_xxx到Xxx的映射關(guān)系。數據庫表示是與否的值,使用tinyint類(lèi)型,堅持is_xxx的命名方式是為了明確其取值含義與取值范圍。正例:表達邏輯刪除的字段名is_deleted,1表示刪除,0表示未刪除。
  • 【強制】表名、字段名必須使用小寫(xiě)字母或數字,禁止出現數字開(kāi)頭,禁止兩個(gè)下劃線(xiàn)中間只出現數字。數據庫字段名的修改代價(jià)很大,因為無(wú)法進(jìn)行預發(fā)布,所以字段名稱(chēng)需要慎重考慮。說(shuō)明:MySQL在Windows下不區分大小寫(xiě),但在Linux下默認是區分大小寫(xiě)。因此,數據庫名、表名、字段名,都不允許出現任何大寫(xiě)字母,避免節外生枝。正例:aliyun_admin,rdc_config,level3_name 反例:AliyunAdmin,rdcConfig,level_3_name
  • 【強制】表名不使用復數名詞。說(shuō)明:表名應該僅僅表示表里面的實(shí)體內容,不應該表示實(shí)體數量,對應于DO類(lèi)名也是單數形式,符合表達習慣。
  • 【強制】禁用保留字,如desc、range、match、delayed等,請參考MySQL官方保留字。
  • 【強制】主鍵索引名為pk_字段名;唯一索引名為uk_字段名;普通索引名則為idx_字段名。說(shuō)明:pk_ primary key;uk_unique key;idx_ 即index的簡(jiǎn)稱(chēng)。
  • 【強制】小數類(lèi)型為decimal,禁止使用float和double。說(shuō)明:在存儲的時(shí)候,float 和 double 都存在精度損失的問(wèn)題,很可能在比較值的時(shí)候,得到不正確的結果。如果存儲的數據范圍超過(guò) decimal 的范圍,建議將數據拆成整數和小數并分開(kāi)存儲。
  • 【強制】如果存儲的字符串長(cháng)度幾乎相等,使用char定長(cháng)字符串類(lèi)型。
  • 【強制】varchar是可變長(cháng)字符串,不預先分配存儲空間,長(cháng)度不要超過(guò)5000,如果存儲長(cháng)度大于此值,定義字段類(lèi)型為text,獨立出來(lái)一張表,用主鍵來(lái)對應,避免影響其它字段索引效率。
  • 【強制】表必備三字段:id, gmt_create, gmt_modified。說(shuō)明:其中id必為主鍵,類(lèi)型為bigint unsigned、單表時(shí)自增、步長(cháng)為1。gmt_create, gmt_modified的類(lèi)型均為datetime類(lèi)型,前者現在時(shí)表示主動(dòng)式創(chuàng )建,后者過(guò)去分詞表示被動(dòng)式更新。
  • 【推薦】表的命名最好是遵循“業(yè)務(wù)名稱(chēng)_表的作用”。正例:alipay_task / force_project / trade_config
  • 【推薦】庫名與應用名稱(chēng)盡量一致。
  • 【推薦】如果修改字段含義或對字段表示的狀態(tài)追加時(shí),需要及時(shí)更新字段注釋。
  • 【推薦】字段允許適當冗余,以提高查詢(xún)性能,但必須考慮數據一致。冗余字段應遵循:1) 不是頻繁修改的字段。2) 不是唯一索引的字段。3) 不是varchar超長(cháng)字段,更不能是text字段。正例:各業(yè)務(wù)線(xiàn)經(jīng)常冗余存儲商品名稱(chēng),避免查詢(xún)時(shí)需要調用IC服務(wù)獲取。
  • 【推薦】單表行數超過(guò)500萬(wàn)行或者單表容量超過(guò)2GB,才推薦進(jìn)行分庫分表。說(shuō)明:如果預計三年后的數據量根本達不到這個(gè)級別,請不要在創(chuàng )建表時(shí)就分庫分表。
  • 【參考】合適的字符存儲長(cháng)度,不但節約數據庫表空間、節約索引存儲,更重要的是提升檢索速度。正例:無(wú)符號值可以避免誤存負數,且擴大了表示范圍。

(二) 索引規約

  • 【強制】業(yè)務(wù)上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引。說(shuō)明:不要以為唯一索引影響了insert速度,這個(gè)速度損耗可以忽略,但提高查找速度是明顯的;另外,即使在應用層做了非常完善的校驗控制,只要沒(méi)有唯一索引,根據墨菲定律,必然有臟數據產(chǎn)生。
  • 【強制】超過(guò)三個(gè)表禁止join。需要join的字段,數據類(lèi)型保持絕對一致;多表關(guān)聯(lián)查詢(xún)時(shí),保證被關(guān)聯(lián)的字段需要有索引。說(shuō)明:即使雙表join也要注意表索引、SQL性能。
  • 【強制】在varchar字段上建立索引時(shí),必須指定索引長(cháng)度,沒(méi)必要對全字段建立索引,根據實(shí)際文本區分度決定索引長(cháng)度。說(shuō)明:索引的長(cháng)度與區分度是一對矛盾體,一般對字符串類(lèi)型數據,長(cháng)度為20的索引,區分度會(huì )高達90%以上,可以使用count(distinct left(列名, 索引長(cháng)度))/count(*)的區分度來(lái)確定。
  • 【強制】頁(yè)面搜索嚴禁左模糊或者全模糊,如果需要請走搜索引擎來(lái)解決。說(shuō)明:索引文件具有B-Tree的最左前綴匹配特性,如果左邊的值未確定,那么無(wú)法使用此索引。
  • 【推薦】如果有order by的場(chǎng)景,請注意利用索引的有序性。order by 最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現file_sort的情況,影響查詢(xún)性能。正例:where a=? and b=? order by c; 索引:a_b_c 反例:索引如果存在范圍查詢(xún),那么索引有序性無(wú)法利用,如:WHERE a>10 ORDER BY b; 索引a_b無(wú)法排序。
  • 【推薦】利用覆蓋索引來(lái)進(jìn)行查詢(xún)操作,避免回表。說(shuō)明:如果一本書(shū)需要知道第11章是什么標題,會(huì )翻開(kāi)第11章對應的那一頁(yè)嗎?目錄瀏覽一下就好,這個(gè)目錄就是起到覆蓋索引的作用。正例:能夠建立索引的種類(lèi)分為主鍵索引、唯一索引、普通索引三種,而覆蓋索引只是一種查詢(xún)的一種效果,用explain的結果,extra列會(huì )出現:using index。
  • 【推薦】利用延遲關(guān)聯(lián)或者子查詢(xún)優(yōu)化超多分頁(yè)場(chǎng)景。說(shuō)明:MySQL并不是跳過(guò)offset行,而是取offset+N行,然后返回放棄前offset行,返回N行,那當offset特別大的時(shí)候,效率就非常的低下,要么控制返回的總頁(yè)數,要么對超過(guò)特定閾值的頁(yè)數進(jìn)行SQL改寫(xiě)。正例:先快速定位需要獲取的id段,然后再關(guān)聯(lián):SELECT a.* FROM 表1 a, (select id from 表1 where 條件 LIMIT 100000,20 ) b where a.id=b.id
  • 【推薦】SQL性能優(yōu)化的目標:至少要達到 range 級別,要求是ref級別,如果可以是consts最好。說(shuō)明:1) consts 單表中最多只有一個(gè)匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數據。2) ref 指的是使用普通的索引(normal index)。3) range 對索引進(jìn)行范圍檢索。反例:explain表的結果,type=index,索引物理文件全掃描,速度非常慢,這個(gè)index級別比較range還低,與全表掃描是小巫見(jiàn)大巫。
  • 【推薦】建組合索引的時(shí)候,區分度最高的在最左邊。正例:如果where a=? and b=?,a列的幾乎接近于唯一值,那么只需要單建idx_a索引即可。說(shuō)明:存在非等號和等號混合判斷條件時(shí),在建索引時(shí),請把等號條件的列前置。如:where c>? and d=? 那么即使c的區分度更高,也必須把d放在索引的最前列,即建立組合索引idx_d_c。
  • 【推薦】防止因字段類(lèi)型不同造成的隱式轉換,導致索引失效。
  • 【參考】創(chuàng )建索引時(shí)避免有如下極端誤解:1) 索引寧濫勿缺。認為一個(gè)查詢(xún)就需要建一個(gè)索引。2) 吝嗇索引的創(chuàng )建。認為索引會(huì )消耗空間、嚴重拖慢記錄的更新以及行的新增速度。3) 抵制惟一索引。認為惟一索引一律需要在應用層通過(guò)“先查后插”方式解決。

(三) SQL語(yǔ)句

  • 【強制】不要使用count(列名)count(常量)來(lái)替代count(),count()是SQL92定義的標準統計行數的語(yǔ)法,跟數據庫無(wú)關(guān),跟NULL和非NULL無(wú)關(guān)。說(shuō)明:count(*)會(huì )統計值為NULL的行,而count(列名)不會(huì )統計此列為NULL值的行。
  • 【強制】count(distinct col) 計算該列除NULL之外的不重復行數,注意 count(distinct col1, col2) 如果其中一列全為NULL,那么即使另一列有不同的值,也返回為0。
  • 【強制】當某一列的值全是NULL時(shí),count(col)的返回結果為0,但sum(col)的返回結果為NULL,因此使用sum()時(shí)需注意NPE問(wèn)題。正例:可以使用如下方式來(lái)避免sum的NPE問(wèn)題:SELECT IFNULL(SUM(column), 0) FROM table;
  • 【強制】使用ISNULL()來(lái)判斷是否為NULL值。說(shuō)明:NULL與任何值的直接比較都為NULL。1) NULL<>NULL的返回結果是NULL,而不是false。2) NULL=NULL的返回結果是NULL,而不是true。3) NULL<>1的返回結果是NULL,而不是true。反例:在SQL語(yǔ)句中,如果在null前換行,影響可讀性。select * from table where column1 is null and column3 is not null;ISNULL(column)是一個(gè)整體,簡(jiǎn)潔易懂。從性能數據上分析,ISNULL(column)執行效率更快一些。
  • 【強制】代碼中寫(xiě)分頁(yè)查詢(xún)邏輯時(shí),若count為0應直接返回,避免執行后面的分頁(yè)語(yǔ)句。
  • 【強制】不得使用外鍵與級聯(lián),一切外鍵概念必須在應用層解決。說(shuō)明:(概念解釋?zhuān)W(xué)生表中的student_id是主鍵,那么成績(jì)表中的student_id則為外鍵。如果更新學(xué)生表中的student_id,同時(shí)觸發(fā)成績(jì)表中的student_id更新,即為級聯(lián)更新。外鍵與級聯(lián)更新適用于單機低并發(fā),不適合分布式、高并發(fā)集群;級聯(lián)更新是強阻塞,存在數據庫更新風(fēng)暴的風(fēng)險;外鍵影響數據庫的插入速度。
  • 【強制】禁止使用存儲過(guò)程,存儲過(guò)程難以調試和擴展,更沒(méi)有移植性。
  • 【強制】數據訂正(特別是刪除或修改記錄操作)時(shí),要先select,避免出現誤刪除,確認無(wú)誤才能執行更新語(yǔ)句。
  • 【強制】對于數據庫中表記錄的查詢(xún)和變更,只要涉及多個(gè)表,都需要在列名前加表的別名(或表名)進(jìn)行限定。說(shuō)明:對多表進(jìn)行查詢(xún)記錄、更新記錄、刪除記錄時(shí),如果對操作列沒(méi)有限定表的別名(或表名),并且操作列在多個(gè)表中存在時(shí),就會(huì )拋異常。正例:select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id; 反例:在某業(yè)務(wù)中,由于多表關(guān)聯(lián)查詢(xún)語(yǔ)句沒(méi)有加表的別名(或表名)的限制,正常運行兩年后,最近在某個(gè)表中增加一個(gè)同名字段,在預發(fā)布環(huán)境做數據庫變更后,線(xiàn)上查詢(xún)語(yǔ)句出現出1052異常:Column 'name’ in field list is ambiguous。
  • 【推薦】SQL語(yǔ)句中表的別名前加as,并且以t1、t2、t3、…的順序依次命名。說(shuō)明:1)別名可以是表的簡(jiǎn)稱(chēng),或者是根據表出現的順序,以t1、t2、t3的方式命名。2)別名前加as使別名更容易識別。正例:select t1.name from table_first as t1, table_second as t2 where t1.id=t2.id;
  • 【推薦】in操作能避免則避免,若實(shí)在避免不了,需要仔細評估in后邊的集合元素數量,控制在1000個(gè)之內。
  • 【參考】因國際化需要,所有的字符存儲與表示,均采用utf8字符集,那么字符計數方法需要注意。說(shuō)明:SELECT LENGTH(“輕松工作”); 返回為12 SELECT CHARACTER_LENGTH(“輕松工作”); 返回為4 如果需要存儲表情,那么選擇utf8mb4來(lái)進(jìn)行存儲,注意它與utf8編碼的區別。
  • 【參考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務(wù)日志資源少,但TRUNCATE無(wú)事務(wù)且不觸發(fā)trigger,有可能造成事故,故不建議在開(kāi)發(fā)代碼中使用此語(yǔ)句。說(shuō)明:TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語(yǔ)句相同。

(四) ORM映射

  • 【強制】在表查詢(xún)中,一律不要使用 * 作為查詢(xún)的字段列表,需要哪些字段必須明確寫(xiě)明。說(shuō)明:1)增加查詢(xún)分析器解析成本。2)增減字段容易與resultMap配置不一致。3)無(wú)用字段增加網(wǎng)絡(luò )消耗,尤其是text類(lèi)型的字段。
  • 【強制】POJO類(lèi)的布爾屬性不能加is,而數據庫字段必須加is_,要求在resultMap中進(jìn)行字段與屬性之間的映射。說(shuō)明:參見(jiàn)定義POJO類(lèi)以及數據庫字段定義規定,在sql.xml增加映射,是必須的。
  • 【強制】不要用resultClass當返回參數,即使所有類(lèi)屬性名與數據庫字段一一對應,也需要定義;反過(guò)來(lái),每一個(gè)表也必然有一個(gè)與之對應。說(shuō)明:配置映射關(guān)系,使字段與DO類(lèi)解耦,方便維護。
  • 【強制】sql.xml配置參數使用:#{},#param# 不要使用${} 此種方式容易出現SQL注入。
  • 【強制】iBATIS自帶的queryForList(String statementName,int start,int size)不推薦使用。說(shuō)明:其實(shí)現方式是在數據庫取到statementName對應的SQL語(yǔ)句的所有記錄,再通過(guò)subList取start,size的子集合。

正例:

Map<String, Object> map = new HashMap<>();
map.put(“start”, start);
map.put(“size”, size);
  • 【強制】不允許直接拿HashMap與Hashtable作為查詢(xún)結果集的輸出。反例:某同學(xué)為避免寫(xiě)一個(gè),直接使用HashTable來(lái)接收數據庫返回結果,結果出現日常是把bigint轉成Long值,而線(xiàn)上由于數據庫版本不一樣,解析成BigInteger,導致線(xiàn)上問(wèn)題。
  • 【強制】更新數據表記錄時(shí),必須同時(shí)更新記錄對應的gmt_modified字段值為當前時(shí)間。
  • 【推薦】不要寫(xiě)一個(gè)大而全的數據更新接口。傳入為POJO類(lèi),不管是不是自己的目標更新字段,都進(jìn)行update table set c1=value1,c2=value2,c3=value3; 這是不對的。執行SQL時(shí),不要更新無(wú)改動(dòng)的字段,一是易出錯;二是效率低;三是增加binlog存儲。
  • 【參考】@Transactional事務(wù)不要濫用。事務(wù)會(huì )影響數據庫的QPS,另外使用事務(wù)的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統計修正等。
  • 【參考】中的compareValue是與屬性值對比的常量,一般是數字,表示相等時(shí)帶上此條件;表示不為空且不為null時(shí)執行;表示不為null值時(shí)執行。

六、MySQL數據類(lèi)型選擇

在MySQL中,選擇正確的數據類(lèi)型,對于性能至關(guān)重要。一般應該遵循下面兩步:

  • 確定合適的大類(lèi)型:數字、字符串、時(shí)間、二進(jìn)制;
  • 確定具體的類(lèi)型:有無(wú)符號、取值范圍、變長(cháng)定長(cháng)等。

在MySQL數據類(lèi)型設置方面,盡量用更小的數據類(lèi)型,因為它們通常有更好的性能,花費更少的硬件資源。并且,盡量 把字段定義為NOT NULL,避免使用NULL.

1、數值類(lèi)型

優(yōu)化建議:

  • 如果整形數據沒(méi)有負數,如ID號,建議指定為UNSIGNED無(wú)符號類(lèi)型,容量可以擴大一倍。
  • 建議使用TINYINT代替ENUM、BITENUM、SET。
  • 避免使用整數的顯示寬度(參看文檔最后),也就是說(shuō),不要用INT(10)類(lèi)似的方法指定字段顯示寬度,直接用INT。
  • DECIMAL最適合保存準確度要求高,而且用于計算的數據,比如價(jià)格。但是在使用DECIMAL類(lèi)型的時(shí)候,注意長(cháng)度設置。
  • 建議使用整形類(lèi)型來(lái)運算和存儲實(shí)數,方法是,實(shí)數乘以相應的倍數后再操作。
  • 整數通常是最佳的數據類(lèi)型,因為它速度快,并且能使用AUTO_INCREMENT。
2、日期和時(shí)間

優(yōu)化建議:

  • MySQL能存儲的最小時(shí)間粒度為秒。
  • 建議用DATE數據類(lèi)型來(lái)保存日期。MySQL中默認的日期格式是yyyy-mm-dd。
  • 用MySQL的內建類(lèi)型DATE、TIME、DATETIME來(lái)存儲時(shí)間,而不是使用字符串。
  • 當數據格式為T(mén)IMESTAMP和DATETIME時(shí),可以用CURRENT_TIMESTAMP作為默認(MySQL5.6以后),MySQL會(huì )自動(dòng)返回記錄插入的確切時(shí)間。
  • TIMESTAMP是UTC時(shí)間戳,與時(shí)區相關(guān)。
  • DATETIME的存儲格式是一個(gè)YYYYMMDD HH:MM:SS的整數,與時(shí)區無(wú)關(guān),你存了什么,讀出來(lái)就是什么。
  • 除非有特殊需求,一般的公司建議使用TIMESTAMP,它比DATETIME更節約空間,但是像阿里這樣的公司一會(huì )用DATETIME,因為不用考慮TIMESTAMP將來(lái)的時(shí)間上限問(wèn)題。
  • 有時(shí)人們把Unix的時(shí)間戳保存為整數值,但是這通常沒(méi)有任何好處,這種格式處理起來(lái)不太方便,我們并不推薦它。
3、字符串

優(yōu)化建議:

  • 字符串的長(cháng)度相差較大用VARCHAR;字符串短,且所有值都接近一個(gè)長(cháng)度用CHAR。
  • CHAR和VARCHAR適用于包括人名、郵政編碼、電話(huà)號碼和不超過(guò)255個(gè)字符長(cháng)度的任意字母數字組合。那些要用來(lái)計算的數字不要用VARCHAR類(lèi)型保存,因為可能會(huì )導致一些與計算相關(guān)的問(wèn)題。換句話(huà)說(shuō),可能影響到計算的準確性和完整性。
  • 盡量少用BLOB和TEXT,如果實(shí)在要用可以考慮將BLOB和TEXT字段單獨存一張表,用id關(guān)聯(lián)。
  • BLOB系列存儲二進(jìn)制字符串,與字符集無(wú)關(guān)。TEXT系列存儲非二進(jìn)制字符串,與字符集相關(guān)。
  • BLOB和TEXT都不能有默認值。

PS:INT顯示寬度

我們經(jīng)常會(huì )使用命令來(lái)創(chuàng )建數據表,而且同時(shí)會(huì )指定一個(gè)長(cháng)度,如下。但是,這里的長(cháng)度并非是TINYINT類(lèi)型存儲的最大長(cháng)度,而是顯示的最大長(cháng)度。

CREATE TABLE `user`(
 `id` TINYINT(2UNSIGNED
);

這里表示user表的id字段的類(lèi)型是TINYINT,可以存儲的最大數值是255。所以,在存儲數據時(shí),如果存入值小于等于255,如200,雖然超過(guò)2位,但是沒(méi)有超出TINYINT類(lèi)型長(cháng)度,所以可以正常保存;如果存入值大于255,如500,那么MySQL會(huì )自動(dòng)保存為T(mén)INYINT類(lèi)型的最大值255。

在查詢(xún)數據時(shí),不管查詢(xún)結果為何值,都按實(shí)際輸出。這里TINYINT(2)中2的作用就是,當需要在查詢(xún)結果前填充0時(shí),命令中加上ZEROFILL就可以實(shí)現,如:

`id` TINYINT(2) UNSIGNED ZEROFILL

這樣,查詢(xún)結果如果是5,那輸出就是05。如果指定TINYINT(5),那輸出就是00005,其實(shí)實(shí)際存儲的值還是5,而且存 儲的數據不會(huì )超過(guò)255,只是MySQL輸出數據時(shí)在前面填充了0。

換句話(huà)說(shuō),在MySQL命令中,字段的類(lèi)型長(cháng)度TINYINT(2)、INT(11)不會(huì )影響數據的插入,只會(huì )在使用ZEROFILL時(shí)有 用,讓查詢(xún)結果前填充0。


本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
SQL優(yōu)化技巧 改善數據庫性能
數據倉庫中的SQL性能優(yōu)化(MySQL篇)
count(*)查詢(xún)性能很差?用這5招輕松優(yōu)化
你們要的多表查詢(xún)優(yōu)化來(lái)啦!請查收
項目中常用的19條MySQL優(yōu)化
mysql整理
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久