最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

一、前言

最近在新發(fā)布某個(gè)項(xiàng)目上線時(shí),每次重啟都會(huì)收到機(jī)器的 CPU 使用率告警,查看對應(yīng)監(jiān)控,持續(xù)時(shí)長達(dá) 5 分鐘,對于服務(wù)重啟有很大風(fēng)險(xiǎn)。而該項(xiàng)目有非常多 Consumer 消費(fèi),服務(wù)啟動(dòng)后會(huì)有大量線程去拉取消息處理邏輯,通過多次 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)
        ......

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

我們可以先回想下 Mybatis 啟動(dòng)加載時(shí)的大致流程,查看下流程中哪些地方會(huì)操作 TypeHandler,會(huì)使用 ConcurrentHashMap.putVal 進(jìn)行緩存操作?

在 Mybatis 啟動(dòng)流程中,大致分為以下幾步:

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

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

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

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

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

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

而在第 6 步時(shí)候(圖中標(biāo)色),會(huì)去獲取 Java 對象類型與 JDBC 類型的映射關(guān)系,并把已經(jīng)處理過的映射關(guān)系 TypeHandler 存入本地緩存中。但是堆棧信息顯示,還是觸發(fā)了 TypeHandler 入緩存的操作,也就是某個(gè) paramType 并沒有命中緩存,而是在 SQL 查詢的時(shí)候?qū)崟r(shí)解析 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 緩存時(shí) debug 下,分析下哪種類型在緩存中缺失?

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

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

2.2、進(jìn)一步分析

為了進(jìn)一步分析,引入了對照組,而對照組的 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);

在裝載參數(shù)的 Handler 類 org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters 處進(jìn)行 debug 分析。

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

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

2.2.2、實(shí)驗(yàn)組為listxxxByMap(paramType=Map)

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

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

三、引申思考

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

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、實(shí)驗(yàn)組(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>

實(shí)驗(yàn)組代碼請求:

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

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

1、對照組(resultMap=BaseResultMap)

List unmappedColumnNames 長度為 0,表示所有字段都命中了 標(biāo)簽配置,符合預(yù)期。

2、實(shí)驗(yàn)組(resultType=JavaBean)

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

四、總結(jié)

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

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

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

五、后續(xù)

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

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

相關(guān)文章

  • SpringBoot越權(quán)和數(shù)據(jù)權(quán)限控制的實(shí)現(xiàn)方案(最新整理)

    SpringBoot越權(quán)和數(shù)據(jù)權(quán)限控制的實(shí)現(xiàn)方案(最新整理)

    文章介紹通過自定義注解@PermissionCheck、切面編程和用戶權(quán)限服務(wù)實(shí)現(xiàn)Java權(quán)限控制,限制只讀用戶操作,優(yōu)化數(shù)據(jù)庫查詢并統(tǒng)一異常處理,確保系統(tǒng)安全與擴(kuò)展性,本文給大家介紹SpringBoot越權(quán)和數(shù)據(jù)權(quán)限控制的實(shí)現(xiàn)方案,感興趣的朋友跟隨小編一起看看吧
    2025-06-06
  • SpringCloud OpenFeign超時(shí)控制示例詳解

    SpringCloud OpenFeign超時(shí)控制示例詳解

    在Spring Cloud中使用OpenFeign時(shí),可以通過配置來控制請求的超時(shí)時(shí)間,這篇文章主要介紹了SpringCloud OpenFeign超時(shí)控制,需要的朋友可以參考下
    2024-05-05
  • java SelectableChannel的使實(shí)例用法講解

    java SelectableChannel的使實(shí)例用法講解

    在本篇文章里小編給大家整理的是一篇關(guān)于java SelectableChannel的使實(shí)例用法講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-03-03
  • Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析

    Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析

    這篇文章主要介紹了Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • java操作文件之文件重命名實(shí)現(xiàn)方式

    java操作文件之文件重命名實(shí)現(xiàn)方式

    這篇文章主要介紹了java操作文件之文件重命名實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2026-03-03
  • Maven2 plugin開發(fā)教程詳解

    Maven2 plugin開發(fā)教程詳解

    這篇文章主要為大家詳細(xì)介紹了Maven2 plugin的開發(fā)教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java實(shí)現(xiàn)聊天機(jī)器人

    Java實(shí)現(xiàn)聊天機(jī)器人

    這篇文章主要為大家詳細(xì)介紹了Java簡單實(shí)現(xiàn)聊天機(jī)器人,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Java中的synchronized?優(yōu)化方法之鎖膨脹機(jī)制

    Java中的synchronized?優(yōu)化方法之鎖膨脹機(jī)制

    這篇文章主要介紹了Java中的synchronized?優(yōu)化方法之鎖膨脹機(jī)制,鎖膨脹機(jī)制是提升?synchronized?性能最有利的方法之一,下面我們就來看看什么事鎖膨脹及鎖膨脹的各種細(xì)節(jié)
    2022-05-05
  • Java后端用EL表達(dá)式改進(jìn)JSP

    Java后端用EL表達(dá)式改進(jìn)JSP

    EL 全名為Expression Language,EL的語法很簡單,它最大的特點(diǎn)就是使用上很方便,本文帶你用EL表達(dá)式改進(jìn)JSP,感興趣的朋友來看看吧
    2022-02-02
  • SpringBoot 集成 MyBatis/MyBatis-Plus 超詳細(xì)教程

    SpringBoot 集成 MyBatis/MyBatis-Plus 超詳細(xì)教程

    本文詳細(xì)介紹了SpringBoot集成MyBatis和MyBatis-Plus的完整流程,包括數(shù)據(jù)源配置、Mapper掃描、XML映射等,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-06-06

最新評論

始兴县| 丹江口市| 丹寨县| 临夏市| 云林县| 托克逊县| 方城县| 叶城县| 曲阳县| 永定县| 化州市| 增城市| 广南县| 曲松县| 宝丰县| 北碚区| 丽江市| 资源县| 濮阳县| 黄浦区| 大渡口区| 深泽县| 呼和浩特市| 平原县| 同仁县| 建始县| 延长县| 新津县| 苍南县| 麻城市| 绵竹市| 申扎县| 会宁县| 通河县| 青冈县| 淮安市| 张家港市| 昌都县| 河间市| 和林格尔县| 漾濞|