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

分享5個(gè)Java接口性能提升的通用技巧

 更新時(shí)間:2023年01月03日 09:16:27   作者:JAVA旭陽(yáng)  
作為后端開(kāi)發(fā)人員,我們總是在編寫(xiě)各種API。這些API在服務(wù)初期可能表現(xiàn)不錯(cuò),但隨著用戶數(shù)量的增長(zhǎng),一開(kāi)始響應(yīng)很快的API越來(lái)越慢,這時(shí)候你就需要考慮如何優(yōu)化你的API性能了。在這篇文章中,我總結(jié)了一些行之有效的API性能優(yōu)化技巧,希望能給有需要的朋友一些幫助

前言

作為后端開(kāi)發(fā)人員,我們總是在編寫(xiě)各種API,無(wú)論是為前端web提供數(shù)據(jù)支持的HTTP REST API ,還是提供內(nèi)部使用的RPC API。這些API在服務(wù)初期可能表現(xiàn)不錯(cuò),但隨著用戶數(shù)量的增長(zhǎng),一開(kāi)始響應(yīng)很快的API越來(lái)越慢,直到用戶抱怨:“你的系統(tǒng)太糟糕了。” 我只是瀏覽網(wǎng)頁(yè)。為什么這么慢?”。這時(shí)候你就需要考慮如何優(yōu)化你的API性能了。

要想提高你的API的性能,我們首先要知道哪些問(wèn)題會(huì)導(dǎo)致接口響應(yīng)慢。API設(shè)計(jì)需要考慮很多方面。開(kāi)發(fā)語(yǔ)言層面只占一小部分。哪個(gè)部分設(shè)計(jì)不好就會(huì)成為性能瓶頸。影響API性能的因素有很多,總結(jié)如下:

  • 數(shù)據(jù)庫(kù)慢查詢
  • 復(fù)雜的業(yè)務(wù)邏輯
  • 糟糕的代碼
  • 資源不足
  • ........

在這篇文章中,我總結(jié)了一些行之有效的API性能優(yōu)化技巧,希望能給有需要的朋友一些幫助。

1. 并發(fā)調(diào)用

假設(shè)我們現(xiàn)在有一個(gè)電子商務(wù)系統(tǒng)需要提交訂單。該功能需要調(diào)用庫(kù)存系統(tǒng)進(jìn)行庫(kù)存查扣,還需要獲取用戶地址信息。最后調(diào)用風(fēng)控系統(tǒng)判斷本次交易無(wú)風(fēng)險(xiǎn)。這個(gè)接口的設(shè)計(jì)大部分可能會(huì)把接口設(shè)計(jì)成一個(gè)順序執(zhí)行的接口。畢竟我們需要獲取到用戶地址信息,完成庫(kù)存扣減,才能進(jìn)行下一步。偽代碼如下:

public Boolean submitOrder(orderInfo orderInfo) {

	//check stock
	stockService.check();
	//invoke addressService
	addressService.getByUser();
	//risk control
	riskControlSerivce.check();
	
	return doSubmitOrder(orderInfo);
}

如果我們仔細(xì)分析這個(gè)函數(shù),就會(huì)發(fā)現(xiàn)幾個(gè)方法調(diào)用之間并沒(méi)有很強(qiáng)的依賴關(guān)系。而且這三個(gè)系統(tǒng)的調(diào)用都比較耗時(shí)。假設(shè)這些系統(tǒng)的調(diào)用耗時(shí)分布如下

  • stockService.check()需要 150 毫秒。
  • addressService.getByUser()需要 200 毫秒。
  • riskControlSerivce.check()需要 300 毫秒。

如果順序調(diào)用此API,則整個(gè)API的執(zhí)行時(shí)間為650ms(150ms+200ms+300ms)。如果能轉(zhuǎn)化為并行調(diào)用,API的執(zhí)行時(shí)間為300ms,性能直接提升50%。使用并行調(diào)用,大致代碼如下:

public Boolean submitOrder(orderInfo orderInfo) {

	//check stock
	CompletableFuture<Void> stockFuture = CompletableFuture.supplyAsync(() -> {
        return stockService.check(); 
    }, executor);
	//invoke addressService
	CompletableFuture<Address> addressFuture = CompletableFuture.supplyAsync(() -> {
        return addressService.getByUser();
    }, executor);
	//risk control
	CompletableFuture<Void> riskFuture = CompletableFuture.supplyAsync(() -> {
        return 	riskControlSerivce.check();
    }, executor);

	CompletableFuture.allOf(stockFuture, addressFuture, riskFuture);
	stockFuture.get();
	addressFuture.get();
	riskFuture.get();
	return doSubmitOrder(orderInfo);
}

2. 避免大事務(wù)

所謂大事務(wù),就是歷經(jīng)時(shí)間很長(zhǎng)的事務(wù)。如果使用Spring @Transaction管理事務(wù),需要注意是否不小心啟動(dòng)了大事務(wù)。因?yàn)镾pring的事務(wù)管理原理是將多個(gè)事務(wù)合并到一個(gè)執(zhí)行中,如果一個(gè)API里面有多個(gè)數(shù)據(jù)庫(kù)讀寫(xiě),而且這個(gè)API的并發(fā)訪問(wèn)量比較高,很可能大事務(wù)會(huì)導(dǎo)致太大大量數(shù)據(jù)鎖在數(shù)據(jù)庫(kù)中,造成大量阻塞,數(shù)據(jù)庫(kù)連接池連接耗盡。

@Transactional(rollbackFor=Exception.class)
public Boolean submitOrder(orderInfo orderInfo) {

    //check stock
    stockService.check();
    //invoke addressService
    addressService.getByUser();
    //risk control
    riskControlRpcApi.check();
    
    orderService.insertOrder(orderInfo);
    orderDetailService.insertOrderDetail(orderInfo);
    
    return true;
}

相信在很多人寫(xiě)的業(yè)務(wù)中都出現(xiàn)過(guò)這種代碼,遠(yuǎn)程調(diào)用操作,一個(gè)非DB操作,混合在持久層代碼中,這種代碼絕對(duì)是一個(gè)大事務(wù)。它不僅需要查詢用戶地址和扣除庫(kù)存,還需要插入訂單數(shù)據(jù)和訂單明細(xì)。這一系列操作需要合并到同一個(gè)事務(wù)中。如果RPC響應(yīng)慢,當(dāng)前線程會(huì)一直占用數(shù)據(jù)庫(kù)連接,導(dǎo)致并發(fā)場(chǎng)景下數(shù)據(jù)庫(kù)連接耗盡。不僅如此,如果事務(wù)需要回滾,你的API響應(yīng)也會(huì)因?yàn)榛貪L慢而變慢。

這個(gè)時(shí)候就需要考慮減小事務(wù)了,我們可以把非事務(wù)操作和事務(wù)操作分開(kāi),像這樣:

@Autowired
private OrderDaoService orderDaoService;

public Boolean submitOrder(OrderInfo orderInfo) {

    //invoke addressService
    addressService.getByUser();
    //risk control
    riskControlRpcApi.check();
    return orderDaoService.doSubmitOrder(orderInfo);
}

@Service
public class OrderDaoService{

    @Transactional(rollbackFor=Exception.class)
    public Boolean doSubmitOrder(OrderInfo orderInfo) {
        //check stock
        stockService.check();
        orderService.insertOrder(orderInfo);
        orderDetailService.insertOrderDetail(orderInfo);
        return true;
    }
}

或者,您可以使用 spring 的編程事務(wù)TransactionTemplate。

@Autowired
private TransactionTemplate transactionTemplate;

public void submitOrder(OrderInfo orderInfo) {

	//invoke addressService
	addressService.getByUser();
	//risk control
	riskControlRpcApi.check();
	return transactionTemplate.execute(()->{
		return doSubmitOrder(orderInfo);
	})
}

public Boolean doSubmitOrder(OrderInfo orderInfo) {
		//check stock
		stockService.check();
		orderService.insertOrder(orderInfo);
		orderDetailService.insertOrderDetail(orderInfo);
		return true;
	}

3. 添加合適的索引

我們的服務(wù)在運(yùn)行初期,系統(tǒng)需要存儲(chǔ)的數(shù)據(jù)量很小,可能是數(shù)據(jù)庫(kù)沒(méi)有加索引來(lái)快速存儲(chǔ)和訪問(wèn)數(shù)據(jù)。但是隨著業(yè)務(wù)的增長(zhǎng),單表數(shù)據(jù)量不斷增加,數(shù)據(jù)庫(kù)的查詢性能變差。這時(shí)候我們應(yīng)該給你的數(shù)據(jù)庫(kù)表添加適當(dāng)?shù)乃饕???梢酝ㄟ^(guò)命令查看表的索引(這里以MySQL為例)。

show index from `your_table_name`;

ALTER TABLE通過(guò)命令添加索引。

ALTER TABLE `your_table_name` ADD INDEX index_name(username);

有時(shí)候,即使加了一些索引,數(shù)據(jù)查詢還是很慢。這時(shí)候你可以使用explain命令查看執(zhí)行計(jì)劃來(lái)判斷你的SQL語(yǔ)句是否命中了索引。例如:

explain select * from product_info where type=0;

你會(huì)得到一個(gè)分析結(jié)果:

一般來(lái)說(shuō),索引失效有幾種情況:

  • 不滿足最左前綴原則。例如,您創(chuàng)建一個(gè)組合索引idx(a,b,c)。但是你的SQL語(yǔ)句是這樣寫(xiě)的select * from tb1 where b='xxx' and c='xxxx';
  • 索引列使用算術(shù)運(yùn)算。select * from tb1 where a%10=0;
  • 索引列使用函數(shù)。select * from tb1 where date_format(a,'%m-%d-%Y')='2023-01-02';
  • like使用關(guān)鍵字的模糊查詢。select * from tb1 where a like '%aaa';
  • 使用not innot exist關(guān)鍵字。
  • 等等

4. 返回更少的數(shù)據(jù)

如果我們查詢大量符合條件的數(shù)據(jù),我們不需要返回所有數(shù)據(jù)。我們可以通過(guò)分頁(yè)的方式增量提供數(shù)據(jù)。這樣,我們需要通過(guò)網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)更少,編碼和解碼數(shù)據(jù)的時(shí)間更短,API 響應(yīng)更快。

但是,傳統(tǒng)的limit offset方法用于 paging( select * from product limit 10000,20)。當(dāng)頁(yè)面數(shù)量很大時(shí),查詢會(huì)越來(lái)越慢。這是因?yàn)槭褂玫脑?code>limit offset是找出10000條數(shù)據(jù),然后丟棄前面的9980條數(shù)據(jù)。我們可以使用延遲關(guān)聯(lián)來(lái)優(yōu)化此 SQL。

select * from product where id in (select id from product limit 10000,20);

5. 使用緩存

緩存是一種以空間換時(shí)間的解決方案。一些用戶經(jīng)常訪問(wèn)的數(shù)據(jù)直接緩存在內(nèi)存中。因?yàn)閮?nèi)存的讀取速度遠(yuǎn)快于磁盤(pán)IO,所以我們也可以通過(guò)適當(dāng)?shù)木彺鎭?lái)提高API的性能。簡(jiǎn)單的,我們可以使用Java的HashMap、ConcurrentHashMap,或者caffeine等本地緩存,或者Memcached、Redis等分布式緩存中間件。

到此這篇關(guān)于分享5個(gè)Java接口性能提升的通用技巧的文章就介紹到這了,更多相關(guān)Java接口性能提升技巧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)詳解

    如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)詳解

    這篇文章主要給大家介紹了關(guān)于如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java實(shí)現(xiàn)斗地主最簡(jiǎn)代碼實(shí)例

    Java實(shí)現(xiàn)斗地主最簡(jiǎn)代碼實(shí)例

    在本篇文章里小編給各位分享的是關(guān)于Java實(shí)現(xiàn)斗地主最簡(jiǎn)代碼實(shí)例,有興趣的朋友們可以參考下。
    2020-05-05
  • Spring?ComponentScan的掃描過(guò)程解析

    Spring?ComponentScan的掃描過(guò)程解析

    這篇文章主要介紹了spring?ComponentScan的掃描過(guò)程解析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • 使用jpa之動(dòng)態(tài)插入與修改(重寫(xiě)save)

    使用jpa之動(dòng)態(tài)插入與修改(重寫(xiě)save)

    這篇文章主要介紹了使用jpa之動(dòng)態(tài)插入與修改(重寫(xiě)save),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java經(jīng)典用法總結(jié)(二)

    Java經(jīng)典用法總結(jié)(二)

    這篇文章主要介紹了Java經(jīng)典用法總結(jié),在本文中,盡量收集一些java最常用的習(xí)慣用法,特別是很難猜到的用法,本文重點(diǎn)講解了java應(yīng)用和輸入輸出常用方法,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Java中properties文件中的中文亂碼問(wèn)題

    Java中properties文件中的中文亂碼問(wèn)題

    Properties為了方便用戶的配置,用于讀取Java的配置文件,不同的編程語(yǔ)言有自己所支持的配置文件,能讓用戶夠脫離程序本身去修改相關(guān)的變量設(shè)置,這篇文章主要介紹了Java中properties文件中的中文亂碼問(wèn)題,需要的朋友可以參考下
    2023-08-08
  • Spring boot隨機(jī)端口你都不會(huì)還怎么動(dòng)態(tài)擴(kuò)容

    Spring boot隨機(jī)端口你都不會(huì)還怎么動(dòng)態(tài)擴(kuò)容

    這篇文章主要介紹了Spring boot隨機(jī)端口你都不會(huì)還怎么動(dòng)態(tài)擴(kuò)容,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • springboot上傳文件,url直接訪問(wèn)資源問(wèn)題

    springboot上傳文件,url直接訪問(wèn)資源問(wèn)題

    這篇文章主要介紹了springboot上傳文件,url直接訪問(wèn)資源問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java算法中的歸并排序算法代碼實(shí)現(xiàn)

    Java算法中的歸并排序算法代碼實(shí)現(xiàn)

    這篇文章主要介紹了Java算法中的歸并排序算法代碼實(shí)現(xiàn),歸并排序使用的是分治思想(Divide and Conquer),分治,顧名思義,就是分而治之,是將一個(gè)大問(wèn)題分解成小的子問(wèn)題來(lái)解決,需要的朋友可以參考下
    2023-12-12
  • 為什么Java開(kāi)發(fā)需要配置環(huán)境變量

    為什么Java開(kāi)發(fā)需要配置環(huán)境變量

    這篇文章主要介紹了為什么Java開(kāi)發(fā)需要配置環(huán)境變量,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-08-08

最新評(píng)論