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

Mybatis的parameterType造成線程阻塞問題分析

 更新時間:2023年06月08日 10:58:40   作者:京東云開發(fā)者  
這篇文章主要詳細分析了Mybatis的parameterType造成線程阻塞問題,文中有詳細的解決方法,及相關的代碼示例,具有一定的參考價值,感興趣的朋友可以借鑒閱讀

一、前言

最近在新發(fā)布某個項目上線時,每次重啟都會收到機器的 CPU 使用率告警,查看對應監(jiān)控,持續(xù)時長達 5 分鐘,對于服務重啟有很大風險。而該項目有非常多 Consumer 消費,服務啟動后會有大量線程去拉取消息處理邏輯,通過多次 Jstack 輸出線程快照發(fā)現(xiàn)有很多 BLOCKED 狀態(tài)線程,此文主要記錄分析 BLOCKED 原因。

二、分析過程

2.1、初步分析

"consumer_order_status_jmq1714_1684822992337" #3125 daemon prio=5 os_prio=0 tid=0x00007fd9eca34000 nid=0x1ca4f waiting for monitor entry [0x00007fd1f33b5000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1027)
    - waiting to lock <0x000000056e822bc8> (a java.util.concurrent.ConcurrentHashMap$Node)
    at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
    at org.apache.ibatis.type.TypeHandlerRegistry.getJdbcHandlerMap(TypeHandlerRegistry.java:234)
    at org.apache.ibatis.type.TypeHandlerRegistry.getTypeHandler(TypeHandlerRegistry.java:200)
    at org.apache.ibatis.type.TypeHandlerRegistry.getTypeHandler(TypeHandlerRegistry.java:191)
    at org.apache.ibatis.mapping.ParameterMapping$Builder.resolveTypeHandler(ParameterMapping.java:128)
    at org.apache.ibatis.mapping.ParameterMapping$Builder.build(ParameterMapping.java:103)
    at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.buildParameterMapping(SqlSourceBuilder.java:123)
    at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.handleToken(SqlSourceBuilder.java:67)
    at org.apache.ibatis.parsing.GenericTokenParser.parse(GenericTokenParser.java:78)
    at org.apache.ibatis.builder.SqlSourceBuilder.parse(SqlSourceBuilder.java:45)
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:44)
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:292)
    at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:83)
	at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
	at com.sun.proxy.$Proxy232.query(Unknown Source)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:77)
	at sun.reflect.GeneratedMethodAccessor160.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
	at com.sun.proxy.$Proxy124.selectOne(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:82)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
        ......

通過對服務連續(xù)間隔 1 分鐘使用 Jstack 抓取線程快照,發(fā)現(xiàn)存在部分線程是 BLOCKED 狀態(tài),通過堆??梢钥闯?,當前線程阻塞在 ConcurrentHashMap.putVal,而 putVal 方法內部使用了 synchronized 導致當前線程被 BLOCKED,而上一級是 Mybaits 的TypeHandlerRegistry,TypeHandlerRegistry 的作用是記錄 Java 類型與 JDBC 類型的相互映射關系,例如 java.lang.String 可以映射 JdbcType.CHAR、JdbcType.VARCHAR 等,更上一級是 Mybaits 的 ParameterMapping,而 ParameterMapping 的作用是記錄請求參數的信息,包括 Java 類型、JDBC 類型,以及兩種類型轉換的操作類 TypeHandler。通過以上信息可以初步定位為在并發(fā)情況下 Mybaits 解析某些參數導致大量線程被阻塞,還需繼續(xù)往下分析。

我們可以先回想下 Mybatis 啟動加載時的大致流程,查看下流程中哪些地方會操作 TypeHandler,會使用 ConcurrentHashMap.putVal 進行緩存操作?

在 Mybatis 啟動流程中,大致分為以下幾步:

1、XMLConfigBuilder#parseConfiguration() 讀取本地XML文件

2、XMLMapperBuilder#configurationElement() 解析XML文件中的 select|insert|update|delete 標簽

3、XMLMapperBuilder#parseStatementNode() 開始解析單條 SQL,包括請求參數、返回參數、替換占位符等

4、SqlSourceBuilder 組合單條 SQL 的基本信息

5、SqlSourceBuilder#buildParameterMapping() 解析請求參數

6、ParameterMapping#getJdbcHandlerMap() 解析 Java 與 JDBC 類型,并把映射結果放入緩存

而在第 6 步時候(圖中標色),會去獲取 Java 對象類型與 JDBC 類型的映射關系,并把已經處理過的映射關系 TypeHandler 存入本地緩存中。但是堆棧信息顯示,還是觸發(fā)了 TypeHandler 入緩存的操作,也就是某個 paramType 并沒有命中緩存,而是在 SQL 查詢的時候實時解析 paramType,在高并發(fā)情況下造成了線程阻塞情況。下面繼續(xù)分析下 sql xml 的配置:

<select id="listxxxByMap" parameterType="java.util.Map" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from xxxxx
        where business_id = #{businessId,jdbcType=VARCHAR}
        and template_id = #{templateId,jdbcType=INTEGER}
    </select>

代碼請求:

Map<String, Object> params = new HashMap<>();
params.put("businessId", "11111");
params.put("templateId", "11111");
List<TrackingInfo> result = trackingInfoMapper.listxxxByMap(params);

初步看沒發(fā)現(xiàn)問題,但是我們在入 TypeHandler 緩存時 debug 下,分析下哪種類型在緩存中缺失?

從 debug 信息中可以看出,TypeHandler 緩存中存在的是 interface java.util.Map,而 SQL 執(zhí)行時傳入的是 class java.util.HashMap,導致并沒有命中緩存。那我們修改下 xml 文件為 parameterType="java.util.HashMap" 是不是就解決了?

很遺憾,部署后仍然存在問題。

2.2、進一步分析

為了進一步分析,引入了對照組,而對照組的 paramType 為具體 JavaBean。

<select id="listResultMap" parameterType="com.jdwl.xxx.domain.TrackingInfo" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from xxxx
        where business_id = #{businessId,jdbcType=VARCHAR}
        and template_id = #{templateId,jdbcType=INTEGER}
    </select>

對照組代碼請求

TrackingInfo record = new TrackingInfo();
record.setBusinessId("11111");
record.setTemplateId(11111);
List<TrackingInfo> result = trackingInfoMapper.listResultMap(record);

在裝載參數的 Handler 類 org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters 處進行 debug 分析。

2.2.1、對照組為 listResultMap(paramType=JavaBean)

兩個參數的解析類型分別為 StringTypeHandler(紅框中灰色的字)與 IntegerTypeHandler(紅框中灰色的字),已經是 Mybatis 提供的 TypeHandler,并沒有再進行類型的二次解析。說明 JavaBean 中的 businessId、templateId 字段已經在啟動時候被預解析了。

2.2.2、實驗組為listxxxByMap(paramType=Map)

兩個參數的解析都是 UnknownTypeHandler(紅框中灰色的字),而在 UnknownTypeHandler 中會再次調用 resolveTypeHandler() 方法,對參數進行類型的二次解析??梢岳斫鉃?Map 里的屬性不是固定類型,只能在執(zhí)行 SQL 時候再解析一次。

最后修改為 paramType=JavaBean 部署測試環(huán)境再抓包,并未發(fā)現(xiàn) TypeHandlerRegistry 相關的線程阻塞。

三、引申思考

既然 paramType 傳值會出現(xiàn)阻塞問題,那 resultType 與 resultMap 是不是有相同問題呢?繼續(xù)分為兩個實驗組:

1、對照組(resultMap=BaseResultMap)

<resultMap id="BaseResultMap" type="com.jdwl.tracking.domain.TrackingInfo">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="template_id" property="templateId" jdbcType="INTEGER"/>
        <result column="business_id" property="businessId" jdbcType="VARCHAR"/>
        <result column="is_delete" property="isDelete" jdbcType="TINYINT"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
        <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
        <result column="ts" property="ts" jdbcType="TIMESTAMP"/>
    </resultMap>
<select id="listResultMap" parameterType="com.jdwl.tracking.domain.TrackingInfo" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from tracking_info
        where business_id = #{businessId,jdbcType=VARCHAR}
        and template_id = #{templateId,jdbcType=INTEGER}
    </select>

對照組代碼請求:

TrackingInfo record = new TrackingInfo();
record.setBusinessId("11111");
record.setTemplateId(11111);
List<TrackingInfo> result1 = trackingInfoMapper.listResultMap(record);

2、實驗組(resultType=JavaBean)

<select id="listResultType" parameterType="com.jdwl.tracking.domain.TrackingInfo" resultType="com.jdwl.tracking.domain.TrackingInfo">
        select
        <include refid="Base_Column_List"/>
        from tracking_info
        where business_id = #{businessId,jdbcType=VARCHAR}
        and template_id = #{templateId,jdbcType=INTEGER}
    </select>

實驗組代碼請求:

TrackingInfo record = new TrackingInfo();
record.setBusinessId("11111");
record.setTemplateId(11111);
List<TrackingInfo> result2 = trackingInfoMapper.listResultType(record);

在對返回結果 Handler 處理類 org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createAutomaticMappings() 進行 debug 分析。

1、對照組(resultMap=BaseResultMap)

List unmappedColumnNames 長度為 0,表示所有字段都命中了 標簽配置,符合預期。

2、實驗組(resultType=JavaBean)

List unmappedColumnNames 長度為 11,表示所有字段都在 標簽配置中未找到。這是因為 SQL 執(zhí)行后的 resultMap 對應的 id 并不等于標簽的 id,所以這些字段被標識為未解析,又會執(zhí)行 TypeHandlerRegistry 的類型映射邏輯,引發(fā)并發(fā)時線程阻塞問題。

四、總結

1、在使用 paramType 時,xml 配置的類型需要與 Java 代碼中傳入的一致,使用 Mybatis 預加載時的類型緩存。

2、在使用 paramType 時,避免使用 java.util.HashMap 類型,避免 SQL 執(zhí)行時解析 TypeHandler。

3、在接受返回值時,使用 resultMap,提前映射返回值,減少 TypeHandler 解析。

五、后續(xù)

在 Mybatis 社區(qū)已經優(yōu)化了 TypeHandler 入緩存的邏輯,可以解決重復計算 TypeHandler 問題,一定程度上緩解以上問題。但是 Mybatis 修復最低版本為 3.5.8,依賴 spring5.x,而我們項目使用的 Mybatis3.4.4,spring4.x,直接升級會存在一定風險,所以在不升級情況下,按照總結規(guī)范使用也可以降低阻塞風險。

以上就是Mybatis的parameterType造成線程阻塞問題分析的詳細內容,更多關于Mybatis parameterType 線程阻塞的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解Java?Unsafe如何花式操作內存

    詳解Java?Unsafe如何花式操作內存

    C++可以動態(tài)的分類內存,而java并不能這樣,是不是java就不能操作內存呢,其實是有其他辦法可以操作內存的,下面就一起看看Unsafe是如何花式操作內存的吧
    2023-08-08
  • Spring根據XML配置文件注入屬性的方法

    Spring根據XML配置文件注入屬性的方法

    下面小編就為大家?guī)硪黄猄pring根據XML配置文件注入屬性的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • Spring實現(xiàn)動態(tài)切換多數據源的解決方案

    Spring實現(xiàn)動態(tài)切換多數據源的解決方案

    這篇文章主要給大家介紹了Spring實現(xiàn)動態(tài)切換多數據源的解決方案,文中給出了詳細的介紹和示例代碼,相信對大家的理解和學習具有一定的參考借鑒價值,有需要的朋友可以參考學習,下面來一起看看吧。
    2017-01-01
  • java設計模式Ctrl?C和Ctrl?V的原型模式詳解

    java設計模式Ctrl?C和Ctrl?V的原型模式詳解

    這篇文章主要為大家介紹了java設計模式Ctrl?C和Ctrl?V的原型模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • idea在plugins中搜不到插件的解決方法

    idea在plugins中搜不到插件的解決方法

    本文主要介紹了idea在plugins中搜不到插件的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • java動態(tài)添加外部jar包到classpath的實例詳解

    java動態(tài)添加外部jar包到classpath的實例詳解

    這篇文章主要介紹了java動態(tài)添加外部jar包到classpath的實例詳解的相關資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • 詳解Spring Boot 目錄文件結構

    詳解Spring Boot 目錄文件結構

    這篇文章主要介紹了Spring Boot 目錄文件結構的相關資料,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • 在Java中輕松使用工廠設計模式介紹

    在Java中輕松使用工廠設計模式介紹

    這篇文章主要介紹了在Java中輕松使用工廠設計模式介紹,工廠設計模式或工廠方法設計模式是一種廣泛使用且易于理解的設計模式,文章通過圍繞主題展開詳細的內容介紹,感興趣的朋友可以參考一下
    2022-09-09
  • Java 線程池全面總結與詳解

    Java 線程池全面總結與詳解

    在一個應用程序中,我們需要多次使用線程,也就意味著,我們需要多次創(chuàng)建并銷毀線程。而創(chuàng)建并銷毀線程的過程勢必會消耗內存。而在Java中,內存資源是及其寶貴的,所以,我們就提出了線程池的概念
    2021-10-10
  • 一篇文章帶你了解SpringMVC數據綁定

    一篇文章帶你了解SpringMVC數據綁定

    這篇文章主要給大家介紹了關于如何通過一篇文章弄懂Spring MVC的參數綁定,文中通過示例代碼以及圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2021-08-08

最新評論