日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

我被 parallel 函數雷了

來源: 責編: 時間:2024-01-15 09:18:20 220觀看
導讀 1. 問題&分析性能優化是技術人的永恒話題,當我們遇到性能問題時,你的第一反應是什么?數據庫索引優化,緩存優化,算法優化?但,有時性能殺手往往就是性能優化引入的。1.1. 案例今天一大早,小艾剛到公司便收到一組系統報警,原來有

 1. 問題&分析

性能優化是技術人的永恒話題,當我們遇到性能問題時,你的第一反應是什么?0av28資訊網——每日最新資訊28at.com

數據庫索引優化,緩存優化,算法優化?0av28資訊網——每日最新資訊28at.com

但,有時性能殺手往往就是性能優化引入的。0av28資訊網——每日最新資訊28at.com

1.1. 案例

今天一大早,小艾剛到公司便收到一組系統報警,原來有一個接口報了一堆的慢情況。仔細排查,發現是前兩天為服務域提供的一個訂單的查詢接口,該接口剛上線不久,正處于放量階段,小艾立即驚出一身冷汗,不會是數據庫出現了 慢SQL?記得上線前通過 explain 指令對 sql 進行過分析,明確已經使用了數據庫索引。他趕緊打開阿里云控制臺,快速進入 慢查詢功能進行查看,但奇怪的是監控顯示沒有一條 慢查詢,真是太詭異了。0av28資訊網——每日最新資訊28at.com

還好不是數據庫慢查詢,不然可能存在將整個 MySQL 數據庫拖垮的可能,小艾的懸著的心也終于放了下來。0av28資訊網——每日最新資訊28at.com

可問題出在哪里呢?0av28資訊網——每日最新資訊28at.com

這個查詢接口非常簡單,示例代碼如下:0av28資訊網——每日最新資訊28at.com

@GetMapping("getOrdersByUsers")public RestResult<List<OrderVO>> allOrderByUsers(@RequestParam List<Long> users){    Stopwatch  stopwatch = Stopwatch.createStarted();    List<Order> orders = getByUserId(users);    List<OrderVO> orderVOS = orders.stream()            .map(order -> OrderVO.applyByParallel(order))            .collect(Collectors.toList());    log.info("get order by user cost {} ms", stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));    return RestResult.success(orderVOS);}

邏輯簡單到令人發指,只有兩步:0av28資訊網——每日最新資訊28at.com

  1. 根據傳入的 user id 從數據庫中查詢訂單
  2. 將查詢的 Order 轉換為 OrderVO 返回用戶

小艾,仔細觀察這個接口,發現一個現象:當入參較多時,接口的性能變的非常差。0av28資訊網——每日最新資訊28at.com

這個也比較好理解,系統使用的是 in 語句對數據進行查詢,示例:select * from order_info where user_id in (?),當入參數據量非常大時,sql 執行耗時變高。這可能是一個原因,但MySQL 慢請求中未記錄任何信息,說明 sql 的執行時間沒有超過 1 秒,所以,這個只是一個表因。0av28資訊網——每日最新資訊28at.com

為了更好的驗證猜想,小艾對日志進行完善,整體如下:0av28資訊網——每日最新資訊28at.com

@GetMapping("getOrdersByUsers")public RestResult<List<OrderVO>> allOrderByUsers(@RequestParam List<Long> users){    Stopwatch  stopwatch = Stopwatch.createStarted();    List<Order> orders = getByUserId(users);    log.info("get data from DB cost {} ms", stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));    stopwatch = Stopwatch.createStarted();    List<OrderVO> orderVOS = orders.stream()            .map(order -> OrderVO.applyByParallel(order))            .collect(Collectors.toList());    log.info("convert to OrderVO cost {} ms", stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));    return RestResult.success(orderVOS);}

選了幾個訂單較多的用戶進行測試,打印日志如下:0av28資訊網——每日最新資訊28at.com

圖片圖片0av28資訊網——每日最新資訊28at.com

好奇怪,數據庫操作耗時有限,但 Order 向 OrderVO 的轉換居然耗時這么多,真是太不可思議!0av28資訊網——每日最新資訊28at.com

1.2. 問題分析

很明顯是轉化這步出了問題,其核心代碼如下所示:0av28資訊網——每日最新資訊28at.com

// 使用 Stream 流進行類型轉化List<OrderVO> orderVOS = orders.stream()        .map(order -> OrderVO.applyByParallel(order))        .collect(Collectors.toList());// Order 到 OrderVO 的轉化邏輯public static OrderVO applyByParallel(Order order){    OrderVO orderVO = new OrderVO();    orderVO.setId(order.getId());    orderVO.setUserId(order.getUserId());    orderVO.setStatus(OrderStatus.parallelParseByCode(order.getOrderStatus()));    orderVO.setOrderType(OrderType.parallelParseByCode(order.getOrderType()));    orderVO.setProductType(ProductType.parallelParseByCode(order.getProductType()));    orderVO.setPromotionType(PromotionType.parallelParseByCode(order.getPromotionType()));    return orderVO;}// 將 Code 轉換為對應的枚舉public static OrderStatus parallelParseByCode(int code) {    return Stream.of(values())            .parallel()            .filter(status -> status.getCode() == code)            .findFirst()            .orElse(null);}

看完核心代碼,請思考幾分鐘,問題可能出現在哪里?0av28資訊網——每日最新資訊28at.com

  1. Stream 操作?Stream 比 for 循環性能超差些,但還不至于有這么大差異
  2. 反射、BeanCopy?核心代碼沒有使用這些 API,乖乖的進行 Coding

那問題究竟在哪?答案是  Stream 的 parallel() 函數。使用 parallel 函數最初的目標便是提升性能,為什么在這里卻成了性能殺手?在解答前,先快速了解下這個函數:0av28資訊網——每日最新資訊28at.com

`Stream.parallel()` 函數是 Java 8 中引入的新特性,底層采用了 Fork/Join 框架來實現并行處理。當你調用 `parallel()` 函數時,實際上是將流的并行性設計為 true。這意味著所進行的任何操作,如 `map` 或 `filter`,都是在并行流(parallel stream)上執行的。Fork/Join 框架首先會將一個大任務拆分成若干個小任務(Fork),然后分別對這些小任務進行處理,最后將得到的結果合并(Join)來得到最終結果。0av28資訊網——每日最新資訊28at.com

這種方式能有效地將任務進行了分解,使得每個線程都可以獨立地處理一部分任務,從而發揮了多核 CPU 的優勢,提高了整體的處理效率。0av28資訊網——每日最新資訊28at.com

從上述解釋中可以看出,parallel 底層使用 Fork/Join 框架,對任務進行拆解,可以發揮多核的優勢,那怎么就成了性能殺手呢?0av28資訊網——每日最新資訊28at.com

先看下 Fork/Join 的整體執行流程:0av28資訊網——每日最新資訊28at.com

圖片圖片0av28資訊網——每日最新資訊28at.com

其執行主要分為以下幾個階段:0av28資訊網——每日最新資訊28at.com

  1. 分割階段(Fork Phase):將大任務拆分成若干個小任務,直到任務的規模足夠小,可以直接執行。這通常是通過遞歸方式實現的。
  2. 執行階段(Computation Phase):執行每個小任務,并生成結果。
  3. 結果合并階段(Join Phase):合并小任務的結果,生成大任務的結果。這也通常通過遞歸的方式實現,與拆分階段對應。
  4. 善后階段(Finalize Phase):所有任務的結果都已合并完畢,大任務的結果也已經生成,可以進行善后工作,比如釋放資源等。

每個階段都有一定開銷,從整個執行流程上看,執行階段占的時間越長,性能提升就越高。在數據量較少,或者執行操作開銷較大時,并行處理不但不能提高性能,還會由于線程管理和任務分配的開銷而導致性能下降。0av28資訊網——每日最新資訊28at.com

再次回到上面這個案例:0av28資訊網——每日最新資訊28at.com

// 將 Code 轉換為對應的枚舉public static OrderStatus parallelParseByCode(int code) {    return Stream.of(values())            .parallel()            .filter(status -> status.getCode() == code)            .findFirst()            .orElse(null);}

首先,枚舉的數量非常小,其次,執行邏輯非常簡單,僅進行一個等值比較。在這種情況下使用 parallel 函數,將致使線程管理和任務分配開銷巨大,從而成為系統瓶頸。0av28資訊網——每日最新資訊28at.com

2. 解決方案

既然問題是通過 parallel 函數引入的,那解決方案便是:刪除 parallel 函數調用,直接串行執行即可。0av28資訊網——每日最新資訊28at.com

修改后的代碼如下:0av28資訊網——每日最新資訊28at.com

public static OrderStatus parseByCode(int code) {    return Stream.of(values())            // .parallel() 直接使用串行執行            .filter(status -> status.getCode() == code)            .findFirst()            .orElse(null);}

使用相同的數據重新測試,耗時如下圖所示:0av28資訊網——每日最新資訊28at.com

圖片圖片0av28資訊網——每日最新資訊28at.com

可見,性能直接提升 10 倍不止。0av28資訊網——每日最新資訊28at.com

3. 示例&源碼

代碼倉庫:https://gitee.com/litao851025/learnFromBug0av28資訊網——每日最新資訊28at.com

代碼地址:https://gitee.com/litao851025/learnFromBug/tree/master/src/main/java/com/geekhalo/demo/thread/parallelfun0av28資訊網——每日最新資訊28at.com


0av28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-60900-0.html我被 parallel 函數雷了

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 內蒙古通遼破獲特大網絡組織淫穢表演案:涉十余萬名注冊用戶、4000 名女主播

下一篇: Requestium - 將Requests和Selenium合并在一起的自動化測試工具

標簽:
  • 熱門焦點
  • 官方承諾:K60至尊版將會首批升級MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版將會搭載天璣9200+處理器和獨顯芯片X7的同時,Redmi給出了官方承諾,K60至尊重大更新首批升級,會首批推送MIUI 15。也就是說雖然
  • 2023 年的 Node.js 生態系統

    隨著技術的不斷演進和創新,Node.js 在 2023 年達到了一個新的高度。Node.js 擁有一個龐大的生態系統,可以幫助開發人員更快地實現復雜的應用。本文就來看看 Node.js 最新的生
  • CSS單標簽實現轉轉logo

    轉轉品牌升級后更新了全新的Logo,今天我們用純CSS來實現轉轉的新Logo,為了有一定的挑戰性,這里我們只使用一個標簽實現,將最大化的使用CSS能力完成Logo的繪制與動畫效果。新logo
  • JVM優化:實戰OutOfMemoryError異常

    一、Java堆溢出堆內存中主要存放對象、數組等,只要不斷地創建這些對象,并且保證 GC Roots 到對象之間有可達路徑來避免垃 圾收集回收機制清除這些對象,當這些對象所占空間超過
  • 拼多多APP上線本地生活入口,群雄逐鹿萬億市場

    Tech星球(微信ID:tech618)文 | 陳橋輝 Tech星球獨家獲悉,拼多多在其APP內上線了&ldquo;本地生活&rdquo;入口,位置較深,位于首頁的&ldquo;充值中心&rdquo;內,目前主要售賣美食相關的
  • 中國家電海外掘金正當時|出海專題

    作者|吳南南編輯|胡展嘉運營|陳佳慧出品|零態LT(ID:LingTai_LT)2023年,出海市場戰況空前,中國創業者在海外紛紛摩拳擦掌,以期能夠把中國的商業模式、創業理念、戰略打法輸出海外,他們依
  • 新電商三兄弟,“抖快紅”成團!

    來源:價值研究所作 者:Hernanderz 隨著內容電商的概念興起,抖音、快手、小紅書組成的&ldquo;新電商三兄弟&rdquo;成為業內一股不可忽視的勢力,給阿里、京東、拼多多帶去了巨大壓
  • 微軟發布Windows 11新版 引入全新任務欄狀態

    近日,微軟發布了Windows 11新版,而Build 22563更新主要引入了幾周前曝光的平板模式任務欄等,系統更流暢了。更新中,Windows 11加入了專門針對平板優化的任務欄
  • 華為舉行春季智慧辦公新品發布會 首次推出電子墨水屏平板

    北京時間2月27日晚,華為在巴塞羅那舉行春季智慧辦公新品發布會,在海外市場推出之前已經在中國市場上市的筆記本、平板、激光打印機等辦公產品,并首次推出搭載
Top 主站蜘蛛池模板: 阿拉尔市| 上林县| 大同县| 富平县| 武乡县| 江陵县| 旌德县| 周口市| 龙江县| 余庆县| 潼南县| 灌南县| 府谷县| 蒙自县| 丹棱县| 尚义县| 延庆县| 涡阳县| 古蔺县| 旬阳县| 西畴县| 商城县| 水富县| 伊金霍洛旗| 陆良县| 淳化县| 新疆| 吉首市| 瓮安县| 蒲城县| 南澳县| 永吉县| 阿拉尔市| 沾益县| 龙井市| 唐海县| 龙门县| 沙河市| 凌源市| 施秉县| 洪泽县|