chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一些行之有效的,優(yōu)化接口性能的辦法

電子工程師 ? 來(lái)源:良許Linux ? 作者:良許Linux ? 2022-08-08 11:11 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群


			

接口性能優(yōu)化對(duì)于從事后端開(kāi)發(fā)的同學(xué)來(lái)說(shuō),肯定再熟悉不過(guò)了,因?yàn)樗且粋€(gè)跟開(kāi)發(fā)語(yǔ)言無(wú)關(guān)的公共問(wèn)題。

該問(wèn)題說(shuō)簡(jiǎn)單也簡(jiǎn)單,說(shuō)復(fù)雜也復(fù)雜。

有時(shí)候,只需加個(gè)索引就能解決問(wèn)題。

有時(shí)候,需要做代碼重構(gòu)。

有時(shí)候,需要增加緩存。

有時(shí)候,需要引入一些中間件,比如mq。

有時(shí)候,需要需要分庫(kù)分表。

有時(shí)候,需要拆分服務(wù)。

等等。。。

導(dǎo)致接口性能問(wèn)題的原因千奇百怪,不同的項(xiàng)目不同的接口,原因可能也不一樣。

本文我總結(jié)了一些行之有效的,優(yōu)化接口性能的辦法,給有需要的朋友一個(gè)參考。

1.索引

接口性能優(yōu)化大家第一個(gè)想到的可能是:優(yōu)化索引。

沒(méi)錯(cuò),優(yōu)化索引的成本是最小的。

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

這時(shí)你可能會(huì)有下面這些疑問(wèn):

  1. 該sql語(yǔ)句加索引了沒(méi)?
  2. 加的索引生效了沒(méi)?
  3. mysql選錯(cuò)索引了沒(méi)?

1.1 沒(méi)加索引

sql語(yǔ)句中where條件的關(guān)鍵字段,或者order by后面的排序字段,忘了加索引,這個(gè)問(wèn)題在項(xiàng)目中很常見(jiàn)。

項(xiàng)目剛開(kāi)始的時(shí)候,由于表中的數(shù)據(jù)量小,加不加索引sql查詢(xún)性能差別不大。

后來(lái),隨著業(yè)務(wù)的發(fā)展,表中數(shù)據(jù)量越來(lái)越多,就不得不加索引了。

可以通過(guò)命令:

showindexfrom`order`;

能單獨(dú)查看某張表的索引情況。

也可以通過(guò)命令:

showcreatetable`order`;

查看整張表的建表語(yǔ)句,里面同樣會(huì)顯示索引情況。

通過(guò)ALTER TABLE命令可以添加索引:

ALTERTABLE`order`ADDINDEXidx_name(name);

也可以通過(guò)CREATE INDEX命令添加索引:

CREATEINDEXidx_nameON`order`(name);

不過(guò)這里有一個(gè)需要注意的地方是:想通過(guò)命令修改索引,是不行的。

目前在mysql中如果想要修改索引,只能先刪除索引,再重新添加新的。

刪除索引可以用DROP INDEX命令:

ALTERTABLE`order`DROPINDEXidx_name;

DROP INDEX命令也行:

DROPINDEXidx_nameON`order`;

1.2 索引沒(méi)生效

通過(guò)上面的命令我們已經(jīng)能夠確認(rèn)索引是有的,但它生效了沒(méi)?此時(shí)你內(nèi)心或許會(huì)冒出這樣一個(gè)疑問(wèn)。

那么,如何查看索引有沒(méi)有生效呢?

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

例如:

explainselect*from`order`wherecode='002';

結(jié)果:

7e75506c-16c7-11ed-ba43-dac502259ad0.jpg

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

7e8088d8-16c7-11ed-ba43-dac502259ad0.jpg

說(shuō)實(shí)話,sql語(yǔ)句沒(méi)有走索引,排除沒(méi)有建索引之外,最大的可能性是索引失效了。

下面說(shuō)說(shuō)索引失效的常見(jiàn)原因:

7e8cf5c8-16c7-11ed-ba43-dac502259ad0.jpg

如果不是上面的這些原因,則需要再進(jìn)一步排查一下其他原因。

1.3 選錯(cuò)索引

此外,你有沒(méi)有遇到過(guò)這樣一種情況:明明是同一條sql,只有入?yún)⒉煌?。有的時(shí)候走的索引a,有的時(shí)候卻走的索引b?

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

必要時(shí)可以使用force index來(lái)強(qiáng)制查詢(xún)sql走某個(gè)索引。

2. sql優(yōu)化

如果優(yōu)化了索引之后,也沒(méi)啥效果。

接下來(lái)試著優(yōu)化一下sql語(yǔ)句,因?yàn)樗母脑斐杀鞠鄬?duì)于java代碼來(lái)說(shuō)也要小得多。

下面給大家列舉了sql優(yōu)化的15個(gè)小技巧:

7e9a2e50-16c7-11ed-ba43-dac502259ad0.jpg

3. 遠(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í)、頭像在用戶(hù)服務(wù)中,積分在積分服務(wù)中,成長(zhǎng)值在成長(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ò)程如下圖所示:

7ea8f57a-16c7-11ed-ba43-dac502259ad0.png

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

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

那么如何優(yōu)化遠(yuǎn)程接口性能呢?

3.1 并行調(diào)用

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

如下圖所示:

7eb76baa-16c7-11ed-ba43-dac502259ad0.png

調(diào)用遠(yuǎn)程接口總耗時(shí) 200ms = 200ms(即耗時(shí)最長(zhǎng)的那次遠(yuǎn)程接口調(diào)用)

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

java8以后通過(guò)CompleteFuture類(lèi)實(shí)現(xiàn)該功能。我們這里以CompleteFuture為例:

publicUserInfogetUserInfo(Longid)throwsInterruptedException,ExecutionException{
finalUserInfouserInfo=newUserInfo();
CompletableFutureuserFuture=CompletableFuture.supplyAsync(()->{
getRemoteUserAndFill(id,userInfo);
returnBoolean.TRUE;
},executor);

CompletableFuturebonusFuture=CompletableFuture.supplyAsync(()->{
getRemoteBonusAndFill(id,userInfo);
returnBoolean.TRUE;
},executor);

CompletableFuturegrowthFuture=CompletableFuture.supplyAsync(()->{
getRemoteGrowthAndFill(id,userInfo);
returnBoolean.TRUE;
},executor);
CompletableFuture.allOf(userFuture,bonusFuture,growthFuture).join();

userFuture.get();
bonusFuture.get();
growthFuture.get();

returnuserInfo;
}

溫馨提醒一下,這兩種方式別忘了使用線程池。示例中我用到了executor,表示自定義的線程池,為了防止高并發(fā)場(chǎng)景下,出現(xiàn)線程過(guò)多的問(wèn)題。

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

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

那么,我們能不能把數(shù)據(jù)冗余一下,把用戶(hù)信息、積分和成長(zhǎng)值的數(shù)據(jù)統(tǒng)一存儲(chǔ)到一個(gè)地方,比如:redis,存的數(shù)據(jù)結(jié)構(gòu)就是用戶(hù)信息查詢(xún)接口所需要的內(nèi)容。然后通過(guò)用戶(hù)id,直接從redis中查詢(xún)數(shù)據(jù)出來(lái),不就OK了?

如果在高并發(fā)的場(chǎng)景下,為了提升接口性能,遠(yuǎn)程接口調(diào)用大概率會(huì)被去掉,而改成保存冗余數(shù)據(jù)的數(shù)據(jù)異構(gòu)方案。

7ec4208e-16c7-11ed-ba43-dac502259ad0.png

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

用戶(hù)信息、積分和成長(zhǎng)值有更新的話,大部分情況下,會(huì)先更新到數(shù)據(jù)庫(kù),然后同步到redis。但這種跨庫(kù)的操作,可能會(huì)導(dǎo)致兩邊數(shù)據(jù)不一致的情況產(chǎn)生。

4. 重復(fù)調(diào)用

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

不信,我們一起看看。

4.1 循環(huán)查數(shù)據(jù)庫(kù)

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

實(shí)現(xiàn)代碼可以這樣寫(xiě):

publicListqueryUser(ListsearchList){
if(CollectionUtils.isEmpty(searchList)){
returnCollections.emptyList();
}

Listresult=Lists.newArrayList();
searchList.forEach(user->result.add(userMapper.getUserById(user.getId())));
returnresult;
}

這里如果有50個(gè)用戶(hù),則需要循環(huán)50次,去查詢(xún)數(shù)據(jù)庫(kù)。我們都知道,每查詢(xún)一次數(shù)據(jù)庫(kù),就是一次遠(yuǎn)程調(diào)用。

如果查詢(xún)50次數(shù)據(jù)庫(kù),就有50次遠(yuǎn)程調(diào)用,這是非常耗時(shí)的操作。

那么,我們?nèi)绾蝺?yōu)化呢?

具體代碼如下:

publicListqueryUser(ListsearchList){
if(CollectionUtils.isEmpty(searchList)){
returnCollections.emptyList();
}
Listids=searchList.stream().map(User::getId).collect(Collectors.toList());
returnuserMapper.getUserByIds(ids);
}

提供一個(gè)根據(jù)用戶(hù)id集合批量查詢(xún)用戶(hù)的接口,只遠(yuǎn)程調(diào)用一次,就能查詢(xún)出所有的數(shù)據(jù)。

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

4.2 死循環(huán)

有些小伙伴看到這個(gè)標(biāo)題,可能會(huì)感到有點(diǎn)意外,死循環(huán)也算?

代碼中不是應(yīng)該避免死循環(huán)嗎?為啥還是會(huì)產(chǎn)生死循環(huán)?

有時(shí)候死循環(huán)是我們自己寫(xiě)的,例如下面這段代碼:

while(true){
if(condition){
break;
}
System.out.println("dosamething");
}

這里使用了while(true)的循環(huán)調(diào)用,這種寫(xiě)法在CAS自旋鎖中使用比較多。

當(dāng)滿(mǎn)足condition等于true的時(shí)候,則自動(dòng)退出該循環(huán)。

如果condition條件非常復(fù)雜,一旦出現(xiàn)判斷不正確,或者少寫(xiě)了一些邏輯判斷,就可能在某些場(chǎng)景下出現(xiàn)死循環(huán)的問(wèn)題。

出現(xiàn)死循環(huán),大概率是開(kāi)發(fā)人員人為的bug導(dǎo)致的,不過(guò)這種情況很容易被測(cè)出來(lái)。

還有一種隱藏的比較深的死循環(huán),是由于代碼寫(xiě)的不太嚴(yán)謹(jǐn)導(dǎo)致的。如果用正常數(shù)據(jù),可能測(cè)不出問(wèn)題,但一旦出現(xiàn)異常數(shù)據(jù),就會(huì)立即出現(xiàn)死循環(huán)。

4.3 無(wú)限遞歸

如果想要打印某個(gè)分類(lèi)的所有父分類(lèi),可以用類(lèi)似這樣的遞歸方法實(shí)現(xiàn):

publicvoidprintCategory(Categorycategory){
if(category==null
||category.getParentId()==null){
return;
}
System.out.println("父分類(lèi)名稱(chēng):"+category.getName());
Categoryparent=categoryMapper.getCategoryById(category.getParentId());
printCategory(parent);
}

正常情況下,這段代碼是沒(méi)有問(wèn)題的。

但如果某次有人誤操作,把某個(gè)分類(lèi)的parentId指向了它自己,這樣就會(huì)出現(xiàn)無(wú)限遞歸的情況。導(dǎo)致接口一直不能返回?cái)?shù)據(jù),最終會(huì)發(fā)生堆棧溢出。

建議寫(xiě)遞歸方法時(shí),設(shè)定一個(gè)遞歸的深度,比如:分類(lèi)最大等級(jí)有4級(jí),則深度可以設(shè)置為4。然后在遞歸方法中做判斷,如果深度大于4時(shí),則自動(dòng)返回,這樣就能避免無(wú)限循環(huán)的情況。

5. 異步處理

有時(shí)候,我們接口性能優(yōu)化,需要重新梳理一下業(yè)務(wù)邏輯,看看是否有設(shè)計(jì)上不太合理的地方。

比如有個(gè)用戶(hù)請(qǐng)求接口中,需要做業(yè)務(wù)操作,發(fā)站內(nèi)通知,和記錄操作日志。為了實(shí)現(xiàn)起來(lái)比較方便,通常我們會(huì)將這些邏輯放在接口中同步執(zhí)行,勢(shì)必會(huì)對(duì)接口性能造成一定的影響。

接口內(nèi)部流程圖如下:

7ee536ca-16c7-11ed-ba43-dac502259ad0.png

這個(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ù)影響不大,所以完全可以異步處理。

通常異步主要有兩種:多線程mq。

5.1 線程池

使用線程池改造之后,接口邏輯如下:

7ef4a0ce-16c7-11ed-ba43-dac502259ad0.png

發(fā)站內(nèi)通知和用戶(hù)操作日志功能,被提交到了兩個(gè)單獨(dú)的線程池中。

這樣接口中重點(diǎn)關(guān)注的是業(yè)務(wù)操作,把其他的邏輯交給線程異步執(zhí)行,這樣改造之后,讓接口性能瞬間提升了。

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

那么這個(gè)問(wèn)題該怎么辦呢?

5.2 mq

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

7f02a020-16c7-11ed-ba43-dac502259ad0.png

對(duì)于發(fā)站內(nèi)通知和用戶(hù)操作日志功能,在接口中并沒(méi)真正實(shí)現(xiàn),它只發(fā)送了mq消息到mq服務(wù)器。然后由mq消費(fèi)者消費(fèi)消息時(shí),才真正的執(zhí)行這兩個(gè)功能。

這樣改造之后,接口性能同樣提升了,因?yàn)榘l(fā)送mq消息速度是很快的,我們只需關(guān)注業(yè)務(wù)操作的代碼即可。

6. 避免大事務(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)題。

7f15e9be-16c7-11ed-ba43-dac502259ad0.png

從圖中能夠看出,大事務(wù)問(wèn)題可能會(huì)造成接口超時(shí),對(duì)接口的性能有直接的影響。

我們?cè)撊绾蝺?yōu)化大事務(wù)呢?

  1. 少用@Transactional注解
  2. 將查詢(xún)(select)方法放到事務(wù)外
  3. 事務(wù)中避免遠(yuǎn)程調(diào)用
  4. 事務(wù)中避免一次性處理太多數(shù)據(jù)
  5. 有些功能可以非事務(wù)執(zhí)行
  6. 有些功能可以異步處理

7. 鎖粒度

在某些業(yè)務(wù)場(chǎng)景中,為了防止多個(gè)線程并發(fā)修改某個(gè)共享數(shù)據(jù),造成數(shù)據(jù)異常。

為了解決并發(fā)場(chǎng)景下,多個(gè)線程同時(shí)修改數(shù)據(jù),造成數(shù)據(jù)不一致的情況。通常情況下,我們會(huì):加鎖。

但如果鎖加得不好,導(dǎo)致鎖的粒度太粗,也會(huì)非常影響接口性能。

7.1 synchronized

在java中提供了synchronized關(guān)鍵字給我們的代碼加鎖。

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

先看看如何在方法上加鎖:

publicsynchronizeddoSave(StringfileUrl){
mkdir();
uploadFile(fileUrl);
sendMessage(fileUrl);
}

這里加鎖的目的是為了防止并發(fā)的情況下,創(chuàng)建了相同的目錄,第二次會(huì)創(chuàng)建失敗,影響業(yè)務(wù)功能。

但這種直接在方法上加鎖,鎖的粒度有點(diǎn)粗。因?yàn)閐oSave方法中的上傳文件和發(fā)消息方法,是不需要加鎖的。只有創(chuàng)建目錄方法,才需要加鎖。

我們都知道文件上傳操作是非常耗時(shí)的,如果將整個(gè)方法加鎖,那么需要等到整個(gè)方法執(zhí)行完之后才能釋放鎖。顯然,這會(huì)導(dǎo)致該方法的性能很差,變得得不償失。

這時(shí),我們可以改成在代碼塊上加鎖了,具體代碼如下:

publicvoiddoSave(Stringpath,StringfileUrl){
synchronized(this){
if(!exists(path)){
mkdir(path);
}
}
uploadFile(fileUrl);
sendMessage(fileUrl);
}

這樣改造之后,鎖的粒度一下子變小了,只有并發(fā)創(chuàng)建目錄功能才加了鎖。而創(chuàng)建目錄是一個(gè)非??斓牟僮?,即使加鎖對(duì)接口的性能影響也不大。

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

當(dāng)然,這種做在單機(jī)版的服務(wù)中,是沒(méi)有問(wèn)題的。但現(xià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)部署避免了因?yàn)槟硞€(gè)節(jié)點(diǎn)掛了,導(dǎo)致服務(wù)不可用的情況。同時(shí)也能分?jǐn)傉麄€(gè)系統(tǒng)的流量,避免系統(tǒng)壓力過(guò)大。

同時(shí)它也帶來(lái)了新的問(wèn)題:synchronized只能保證一個(gè)節(jié)點(diǎn)加鎖是有效的,但如果有多個(gè)節(jié)點(diǎn)如何加鎖呢?

答:這就需要使用:分布式鎖了。目前主流的分布式鎖包括:redis分布式鎖、zookeeper分布式鎖 和 數(shù)據(jù)庫(kù)分布式鎖。

由于zookeeper分布式鎖的性能不太好,真實(shí)業(yè)務(wù)場(chǎng)景用的不多,這里先不講。

下面聊一下redis分布式鎖。

7.2 redis分布式鎖

在分布式系統(tǒng)中,由于redis分布式鎖相對(duì)于更簡(jiǎn)單和高效,成為了分布式鎖的首先,被我們用到了很多實(shí)際業(yè)務(wù)場(chǎng)景當(dāng)中。

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

publicvoiddoSave(Stringpath,StringfileUrl){
try{
Stringresult=jedis.set(lockKey,requestId,"NX","PX",expireTime);
if("OK".equals(result)){
if(!exists(path)){
mkdir(path);
uploadFile(fileUrl);
sendMessage(fileUrl);
}
returntrue;
}
}finally{
unlock(lockKey,requestId);
}
returnfalse;
}

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

其實(shí)只有創(chuàng)建目錄的時(shí)候,才需要加分布式鎖,其余代碼根本不用加鎖。

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

publicvoiddoSave(Stringpath,StringfileUrl){
if(this.tryLock()){
mkdir(path);
}
uploadFile(fileUrl);
sendMessage(fileUrl);
}

privatebooleantryLock(){
try{
Stringresult=jedis.set(lockKey,requestId,"NX","PX",expireTime);
if("OK".equals(result)){
returntrue;
}
}finally{
unlock(lockKey,requestId);
}
returnfalse;
}

上面代碼將加鎖的范圍縮小了,只有創(chuàng)建目錄時(shí)才加了鎖。這樣看似簡(jiǎn)單的優(yōu)化之后,接口性能能提升很多。說(shuō)不定,會(huì)有意外的驚喜喔。哈哈哈。

redis分布式鎖雖說(shuō)好用,但它在使用時(shí),有很多注意的細(xì)節(jié),隱藏了很多坑,如果稍不注意很容易踩中。

7.3 數(shù)據(jù)庫(kù)分布式鎖

mysql數(shù)據(jù)庫(kù)中主要有三種鎖:

  • 表鎖:加鎖快,不會(huì)出現(xiàn)死鎖。但鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。
  • 行鎖:加鎖慢,會(huì)出現(xiàn)死鎖。但鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
  • 間隙鎖:開(kāi)銷(xiāo)和加鎖時(shí)間界于表鎖和行鎖之間。它會(huì)出現(xiàn)死鎖,鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。

并發(fā)度越高,意味著接口性能越好。

所以數(shù)據(jù)庫(kù)鎖的優(yōu)化方向是:

優(yōu)先使用行鎖,其次使用間隙鎖,再其次使用表鎖。

趕緊看看,你用對(duì)了沒(méi)?

8.分頁(yè)處理

有時(shí)候我會(huì)調(diào)用某個(gè)接口批量查詢(xún)數(shù)據(jù),比如:通過(guò)用戶(hù)id批量查詢(xún)出用戶(hù)信息,然后給這些用戶(hù)送積分。

但如果你一次性查詢(xún)的用戶(hù)數(shù)量太多了,比如一次查詢(xún)2000個(gè)用戶(hù)的數(shù)據(jù)。參數(shù)中傳入了2000個(gè)用戶(hù)的id,遠(yuǎn)程調(diào)用接口,會(huì)發(fā)現(xiàn)該用戶(hù)查詢(xún)接口經(jīng)常超時(shí)。

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

Listusers=remoteCallUser(ids);

眾所周知,調(diào)用接口從數(shù)據(jù)庫(kù)獲取數(shù)據(jù),是需要經(jīng)過(guò)網(wǎng)絡(luò)傳輸?shù)?。如果?shù)據(jù)量太大,無(wú)論是獲取數(shù)據(jù)的速度,還是網(wǎng)絡(luò)傳輸受限于帶寬,都會(huì)導(dǎo)致耗時(shí)時(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>allIds=Lists.partition(ids,200);

for(ListbatchIds:allIds){
Listusers=remoteCallUser(batchIds);
}

代碼中我用的googleguava工具中的Lists.partition方法,用它來(lái)做分頁(yè)簡(jiǎn)直太好用了,不然要巴拉巴拉寫(xiě)一大堆分頁(yè)的代碼。

8.2 異步調(diào)用

如果是在某個(gè)接口中需要獲取2000個(gè)用戶(hù)的信息,它考慮的就需要更多一些。

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

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

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

代碼如下:

List>allIds=Lists.partition(ids,200);

finalListresult=Lists.newArrayList();
allIds.stream().forEach((batchIds)->{
CompletableFuture.supplyAsync(()->{
result.addAll(remoteCallUser(batchIds));
returnBoolean.TRUE;
},executor);
})

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

9.加緩存

解決接口性能問(wèn)題,加緩存是一個(gè)非常高效的方法。

但不能為了緩存而緩存,還是要看具體的業(yè)務(wù)場(chǎng)景。畢竟加了緩存,會(huì)導(dǎo)致接口的復(fù)雜度增加,它會(huì)帶來(lái)數(shù)據(jù)不一致問(wèn)題。

在有些并發(fā)量比較低的場(chǎng)景中,比如用戶(hù)下單,可以不用加緩存。

還有些場(chǎng)景,比如在商城首頁(yè)顯示商品分類(lèi)的地方,假設(shè)這里的分類(lèi)是調(diào)用接口獲取到的數(shù)據(jù),但頁(yè)面暫時(shí)沒(méi)有做靜態(tài)化。

如果查詢(xún)分類(lèi)樹(shù)的接口沒(méi)有使用緩存,而直接從數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù),性能會(huì)非常差。

那么如何使用緩存呢?

9.1 redis緩存

通常情況下,我們使用最多的緩存可能是:redismemcached。

但對(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),這個(gè)三級(jí)分類(lèi),又是某個(gè)二級(jí)分類(lèi)的子分類(lèi),而這個(gè)二級(jí)分類(lèi),又是某個(gè)一級(jí)分類(lèi)的子分類(lèi)。

這種存儲(chǔ)結(jié)構(gòu)決定了,想一次性查出這個(gè)分類(lèi)樹(shù),并非是一件非常容易的事情。這就需要使用程序遞歸查詢(xún)了,如果分類(lèi)多的話,這個(gè)遞歸是比較耗時(shí)的。

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

這時(shí)我們可以使用緩存,大部分情況,接口都直接從緩存中獲取數(shù)據(jù)。操作redis可以使用成熟的框架,比如:jedis和redisson等。

用jedis偽代碼如下:

Stringjson=jedis.get(key);
if(StringUtils.isNotEmpty(json)){
CategoryTreecategoryTree=JsonUtil.toObject(json);
returncategoryTree;
}
returnqueryCategoryTreeFromDb();

先從redis中根據(jù)某個(gè)key查詢(xún)是否有菜單數(shù)據(jù),如果有則轉(zhuǎn)換成對(duì)象,直接返回。如果redis中沒(méi)有查到菜單數(shù)據(jù),則再?gòu)臄?shù)據(jù)庫(kù)中查詢(xún)菜單數(shù)據(jù),有則返回。

此外,我們還需要有個(gè)job每隔一段時(shí)間,從數(shù)據(jù)庫(kù)中查詢(xún)菜單數(shù)據(jù),更新到redis當(dāng)中,這樣以后每次都能直接從redis中獲取菜單的數(shù)據(jù),而無(wú)需訪問(wèn)數(shù)據(jù)庫(kù)了。

7f226a2c-16c7-11ed-ba43-dac502259ad0.png

這樣改造之后,能快速的提升性能。

但這樣做性能提升不是最佳的,還有其他的方案,我們一起看看下面的內(nèi)容。

9.2 二級(jí)緩存

上面的方案是基于redis緩存的,雖說(shuō)redis訪問(wèn)速度很快。但畢竟是一個(gè)遠(yuǎn)程調(diào)用,而且菜單樹(shù)的數(shù)據(jù)很多,在網(wǎng)絡(luò)傳輸?shù)倪^(guò)程中,是有些耗時(shí)的。

有沒(méi)有辦法,不經(jīng)過(guò)請(qǐng)求遠(yuǎn)程,就能直接獲取到數(shù)據(jù)呢?

答:使用二級(jí)緩存,即基于內(nèi)存的緩存。

除了自己手寫(xiě)的內(nèi)存緩存之后,目前使用比較多的內(nèi)存緩存框架有:guava、Ehcache、caffine等。

我們?cè)谶@里以caffeine為例,它是spring官方推薦的。

第一步,引入caffeine的相關(guān)jar包


org.springframework.boot
spring-boot-starter-cache


com.github.ben-manes.caffeine
caffeine
2.6.0

第二步,配置CacheManager,開(kāi)啟EnableCaching

@Configuration
@EnableCaching
publicclassCacheConfig{
@Bean
publicCacheManagercacheManager(){
CaffeineCacheManagercacheManager=newCaffeineCacheManager();
//Caffeine配置
Caffeinecaffeine=Caffeine.newBuilder()
//最后一次寫(xiě)入后經(jīng)過(guò)固定時(shí)間過(guò)期
.expireAfterWrite(10,TimeUnit.SECONDS)
//緩存的最大條數(shù)
.maximumSize(1000);
cacheManager.setCaffeine(caffeine);
returncacheManager;
}
}

第三步,使用Cacheable注解獲取數(shù)據(jù)

@Service
publicclassCategoryService{

@Cacheable(value="category",key="#categoryKey")
publicCategoryModelgetCategory(StringcategoryKey){
Stringjson=jedis.get(categoryKey);
if(StringUtils.isNotEmpty(json)){
CategoryTreecategoryTree=JsonUtil.toObject(json);
returncategoryTree;
}
returnqueryCategoryTreeFromDb();
}
}

調(diào)用categoryService.getCategory()方法時(shí),先從caffine緩存中獲取數(shù)據(jù),如果能夠獲取到數(shù)據(jù),則直接返回該數(shù)據(jù),不進(jìn)入方法體。

如果不能獲取到數(shù)據(jù),則再?gòu)膔edis中查一次數(shù)據(jù)。如果查詢(xún)到了,則返回?cái)?shù)據(jù),并且放入caffine中。

如果還是沒(méi)有查到數(shù)據(jù),則直接從數(shù)據(jù)庫(kù)中獲取到數(shù)據(jù),然后放到caffine緩存中。

具體流程圖如下:

7f3fa876-16c7-11ed-ba43-dac502259ad0.png

該方案的性能更好,但有個(gè)缺點(diǎn)就是,如果數(shù)據(jù)更新了,不能及時(shí)刷新緩存。此外,如果有多臺(tái)服務(wù)器節(jié)點(diǎn),可能存在各個(gè)節(jié)點(diǎn)上數(shù)據(jù)不一樣的情況。

由此可見(jiàn),二級(jí)緩存給我們帶來(lái)性能提升的同時(shí),也帶來(lái)了數(shù)據(jù)不一致的問(wèn)題。使用二級(jí)緩存一定要結(jié)合實(shí)際的業(yè)務(wù)場(chǎng)景,并非所有的業(yè)務(wù)場(chǎng)景都適用。

但上面我列舉的分類(lèi)場(chǎng)景,是適合使用二級(jí)緩存的。因?yàn)樗鼘儆谟脩?hù)不敏感數(shù)據(jù),即使出現(xiàn)了稍微有點(diǎn)數(shù)據(jù)不一致也沒(méi)有關(guān)系,用戶(hù)有可能都沒(méi)有察覺(jué)出來(lái)。

10. 分庫(kù)分表

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

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

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

這時(shí)該怎么辦呢?

答:需要做分庫(kù)分表。

如下圖所示:

7f60d55a-16c7-11ed-ba43-dac502259ad0.jpg

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

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

路由的算法挺多的:

  • 根據(jù)id取模,比如:id=7,有4張表,則7%4=3,模為3,路由到用戶(hù)表3。
  • 給id指定一個(gè)區(qū)間范圍,比如:id的值是0-10萬(wàn),則數(shù)據(jù)存在用戶(hù)表0,id的值是10-20萬(wàn),則數(shù)據(jù)存在用戶(hù)表1。
  • 一致性hash算法

分庫(kù)分表主要有兩個(gè)方向:垂直水平。

說(shuō)實(shí)話垂直方向(即業(yè)務(wù)方向)更簡(jiǎn)單。

在水平方向(即數(shù)據(jù)方向)上,分庫(kù)和分表的作用,其實(shí)是有區(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)景中,用戶(hù)并發(fā)量很大,但是需要保存的數(shù)據(jù)量很少,這時(shí)可以只分庫(kù),不分表。

如果在有些業(yè)務(wù)場(chǎng)景中,用戶(hù)并發(fā)量不大,但是需要保存的數(shù)量很多,這時(shí)可以只分表,不分庫(kù)。

如果在有些業(yè)務(wù)場(chǎng)景中,用戶(hù)并發(fā)量大,并且需要保存的數(shù)量也很多時(shí),可以分庫(kù)分表。

11. 輔助功能

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

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

通常情況下,為了定位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)罩敬娣诺穆窂?/li>
  • long_query_time 超過(guò)多少秒才會(huì)記錄日志

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

setglobalslow_query_log='ON';
setglobalslow_query_log_file='/usr/local/mysql/data/slow.log';
setgloballong_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)圖如下:

7f7f64ca-16c7-11ed-ba43-dac502259ad0.png

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

  • 接口響應(yīng)時(shí)間
  • 調(diào)用第三方服務(wù)耗時(shí)
  • 慢查詢(xún)sql耗時(shí)
  • cpu使用情況
  • 內(nèi)存使用情況
  • 磁盤(pán)使用情況
  • 數(shù)據(jù)庫(kù)使用情況

等等。。。

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

7f91d236-16c7-11ed-ba43-dac502259ad0.png

可以看到mysql當(dāng)前qps,活躍線程數(shù),連接數(shù),緩存池的大小等信息。

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

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

截圖中只是它一小部分功能,如果你想了解更多功能,可以訪問(wèn)Prometheus的官網(wǎng):https://prometheus.io/

11.3 鏈路跟蹤

有時(shí)候某個(gè)接口涉及的邏輯很多,比如:查數(shù)據(jù)庫(kù)、查redis、遠(yuǎn)程調(diào)用接口,發(fā)mq消息,執(zhí)行業(yè)務(wù)代碼等等。

該接口一次請(qǐng)求的鏈路很長(zhǎng),如果逐一排查,需要花費(fèi)大量的時(shí)間,這時(shí)候,我們已經(jīng)沒(méi)法用傳統(tǒng)的辦法定位問(wèn)題了。

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

用分布式鏈路跟蹤系統(tǒng):skywalking。

架構(gòu)圖如下:

7fa488fe-16c7-11ed-ba43-dac502259ad0.png

通過(guò)skywalking定位性能問(wèn)題:

7fb1d93c-16c7-11ed-ba43-dac502259ad0.png

在skywalking中可以通過(guò)traceId(全局唯一的id),串聯(lián)一個(gè)接口請(qǐng)求的完整鏈路??梢钥吹秸麄€(gè)接口的耗時(shí),調(diào)用的遠(yuǎn)程服務(wù)的耗時(shí),訪問(wèn)數(shù)據(jù)庫(kù)或者redis的耗時(shí)等等,功能非常強(qiáng)大。

之前沒(méi)有這個(gè)功能的時(shí)候,為了定位線上接口性能問(wèn)題,我們還需要在代碼中加日志,手動(dòng)打印出鏈路中各個(gè)環(huán)節(jié)的耗時(shí)情況,然后再逐一排查。

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


審核編輯 :李倩



聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    9532

    瀏覽量

    157107
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7339

    瀏覽量

    94822
  • 索引
    +關(guān)注

    關(guān)注

    0

    文章

    60

    瀏覽量

    10817

原文標(biāo)題:接口性能優(yōu)化的 11 個(gè)技巧

文章出處:【微信號(hào):良許Linux,微信公眾號(hào):良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    在并聯(lián)使用MOS存在一些問(wèn)題,要怎樣做才能避免這些問(wèn)題?

    在并聯(lián)使用MOS存在一些問(wèn)題,那我們要怎樣做才能避免這些問(wèn)題? 首先,器件的致性定要好。 在功率MOSFET多管并聯(lián)時(shí),器件內(nèi)部參數(shù)的微小差異就會(huì)引起并聯(lián)各支路電流的不平衡而導(dǎo)致單管過(guò)流損壞。 其次是功率。如果功率高于25%
    發(fā)表于 12-10 08:19

    關(guān)于六類(lèi)網(wǎng)線一些問(wèn)題的解答

    今天我們就圍繞網(wǎng)友一些常見(jiàn)的關(guān)于六類(lèi)網(wǎng)線的問(wèn)題進(jìn)行下匯總式解答: 問(wèn) 六類(lèi)網(wǎng)線可以當(dāng)電源用嗎? 答 六類(lèi)網(wǎng)線并不是設(shè)計(jì)用于傳輸電力的電纜,因此般不建議將其用于電源傳輸。 盡管六類(lèi)網(wǎng)線的線芯可以
    的頭像 發(fā)表于 12-09 11:13 ?597次閱讀

    請(qǐng)問(wèn)Keil的優(yōu)化等級(jí)到底該如何選擇?

    試性的前提下,進(jìn)行適度的優(yōu)化以提高代碼大小和執(zhí)行速度。 行為: 編譯器會(huì)進(jìn)行一些基本的、不太影響調(diào)試的優(yōu)化,如未使用代碼/數(shù)據(jù)的消除、簡(jiǎn)單的常量傳播和合并、簡(jiǎn)單的跳轉(zhuǎn)優(yōu)化等。變量通常仍
    發(fā)表于 11-20 07:51

    如果將蜂鳥(niǎo)的risc-v移植到其他的fpga中想實(shí)現(xiàn)一些外設(shè)功能有什么辦法?可以不用操作系統(tǒng)直接添加verilog代碼嗎?

    請(qǐng)問(wèn)如果將蜂鳥(niǎo)的risc-v移植到其他的fpga中想實(shí)現(xiàn)一些外設(shè)功能有什么辦法?可以不用操作系統(tǒng)直接添加verilog代碼嗎?
    發(fā)表于 11-10 06:35

    蜂鳥(niǎo)E203的浮點(diǎn)指令集F的一些實(shí)現(xiàn)細(xì)節(jié)

    周期。 總結(jié) 本文介紹的內(nèi)容是為了完成基礎(chǔ)功能:對(duì)蜂鳥(niǎo)E203 RISC-V內(nèi)核的微架構(gòu)實(shí)現(xiàn)進(jìn)行優(yōu)化,在添加F拓展的過(guò)程中的一些記錄。
    發(fā)表于 10-24 08:57

    Vivado浮點(diǎn)數(shù)IP核的一些設(shè)置注意點(diǎn)

    : 總結(jié) 本文介紹的內(nèi)容是為了完成基礎(chǔ)功能:對(duì)蜂鳥(niǎo)E203 RISC-V內(nèi)核的微架構(gòu)實(shí)現(xiàn)進(jìn)行優(yōu)化,在添加F拓展的過(guò)程中的一些記錄。
    發(fā)表于 10-24 06:25

    如何保障電網(wǎng)結(jié)構(gòu)優(yōu)化措施的有效實(shí)施?

    電網(wǎng)結(jié)構(gòu)優(yōu)化措施的有效實(shí)施,需突破 “規(guī)劃脫節(jié)、技術(shù)壁壘、協(xié)同不足、資金短缺、風(fēng)險(xiǎn)失控” 等關(guān)鍵瓶頸,從 頂層設(shè)計(jì)、技術(shù)支撐、資金保障、協(xié)同機(jī)制、風(fēng)險(xiǎn)管控、監(jiān)督評(píng)估、市場(chǎng)激勵(lì)、人才建設(shè) 八大維度構(gòu)建
    的頭像 發(fā)表于 10-14 17:57 ?1018次閱讀

    高頻無(wú)線鏈路中的天線接口優(yōu)化方法

    在高速無(wú)線鏈路中,天線接口性能直接決定發(fā)射功率的有效輻射和接收靈敏度的上限。若接口參數(shù)未經(jīng)過(guò)系統(tǒng)級(jí)優(yōu)化,即使射頻前端指標(biāo)出色,整機(jī)鏈路預(yù)算
    的頭像 發(fā)表于 08-19 14:38 ?1915次閱讀

    鴻蒙5開(kāi)發(fā)寶藏案例分享---長(zhǎng)列表性能優(yōu)化解析

    鴻蒙長(zhǎng)列表性能優(yōu)化大揭秘!告別卡頓,實(shí)戰(zhàn)代碼解析來(lái)了! 大家好呀~今天在翻鴻蒙開(kāi)發(fā)者文檔時(shí),發(fā)現(xiàn)了個(gè) 性能優(yōu)化寶藏案例 !官方居然悄悄放出了長(zhǎng)列表卡頓的完整解決方案,實(shí)測(cè)效果炸裂!我連
    發(fā)表于 06-12 17:40

    鴻蒙5開(kāi)發(fā)寶藏案例分享---性能優(yōu)化案例解析

    鴻蒙性能優(yōu)化寶藏指南:實(shí)戰(zhàn)工具與代碼案例解析 大家好呀!今天在翻鴻蒙開(kāi)發(fā)者文檔時(shí),意外挖到個(gè) 性能優(yōu)化寶藏庫(kù) ——原來(lái)官方早就提供了超多實(shí)
    發(fā)表于 06-12 16:36

    HarmonyOS優(yōu)化應(yīng)用內(nèi)存占用問(wèn)題性能優(yōu)化

    問(wèn)題。 張全屏的圖片,不同分辨率的內(nèi)存占用大小如下: 由上圖可以看出,對(duì)于一些頁(yè)面多、圖片多、效果多的資源密集型應(yīng)用,內(nèi)存很容易達(dá)到較高水平。當(dāng)應(yīng)用的內(nèi)存占用超過(guò)系統(tǒng)設(shè)定的閾值(如4G,其中4G只是示例
    發(fā)表于 05-24 17:20

    HarmonyOS優(yōu)化應(yīng)用內(nèi)存占用問(wèn)題性能優(yōu)化

    應(yīng)用開(kāi)發(fā)過(guò)程中注重內(nèi)存管理,積極采取措施來(lái)減少內(nèi)存占用,以優(yōu)化應(yīng)用程序的性能和用戶(hù)體驗(yàn)。 HarmonyOS提供了一些內(nèi)存管理的工具和接口,幫助開(kāi)發(fā)者
    發(fā)表于 05-21 11:27

    如何利用5位從FIFO接口優(yōu)化FX3性能并避免數(shù)據(jù)丟失?

    讀取 將端點(diǎn)切換到插座 6(線程 2),在那里傳輸一些數(shù)據(jù) 端點(diǎn)切換回插座 2 。..。,然后再執(zhí)行次突發(fā)讀取。 端點(diǎn)切換后的 Socket 2 讀取會(huì)導(dǎo)致不良數(shù)據(jù)(樣本丟失或重復(fù))。 我想我們已經(jīng)把
    發(fā)表于 05-20 06:13

    Debian和Ubuntu哪個(gè)好一些

    兼容性對(duì)比Debian和Ubuntu哪個(gè)好一些,并為您揭示如何通過(guò)RAKsmart服務(wù)器釋放Linux系統(tǒng)的最大潛能。
    的頭像 發(fā)表于 05-07 10:58 ?1168次閱讀

    從RF到HDMI:傳統(tǒng)接口的現(xiàn)代優(yōu)化

    射頻(RF)、復(fù)合視頻(RCA)、S-Video和視頻色差是幾種傳統(tǒng)的視頻接口。盡管這些接口一些舊設(shè)備或特定應(yīng)用場(chǎng)景中仍然被使用,但隨著數(shù)字技術(shù)的發(fā)展,它們的使用頻率已經(jīng)顯著下降?,F(xiàn)代顯示設(shè)備
    的頭像 發(fā)表于 04-15 11:50 ?1623次閱讀
    從RF到HDMI:傳統(tǒng)<b class='flag-5'>接口</b>的現(xiàn)代<b class='flag-5'>優(yōu)化</b>