中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)上做衣服的網(wǎng)站優(yōu)化設(shè)計(jì)的答案

網(wǎng)上做衣服的網(wǎng)站,優(yōu)化設(shè)計(jì)的答案,網(wǎng)址短鏈接在線(xiàn)生成,餐飲業(yè)網(wǎng)站建設(shè)招標(biāo)書(shū)一、索引優(yōu)化 接口性能優(yōu)化時(shí),大家第一個(gè)想到的通常是:優(yōu)化索引。 確實(shí),優(yōu)化索引的成本是最小的。 你可以通過(guò)查看線(xiàn)上日志或監(jiān)控報(bào)告,發(fā)現(xiàn)某個(gè)接口使用的某條SQL語(yǔ)句耗時(shí)較長(zhǎng)。 此時(shí),你可能會(huì)有以下疑問(wèn)&#xff…

一、索引優(yōu)化

接口性能優(yōu)化時(shí),大家第一個(gè)想到的通常是:優(yōu)化索引。

確實(shí),優(yōu)化索引的成本是最小的。

你可以通過(guò)查看線(xiàn)上日志或監(jiān)控報(bào)告,發(fā)現(xiàn)某個(gè)接口使用的某條SQL語(yǔ)句耗時(shí)較長(zhǎng)。

此時(shí),你可能會(huì)有以下疑問(wèn):

這條SQL語(yǔ)句是否已經(jīng)加了索引?
加的索引是否生效了?
MySQL是否選擇了錯(cuò)誤的索引?

1.1 沒(méi)加索引

在SQL語(yǔ)句中,忘記為WHERE條件的關(guān)鍵字段或ORDER BY后的排序字段加索引是項(xiàng)目中常見(jiàn)的問(wèn)題。

在項(xiàng)目初期,由于表中的數(shù)據(jù)量較小,加不加索引對(duì)SQL查詢(xún)性能影響不大。

然而,隨著業(yè)務(wù)的發(fā)展,表中的數(shù)據(jù)量不斷增加,這時(shí)就必須加索引了。

可以通過(guò)以下命令查看/添加索引:

show?index?from?`table_name`
CREATE?INDEX?index_name?ON?table_name?(column_name);

這種方式能夠顯著提高查詢(xún)性能,尤其是在數(shù)據(jù)量龐大的情況下。

1.2 索引沒(méi)生效

通過(guò)上述命令我們已經(jīng)確認(rèn)索引是存在的,但它是否生效呢?

此時(shí),你可能會(huì)有這樣的疑問(wèn)。

那么,如何查看索引是否生效呢?

答案是:可以使用?EXPLAIN?命令,查看?MySQL?的執(zhí)行計(jì)劃,它會(huì)顯示索引的使用情況。

例如:

EXPLAIN?SELECT?*?FROM?`order`?WHERE?code='002';

結(jié)果:

這個(gè)命令將顯示查詢(xún)的執(zhí)行計(jì)劃,包括使用了哪些索引。

如果索引生效,你會(huì)在輸出結(jié)果中看到相關(guān)的信息。

通過(guò)這幾列可以判斷索引使用情況,執(zhí)行計(jì)劃包含列的含義如下圖所示:

說(shuō)實(shí)話(huà),SQL語(yǔ)句沒(méi)有使用索引,除去沒(méi)有建索引的情況外,最大的可能性是索引失效了。

以下是索引失效的常見(jiàn)原因:

了解這些原因,可以幫助你在查詢(xún)優(yōu)化時(shí)避免索引失效的問(wèn)題,確保數(shù)據(jù)庫(kù)查詢(xún)性能保持最佳。

1.3 選錯(cuò)索引

此外,你是否遇到過(guò)這樣一種情況:明明是同一條SQL語(yǔ)句,只是入?yún)⒉煌?/p>

有時(shí)候使用的是索引A,有時(shí)候卻使用索引B?

沒(méi)錯(cuò),有時(shí)候MySQL會(huì)選錯(cuò)索引。

必要時(shí)可以使用?FORCE INDEX?來(lái)強(qiáng)制查詢(xún)SQL使用某個(gè)索引。

例如:

SELECT?*?FROM?`order`?FORCE?INDEX?(index_name)?WHERE?code='002';

至于為什么MySQL會(huì)選錯(cuò)索引,原因可能有以下幾點(diǎn):

了解這些原因,可以幫助你更好地理解和控制MySQL的索引選擇行為,確保查詢(xún)性能的穩(wěn)定性。

二、SQL優(yōu)化

如果優(yōu)化了索引之后效果不明顯,接下來(lái)可以嘗試優(yōu)化一下SQL語(yǔ)句,因?yàn)橄鄬?duì)于修改Java代碼來(lái)說(shuō),改造SQL語(yǔ)句的成本要小得多。

以下是SQL優(yōu)化的15個(gè)小技巧:

三、遠(yuǎn)程調(diào)用

多時(shí)候,我們需要在一個(gè)接口中調(diào)用其他服務(wù)的接口。

例如,有這樣的業(yè)務(wù)場(chǎng)景:

在用戶(hù)信息查詢(xún)接口中需要返回以下信息:用戶(hù)名稱(chēng)、性別、等級(jí)、頭像、積分和成長(zhǎng)值。

其中,用戶(hù)名稱(chēng)、性別、等級(jí)和頭像存儲(chǔ)在用戶(hù)服務(wù)中,積分存儲(chǔ)在積分服務(wù)中,成長(zhǎng)值存儲(chǔ)在成長(zhǎng)值服務(wù)中。為了將這些數(shù)據(jù)統(tǒng)一返回,我們需要提供一個(gè)額外的對(duì)外接口服務(wù)。

因此,用戶(hù)信息查詢(xún)接口需要調(diào)用用戶(hù)查詢(xún)接口、積分查詢(xún)接口和成長(zhǎng)值查詢(xún)接口,然后將數(shù)據(jù)匯總并統(tǒng)一返回。

調(diào)用過(guò)程如下圖所示:

調(diào)用遠(yuǎn)程接口總耗時(shí) 530ms = 200ms + 150ms + 180ms

顯然這種串行調(diào)用遠(yuǎn)程接口性能是非常不好的,調(diào)用遠(yuǎn)程接口總的耗時(shí)為所有的遠(yuǎn)程接口耗時(shí)之和。

3.1 串行改并行

上面說(shuō)到,既然串行調(diào)用多個(gè)遠(yuǎn)程接口性能很差,為什么不改成并行呢?

如下圖所示:

調(diào)用遠(yuǎn)程接口的總耗時(shí)為200ms,這等于耗時(shí)最長(zhǎng)的那次遠(yuǎn)程接口調(diào)用時(shí)間。

在Java 8之前,可以通過(guò)實(shí)現(xiàn)Callable接口來(lái)獲取線(xiàn)程的返回結(jié)果。

在Java 8之后,可以通過(guò)CompletableFuture類(lèi)來(lái)實(shí)現(xiàn)這一功能。

以下是一個(gè)使用CompletableFuture的示例:

public?class?RemoteServiceExample?{public?static?void?main(String[]?args)?throws?ExecutionException,?InterruptedException?{//?調(diào)用用戶(hù)服務(wù)接口CompletableFuture<String>?userFuture?=?CompletableFuture.supplyAsync(()?->?{//?模擬遠(yuǎn)程調(diào)用simulateDelay(200);return?"User?Info";});//?調(diào)用積分服務(wù)接口CompletableFuture<String>?pointsFuture?=?CompletableFuture.supplyAsync(()?->?{//?模擬遠(yuǎn)程調(diào)用simulateDelay(150);return?"Points?Info";});//?調(diào)用成長(zhǎng)值服務(wù)接口CompletableFuture<String>?growthFuture?=?CompletableFuture.supplyAsync(()?->?{//?模擬遠(yuǎn)程調(diào)用simulateDelay(100);return?"Growth?Info";});//?匯總結(jié)果CompletableFuture<Void>?allOf?=?CompletableFuture.allOf(userFuture,?pointsFuture,?growthFuture);//?等待所有異步操作完成allOf.join();//?獲取結(jié)果String?userInfo?=?userFuture.get();String?pointsInfo?=?pointsFuture.get();String?growthInfo?=?growthFuture.get();}
}

3.2?數(shù)據(jù)異構(gòu)

為了提升接口性能,尤其在高并發(fā)場(chǎng)景下,可以考慮數(shù)據(jù)冗余,將用戶(hù)信息、積分和成長(zhǎng)值的數(shù)據(jù)統(tǒng)一存儲(chǔ)在一個(gè)地方,比如Redis。

這樣,通過(guò)用戶(hù)ID可以直接從Redis中查詢(xún)所需的數(shù)據(jù),從而避免遠(yuǎn)程接口調(diào)用

但需要注意的是,如果使用了數(shù)據(jù)異構(gòu)方案,就可能會(huì)出現(xiàn)數(shù)據(jù)一致性問(wèn)題。

用戶(hù)信息、積分和成長(zhǎng)值有更新的話(huà),大部分情況下,會(huì)先更新到數(shù)據(jù)庫(kù),然后同步到redis。

但這種跨庫(kù)的操作,可能會(huì)導(dǎo)致兩邊數(shù)據(jù)不一致的情況產(chǎn)生。

四、重復(fù)調(diào)用

在我們的日常工作代碼中,重復(fù)調(diào)用非常常見(jiàn),但如果沒(méi)有控制好,會(huì)嚴(yán)重影響接口的性能。

讓我們一起來(lái)看看這個(gè)問(wèn)題。

4.1 循環(huán)查數(shù)據(jù)庫(kù) 有時(shí)候,我們需要從指定的用戶(hù)集合中查詢(xún)出哪些用戶(hù)已經(jīng)存在于數(shù)據(jù)庫(kù)中。

一種實(shí)現(xiàn)方式如下:

public?List<User>?findExistingUsers(List<String>?userIds)?{List<User>?existingUsers?=?new?ArrayList<>();for?(String?userId?:?userIds)?{User?user?=?userRepository.findById(userId);if?(user?!=?null)?{existingUsers.add(user);}}return?existingUsers;
}

上述代碼會(huì)對(duì)每個(gè)用戶(hù)ID執(zhí)行一次數(shù)據(jù)庫(kù)查詢(xún),這在用戶(hù)集合較大時(shí)會(huì)導(dǎo)致性能問(wèn)題。

我們可以通過(guò)批量查詢(xún)來(lái)優(yōu)化性能,減少數(shù)據(jù)庫(kù)的查詢(xún)次數(shù)。

public?List<User>?findExistingUsers(List<String>?userIds)?{//?批量查詢(xún)數(shù)據(jù)庫(kù)List<User>?users?=?userRepository.findByIds(userIds);return?users;
}

這里有個(gè)需要注意的地方是:id集合的大小要做限制,最好一次不要請(qǐng)求太多的數(shù)據(jù)。要根據(jù)實(shí)際情況而定,建議控制每次請(qǐng)求的記錄條數(shù)在500以?xún)?nèi)。

五、異步處理

在進(jìn)行接口性能優(yōu)化時(shí),有時(shí)候需要重新梳理業(yè)務(wù)邏輯,檢查是否存在設(shè)計(jì)不合理的地方。

假設(shè)有一個(gè)用戶(hù)請(qǐng)求接口,需要執(zhí)行以下操作:

  1. 業(yè)務(wù)操作

  2. 發(fā)送站內(nèi)通知

  3. 記錄操作日志 為了實(shí)現(xiàn)方便,通常會(huì)將這些邏輯放在接口中同步執(zhí)行,但這會(huì)對(duì)接口性能造成一定影響。

這個(gè)接口表面上看起來(lái)沒(méi)有問(wèn)題,但如果你仔細(xì)梳理一下業(yè)務(wù)邏輯,會(huì)發(fā)現(xiàn)只有業(yè)務(wù)操作才是核心邏輯,其他的功能都是非核心邏輯。

在這里有個(gè)原則就是:

核心邏輯可以同步執(zhí)行,同步寫(xiě)庫(kù)。非核心邏輯,可以異步執(zhí)行,異步寫(xiě)庫(kù)。

上面這個(gè)例子中,發(fā)站內(nèi)通知和用戶(hù)操作日志功能,對(duì)實(shí)時(shí)性要求不高,即使晚點(diǎn)寫(xiě)庫(kù),用戶(hù)無(wú)非是晚點(diǎn)收到站內(nèi)通知,或者運(yùn)營(yíng)晚點(diǎn)看到用戶(hù)操作日志,對(duì)業(yè)務(wù)影響不大,所以完全可以異步處理。

異步處理方案

異步處理通常有兩種主要方式:多線(xiàn)程和消息隊(duì)列(MQ)

5.1 線(xiàn)程池異步優(yōu)化

使用線(xiàn)程池改造之后,接口邏輯如下

5.2 MQ異步

使用線(xiàn)程池有個(gè)小問(wèn)題就是:如果服務(wù)器重啟了,或者是需要被執(zhí)行的功能出現(xiàn)異常了,無(wú)法重試,會(huì)丟數(shù)據(jù)。

為了避免使用線(xiàn)程池處理異步任務(wù)時(shí)出現(xiàn)數(shù)據(jù)丟失的問(wèn)題,可以考慮使用更加健壯和可靠的異步處理方案,如消息隊(duì)列(MQ)。消息隊(duì)列不僅可以異步處理任務(wù),還能夠保證消息的持久化和可靠性,支持重試機(jī)制。

使用mq改造之后,接口邏輯如下

六、避免大事務(wù)

很多小伙伴在使用Spring框架開(kāi)發(fā)項(xiàng)目時(shí),為了方便,喜歡使用@Transactional注解提供事務(wù)功能。

沒(méi)錯(cuò),使用@Transactional注解這種聲明式事務(wù)的方式提供事務(wù)功能,確實(shí)能少寫(xiě)很多代碼,提升開(kāi)發(fā)效率。

但也容易造成大事務(wù),引發(fā)性能的問(wèn)題。

為了避免大事務(wù)引發(fā)的問(wèn)題,可以考慮以下優(yōu)化建議:

  1. 少用@Transactional注解

  2. 將查詢(xún)(select)方法放到事務(wù)外

  3. 事務(wù)中避免遠(yuǎn)程調(diào)用

  4. 事務(wù)中避免一次性處理太多數(shù)據(jù)

  5. 有些功能可以非事務(wù)執(zhí)行

  6. 有些功能可以異步處理

七、鎖粒度

在一些業(yè)務(wù)場(chǎng)景中,為了避免多個(gè)線(xiàn)程并發(fā)修改同一共享數(shù)據(jù)而引發(fā)數(shù)據(jù)異常,通常我們會(huì)使用加鎖的方式來(lái)解決這個(gè)問(wèn)題。

然而,如果鎖的設(shè)計(jì)不當(dāng),導(dǎo)致鎖的粒度過(guò)粗,也會(huì)對(duì)接口性能產(chǎn)生顯著的負(fù)面影響。

7.1 synchronized

在Java中,我們可以使用synchronized關(guān)鍵字來(lái)為代碼加鎖。

通常有兩種寫(xiě)法:在方法上加鎖和在代碼塊上加鎖。

1. 方法上加鎖

public?synchronized?void?doSave(String?fileUrl)?{mkdir();uploadFile(fileUrl);sendMessage(fileUrl);
}

在方法上加鎖的目的是為了防止并發(fā)情況下創(chuàng)建相同的目錄,避免第二次創(chuàng)建失敗而影響業(yè)務(wù)功能。

但這種直接在方法上加鎖的方式,鎖的粒度較粗。

因?yàn)閐oSave方法中的文件上傳和消息發(fā)送并不需要加鎖,只有創(chuàng)建目錄的方法需要加鎖。

我們知道,文件上傳操作非常耗時(shí),如果將整個(gè)方法加鎖,那么需要等到整個(gè)方法執(zhí)行完之后才能釋放鎖。

顯然,這會(huì)導(dǎo)致該方法的性能下降,得不償失。

2. 代碼塊上加鎖我們可以將加鎖改在代碼塊上,從而縮小鎖的粒度, 如下:

public?void?doSave(String?path,?String?fileUrl)?{synchronized(this)?{if?(!exists(path))?{mkdir(path);}}uploadFile(fileUrl);sendMessage(fileUrl);
}

這樣改造后,鎖的粒度變小了,只有并發(fā)創(chuàng)建目錄時(shí)才加鎖。

創(chuàng)建目錄是一個(gè)非??斓牟僮?#xff0c;即使加鎖對(duì)接口性能的影響也不大。

最重要的是,其他的文件上傳和消息發(fā)送功能仍然可以并發(fā)執(zhí)行。

多節(jié)點(diǎn)環(huán)境中的問(wèn)題 在單機(jī)版服務(wù)中,這種做法沒(méi)有問(wèn)題。但在生產(chǎn)環(huán)境中,為了保證服務(wù)的穩(wěn)定性,同一個(gè)服務(wù)通常會(huì)部署在多個(gè)節(jié)點(diǎn)上。如果某個(gè)節(jié)點(diǎn)掛掉,其他節(jié)點(diǎn)的服務(wù)仍然可用。

多節(jié)點(diǎn)部署避免了某個(gè)節(jié)點(diǎn)掛掉導(dǎo)致服務(wù)不可用的情況,同時(shí)也能分?jǐn)傉麄€(gè)系統(tǒng)的流量,避免系統(tǒng)壓力過(guò)大。

但這種部署方式也帶來(lái)了新的問(wèn)題:synchronized只能保證一個(gè)節(jié)點(diǎn)加鎖有效。

7.2 Redis分布式鎖

在分布式系統(tǒng)中,由于Redis分布式鎖的實(shí)現(xiàn)相對(duì)簡(jiǎn)單且高效,因此它在許多實(shí)際業(yè)務(wù)場(chǎng)景中被廣泛采用。

使用Redis分布式鎖的偽代碼如下:

public?boolean?doSave(String?path,?String?fileUrl)?{try?{String?result?=?jedis.set(lockKey,?requestId,?"NX",?"PX",?expireTime);if?("OK".equals(result))?{if?(!exists(path))?{mkdir(path);uploadFile(fileUrl);sendMessage(fileUrl);}return?true;}}?finally?{unlock(lockKey,?requestId);}return?false;
}

與之前使用synchronized關(guān)鍵字加鎖時(shí)一樣,這里的鎖的范圍也太大了,換句話(huà)說(shuō),鎖的粒度太粗。這會(huì)導(dǎo)致整個(gè)方法的執(zhí)行效率很低。

實(shí)際上,只有在創(chuàng)建目錄時(shí)才需要加分布式鎖,其余代碼不需要加鎖。

于是,我們需要優(yōu)化代碼:

public?void?doSave(String?path,?String?fileUrl)?{if?(tryLock())?{try?{if?(!exists(path))?{mkdir(path);}}?finally?{unlock(lockKey,?requestId);}}uploadFile(fileUrl);sendMessage(fileUrl);
}private?boolean?tryLock()?{String?result?=?jedis.set(lockKey,?requestId,?"NX",?"PX",?expireTime);return?"OK".equals(result);
}private?void?unlock(String?lockKey,?String?requestId)?{//?解鎖邏輯
}

上面的代碼將加鎖的范圍縮小了,只有在創(chuàng)建目錄時(shí)才加鎖。這樣的簡(jiǎn)單優(yōu)化后,接口性能可以得到顯著提升。

7.3 數(shù)據(jù)庫(kù)鎖

MySQL數(shù)據(jù)庫(kù)中的三種鎖

  1. 表鎖

    • 優(yōu)點(diǎn):加鎖快,不會(huì)出現(xiàn)死鎖。

    • 缺點(diǎn):鎖定粒度大,鎖沖突的概率高,并發(fā)度最低。

  2. 行鎖

    • 優(yōu)點(diǎn):鎖定粒度最小,鎖沖突的概率低,并發(fā)度最高。

    • 缺點(diǎn):加鎖慢,會(huì)出現(xiàn)死鎖。

  3. 間隙鎖

    • 優(yōu)點(diǎn):鎖定粒度介于表鎖和行鎖之間。

    • 缺點(diǎn):開(kāi)銷(xiāo)和加鎖時(shí)間介于表鎖和行鎖之間,并發(fā)度一般,也會(huì)出現(xiàn)死鎖。

鎖與并發(fā)度

并發(fā)度越高,接口性能越好。因此,數(shù)據(jù)庫(kù)鎖的優(yōu)化方向是:

  1. 優(yōu)先使用行鎖

  2. 其次使用間隙鎖

  3. 最后使用表鎖

八、分頁(yè)處理

有時(shí)候需要調(diào)用某個(gè)接口來(lái)批量查詢(xún)數(shù)據(jù),例如,通過(guò)用戶(hù)ID批量查詢(xún)用戶(hù)信息,然后為這些用戶(hù)贈(zèng)送積分。

但是,如果一次性查詢(xún)的用戶(hù)數(shù)量太多,例如一次查詢(xún)2000個(gè)用戶(hù)的數(shù)據(jù),傳入2000個(gè)用戶(hù)的ID進(jìn)行遠(yuǎn)程調(diào)用時(shí),用戶(hù)查詢(xún)接口經(jīng)常會(huì)出現(xiàn)超時(shí)的情況。

調(diào)用代碼如下:

List<User>?users?=?remoteCallUser(ids);

眾所周知,調(diào)用接口從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)需要經(jīng)過(guò)網(wǎng)絡(luò)傳輸。如果數(shù)據(jù)量過(guò)大,無(wú)論是數(shù)據(jù)獲取速度還是網(wǎng)絡(luò)傳輸速度都會(huì)受到帶寬限制,從而導(dǎo)致耗時(shí)較長(zhǎng)。

優(yōu)化使用:分頁(yè)處理。

將一次性獲取所有數(shù)據(jù)的請(qǐng)求,改為分多次獲取,每次只獲取一部分用戶(hù)的數(shù)據(jù),最后進(jìn)行合并和匯總。

其實(shí),處理這個(gè)問(wèn)題可以分為兩種場(chǎng)景:同步調(diào)用和異步調(diào)用。

8.1 同步調(diào)用

如果在job中需要獲取2000個(gè)用戶(hù)的信息,它要求只要能正確獲取到數(shù)據(jù)即可,對(duì)獲取數(shù)據(jù)的總耗時(shí)要求不高。

但對(duì)每一次遠(yuǎn)程接口調(diào)用的耗時(shí)有要求,不能大于500ms,否則會(huì)有郵件預(yù)警。

這時(shí),我們可以同步分頁(yè)調(diào)用批量查詢(xún)用戶(hù)信息接口。

具體示例代碼如下:

List<List<Long>>?allIds?=?Lists.partition(ids,?200);for?(List<Long>?batchIds?:?allIds)?{List<User>?users?=?remoteCallUser(batchIds);
}

代碼中我使用了Google Guava工具中的Lists.partition方法,用它來(lái)做分頁(yè)簡(jiǎn)直太好用了,不然要寫(xiě)一大堆分頁(yè)的代碼。 8.2 異步調(diào)用 如果是在某個(gè)接口中需要獲取2000個(gè)用戶(hù)的信息,需要考慮的因素更多。

除了遠(yuǎn)程調(diào)用接口的耗時(shí),還需要考慮該接口本身的總耗時(shí),也不能超過(guò)500ms。

這時(shí),使用上面的同步分頁(yè)請(qǐng)求遠(yuǎn)程接口的方法肯定是行不通的。

那么,只能使用異步調(diào)用了。

代碼如下:

List<List<Long>>?allIds?=?Lists.partition(ids,?200);final?List<User>?result?=?Lists.newArrayList();
allIds.stream().forEach(batchIds?->?{CompletableFuture.supplyAsync(()?->?{result.addAll(remoteCallUser(batchIds));return?Boolean.TRUE;},?executor);
});

使用CompletableFuture類(lèi),通過(guò)多個(gè)線(xiàn)程異步調(diào)用遠(yuǎn)程接口,最后匯總結(jié)果統(tǒng)一返回。

九、加緩存

通常情況下,我們最常用的緩存是:Redis和Memcached。

但對(duì)于Java應(yīng)用來(lái)說(shuō),絕大多數(shù)情況下使用的是Redis,所以接下來(lái)我們以Redis為例。

在關(guān)系型數(shù)據(jù)庫(kù)(例如:MySQL)中,菜單通常有上下級(jí)關(guān)系。某個(gè)四級(jí)分類(lèi)是某個(gè)三級(jí)分類(lèi)的子分類(lèi),三級(jí)分類(lèi)是某個(gè)二級(jí)分類(lèi)的子分類(lèi),而二級(jí)分類(lèi)又是某個(gè)一級(jí)分類(lèi)的子分類(lèi)。

這種存儲(chǔ)結(jié)構(gòu)決定了,想一次性查出整個(gè)分類(lèi)樹(shù)并非易事。這需要使用程序遞歸查詢(xún),而如果分類(lèi)很多,這個(gè)遞歸操作會(huì)非常耗時(shí)。

因此,如果每次都直接從數(shù)據(jù)庫(kù)中查詢(xún)分類(lèi)樹(shù)的數(shù)據(jù),會(huì)是一個(gè)非常耗時(shí)的操作。

這時(shí)我們可以使用緩存。在大多數(shù)情況下,接口直接從緩存中獲取數(shù)據(jù)。操作Redis可以使用成熟的框架,比如:Jedis和Redisson等。 使用Jedis的偽代碼如下:

String?json?=?jedis.get(key);
if?(StringUtils.isNotEmpty(json))?{CategoryTree?categoryTree?=?JsonUtil.toObject(json);return?categoryTree;
}
return?queryCategoryTreeFromDb();

十、分庫(kù)分表

有時(shí)候,接口性能受限的并不是其他方面,而是數(shù)據(jù)庫(kù)。

當(dāng)系統(tǒng)發(fā)展到一定階段,用戶(hù)并發(fā)量增加,會(huì)有大量的數(shù)據(jù)庫(kù)請(qǐng)求,這不僅需要占用大量的數(shù)據(jù)庫(kù)連接,還會(huì)帶來(lái)磁盤(pán)IO的性能瓶頸問(wèn)題。

此外,隨著用戶(hù)數(shù)量的不斷增加,產(chǎn)生的數(shù)據(jù)量也越來(lái)越大,一張表可能無(wú)法存儲(chǔ)所有數(shù)據(jù)。由于數(shù)據(jù)量太大,即使SQL語(yǔ)句使用了索引,查詢(xún)數(shù)據(jù)時(shí)也會(huì)非常耗時(shí)。

那么,這種情況下該怎么辦呢?

答案是:需要進(jìn)行分庫(kù)分表。

如下圖所示:

圖中將用戶(hù)庫(kù)拆分成了三個(gè)庫(kù),每個(gè)庫(kù)都包含了三張用戶(hù)表。

如果有用戶(hù)請(qǐng)求過(guò)來(lái),先根據(jù)用戶(hù)ID路由到其中一個(gè)用戶(hù)庫(kù),然后再定位到某張表。

路由的算法有很多:

  1. 根據(jù)ID取模

    • 例如:ID=7,有3張表,則7%3=1,模為1,路由到用戶(hù)表1。

  2. 給ID指定一個(gè)區(qū)間范圍

    • 例如:ID的值是0-10萬(wàn),則數(shù)據(jù)存在用戶(hù)表0;ID的值是10-20萬(wàn),則數(shù)據(jù)存在用戶(hù)表1。

  3. 一致性Hash算法。 分庫(kù)分表主要有兩個(gè)方向:垂直和水平。

1. 垂直分庫(kù)分表
垂直分庫(kù)分表(即業(yè)務(wù)方向)更簡(jiǎn)單,將不同的業(yè)務(wù)數(shù)據(jù)存儲(chǔ)在不同的庫(kù)或表中。

例如,將用戶(hù)數(shù)據(jù)和訂單數(shù)據(jù)存儲(chǔ)在不同的庫(kù)中。

2. 水平分庫(kù)分表
水平分庫(kù)分表(即數(shù)據(jù)方向)上,分庫(kù)和分表的作用有區(qū)別,不能混為一談。

分庫(kù)
  • 目的:解決數(shù)據(jù)庫(kù)連接資源不足問(wèn)題和磁盤(pán)IO的性能瓶頸問(wèn)題。

分表
  • 目的:解決單表數(shù)據(jù)量太大,SQL語(yǔ)句查詢(xún)數(shù)據(jù)時(shí),即使走了索引也非常耗時(shí)的問(wèn)題。此外,還可以解決消耗CPU資源的問(wèn)題。

分庫(kù)分表
  • 目的:綜合解決數(shù)據(jù)庫(kù)連接資源不足、磁盤(pán)IO性能瓶頸、數(shù)據(jù)檢索耗時(shí)和CPU資源消耗等問(wèn)題。

業(yè)務(wù)場(chǎng)景中的應(yīng)用

  1. 只分庫(kù)

    • 用戶(hù)并發(fā)量大,但需要保存的數(shù)據(jù)量很少。

  2. 只分表

    • 用戶(hù)并發(fā)量不大,但需要保存的數(shù)據(jù)量很多。

  3. 分庫(kù)分表

    • 用戶(hù)并發(fā)量大,并且需要保存的數(shù)據(jù)量也很多。

十一、監(jiān)控功能

優(yōu)化接口性能問(wèn)題,除了上面提到的這些常用方法之外,還需要配合使用一些輔助功能,因?yàn)樗鼈冋娴目梢詭臀覀兲嵘檎覇?wèn)題的效率。

11.1 開(kāi)啟慢查詢(xún)?nèi)罩?/h4>

通常情況下,為了定位SQL的性能瓶頸,我們需要開(kāi)啟MySQL的慢查詢(xún)?nèi)罩尽0殉^(guò)指定時(shí)間的SQL語(yǔ)句單獨(dú)記錄下來(lái),方便以后分析和定位問(wèn)題。

開(kāi)啟慢查詢(xún)?nèi)罩拘枰攸c(diǎn)關(guān)注三個(gè)參數(shù):

  • slow_query_log:慢查詢(xún)開(kāi)關(guān)

  • slow_query_log_file:慢查詢(xún)?nèi)罩敬娣诺穆窂?/p>

  • long_query_time:超過(guò)多少秒才會(huì)記錄日志

通過(guò)MySQL的SET命令可以設(shè)置:

SET?GLOBAL?slow_query_log?=?'ON';
SET?GLOBAL?slow_query_log_file?=?'/usr/local/mysql/data/slow.log';
SET?GLOBAL?long_query_time?=?2;

設(shè)置完之后,如果某條SQL的執(zhí)行時(shí)間超過(guò)了2秒,會(huì)被自動(dòng)記錄到slow.log文件中。

當(dāng)然,也可以直接修改配置文件my.cnf:

[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 2

但這種方式需要重啟MySQL服務(wù)。

很多公司每天早上都會(huì)發(fā)一封慢查詢(xún)?nèi)罩镜泥]件,開(kāi)發(fā)人員根據(jù)這些信息優(yōu)化SQL。

11.2 加監(jiān)控

為了在出現(xiàn)SQL問(wèn)題時(shí)能夠及時(shí)發(fā)現(xiàn),我們需要對(duì)系統(tǒng)做監(jiān)控。

目前業(yè)界使用比較多的開(kāi)源監(jiān)控系統(tǒng)是:Prometheus

它提供了監(jiān)控和預(yù)警的功能。

架構(gòu)圖如下:

我們可以用它監(jiān)控如下信息:

  • 接口響應(yīng)時(shí)間

  • 調(diào)用第三方服務(wù)耗時(shí)

  • 慢查詢(xún)sql耗時(shí)

  • cpu使用情況

  • 內(nèi)存使用情況

  • 磁盤(pán)使用情況

  • 數(shù)據(jù)庫(kù)使用情況

  • 等等。。。

它的界面大概長(zhǎng)這樣子:

可以看到MySQL的當(dāng)前QPS、活躍線(xiàn)程數(shù)、連接數(shù)、緩存池的大小等信息。

如果發(fā)現(xiàn)連接池占用的數(shù)據(jù)量太多,肯定會(huì)對(duì)接口性能造成影響。

這時(shí)可能是由于代碼中開(kāi)啟了連接卻忘記關(guān)閉,或者并發(fā)量太大導(dǎo)致的,需要進(jìn)一步排查和系統(tǒng)優(yōu)化

11.3鏈路跟蹤

有時(shí)候,一個(gè)接口涉及的邏輯非常復(fù)雜,例如查詢(xún)數(shù)據(jù)庫(kù)、查詢(xún)Redis、遠(yuǎn)程調(diào)用接口、發(fā)送MQ消息以及執(zhí)行業(yè)務(wù)代碼等等。

這種情況下,接口的一次請(qǐng)求會(huì)涉及到非常長(zhǎng)的調(diào)用鏈路。如果逐一排查這些問(wèn)題,會(huì)耗費(fèi)大量時(shí)間,此時(shí)我們已經(jīng)無(wú)法用傳統(tǒng)的方法來(lái)定位問(wèn)題。

有沒(méi)有辦法解決這個(gè)問(wèn)題呢?

答案是使用分布式鏈路跟蹤系統(tǒng):SkyWalking

SkyWalking的架構(gòu)圖如下:

在SkyWalking中,可以通過(guò)traceId(全局唯一的ID)來(lái)串聯(lián)一個(gè)接口請(qǐng)求的完整鏈路。你可以看到整個(gè)接口的耗時(shí)、調(diào)用的遠(yuǎn)程服務(wù)的耗時(shí)、訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)或者Redis的耗時(shí)等,功能非常強(qiáng)大。

之前沒(méi)有這個(gè)功能時(shí),為了定位線(xiàn)上接口性能問(wèn)題,我們需要在代碼中加日志,手動(dòng)打印出鏈路中各個(gè)環(huán)節(jié)的耗時(shí)情況,然后再逐一排查。這種方法不僅費(fèi)時(shí)費(fèi)力,而且容易遺漏細(xì)節(jié)。

如果你用過(guò)SkyWalking來(lái)排查接口性能問(wèn)題,你會(huì)不自覺(jué)地愛(ài)上它的功能。如果你想了解更多功能,可以訪(fǎng)問(wèn)SkyWalking的官網(wǎng):www.skywalking.apache.org

http://www.risenshineclean.com/news/8421.html

相關(guān)文章:

  • 政府網(wǎng)站平臺(tái)安全建設(shè)seo排名優(yōu)化軟件有用
  • 銷(xiāo)量不高的網(wǎng)站怎么做福州seo網(wǎng)站管理
  • seo教程合集seo網(wǎng)絡(luò)推廣課程
  • 自己做外貿(mào)開(kāi)通什么網(wǎng)站百度信息流效果怎么樣
  • 免費(fèi)php企業(yè)網(wǎng)站星巴克網(wǎng)絡(luò)營(yíng)銷(xiāo)案例分析
  • 北京市著名的網(wǎng)站制作公司怎么做網(wǎng)站優(yōu)化排名
  • 飛揚(yáng)世紀(jì)網(wǎng)站建設(shè)站長(zhǎng)工具查詢(xún)網(wǎng)站
  • 北京最大的網(wǎng)站建設(shè)有限公司cms網(wǎng)站
  • 青島做網(wǎng)站哪個(gè)公司好東莞市網(wǎng)絡(luò)seo推廣企業(yè)
  • 廣州網(wǎng)站建設(shè)制作北京優(yōu)化seo
  • wordpress副標(biāo)題調(diào)用長(zhǎng)沙網(wǎng)站包年優(yōu)化
  • 在中國(guó)可以做國(guó)外的域名網(wǎng)站嗎免費(fèi)做網(wǎng)站軟件
  • wordpress虛擬主機(jī)企業(yè)關(guān)鍵詞優(yōu)化最新報(bào)價(jià)
  • 企業(yè)網(wǎng)站制作公司電話(huà)怎么申請(qǐng)域名建網(wǎng)站
  • 漳州網(wǎng)站建設(shè)seo研究學(xué)院
  • 佛山網(wǎng)站建設(shè) 天博網(wǎng)絡(luò)廣告策劃
  • 網(wǎng)站如何帶來(lái)流量鄭州網(wǎng)絡(luò)推廣排名
  • 網(wǎng)站策劃主要工作是什么百度在線(xiàn)掃題入口
  • 奶茶加盟網(wǎng)站建設(shè)外鏈網(wǎng)盤(pán)
  • 做網(wǎng)站專(zhuān)家一個(gè)網(wǎng)站推廣
  • 彩票網(wǎng)站開(kāi)發(fā)風(fēng)險(xiǎn)中美關(guān)系最新消息
  • wordpress彈移動(dòng)端seo關(guān)鍵詞優(yōu)化
  • app軟件系統(tǒng)定制開(kāi)發(fā)網(wǎng)站內(nèi)部鏈接優(yōu)化方法
  • 南京核酸最新通知重慶網(wǎng)站seo公司
  • 做張家界旅游網(wǎng)站多少錢(qián)全網(wǎng)營(yíng)銷(xiāo)有哪些平臺(tái)
  • 深圳app開(kāi)發(fā)公司大概價(jià)格seo關(guān)鍵詞分析表
  • 游戲源代碼網(wǎng)站百度推廣電話(huà)號(hào)碼
  • 手機(jī)網(wǎng)站開(kāi)發(fā)公司百度搜索引擎api
  • 自助網(wǎng)站模板平臺(tái)北大青鳥(niǎo)
  • asp網(wǎng)站banner修改市場(chǎng)營(yíng)銷(xiāo)計(jì)劃方案