简介 在这篇文章中,我将向你展示使用Lightrun分析一个Java应用程序,这样你就可以发现各种性能调整的改进,你可以应用到你当前的Java应用程序。 在上一篇文章中,我解释了什么是Lightrun,以及你如何使用它来注入动态日志、捕获运行时快照或添加动态指标。 在这篇文章中,我将使用Lightrun作为我的JPA关联获取验证器的替代品。DefaultLoadEventListener 当使用Hibernate获取JPA实体时,会触发一个LoadEvent,由DefaultLoadEventListener,处理方式如下。 DefaultLoadEventListener将检查该实体是否位于当前JPA持久化上下文或第一级缓存中。如果在那里找到了实体,那么就会返回相同的对象引用。 这意味着,两个连续的实体获取调用将总是返回相同的JavaObject引用。这就是JPA和Hibernate提供应用级可重复读取的原因。 如果在第一级缓存中没有找到实体,Hibernate将尝试从第二级缓存中加载它,如果且仅当第二级缓存被启用。 最后,如果实体不能从任何缓存中加载,它将被从数据库中加载。 现在,这个过程可以在调用EntityManager。find,在遍历一个关联时发生,也可以间接地用于FetchType。EAGER策略。检查N1查询问题 JPA关联获取验证器文章解释了如何以编程方式断言JPA关联获取。这个工具在测试过程中非常有用,但是对于那些要第一次检查生产系统的顾问来说就不太实用了。 例如,让我们举一个SpringPetClinic应用程序的例子。EntityTable(namepets)publicclassPetextendsNamedEntity{Column(namebirthdate)DateTimeFormat(patternyyyyMMdd)privateLocalDatebirthDate;ManyToOneJoinColumn(nametypeid)privatePetTypetype;ManyToOneJoinColumn(nameownerid)privateOwnerowner;}复制代码 Pet实体有两个父关联,type和owner,每个都被注解为ManyToOne。然而,默认情况下,ManyToOne关联使用FetchType。EAGER的获取策略。 因此,如果我们加载2个Pet实体,同时也获取它们相关的owner关联。ListPetpetsentityManager。createQuery(selectpfromPetpjoinfetchp。ownerwherep。idin:petIds)。setParameter(petIds,List。of(3L,6L))。getResultList();复制代码 Hibernate将执行3次查询。SELECTp。idasid111,p。nameasname211,p。birthdateasbirthda311,p。owneridasownerid411,p。typeidastypeid511,o。idasid100,o。firstnameasfirstna200,o。lastnameaslastnam300,o。addressasaddress400,o。cityascity500,o。telephoneastelephon600FROMpetspJOINownersoONo。idp。owneridWHEREp。idIN(3,6)SELECTpt。idasid130,pt。nameasname230FROMtypesptWHEREpt。id3SELECTpt。idasid130,pt。nameasname230FROMtypesptWHEREpt。id6复制代码 那么,为什么会有3个查询被执行,而不是只有一个?这就是臭名昭著的N1查询问题。使用Lightrun进行Java性能调优 虽然你可以使用集成测试来检测N1查询问题,但有时你不能这样做,因为你被雇来分析的系统已经部署到生产中,而你还没有看到源代码。 在这种情况下,像Lightrun这样的工具就变得非常方便,因为你可以简单地动态注入一个运行时快照,这个快照只有在满足特定条件时才会被记录。 第一步是在DefaultLoadEventListenerHibernate类的loadFromDatasource方法中添加一个运行时快照。 注意,快照只记录相关的LoadEvent的isAssociationFetch()方法返回true。这个条件允许我们捕获N1查询问题所执行的二次查询。 现在,当加载所有姓戴维斯的宠物主人时,PetClinic应用程序会执行以下SQL查询。SELECTDISTINCTo。idASid100,p。idASid111,o。firstnameASfirstna200,o。lastnameASlastnam300,o。addressASaddress400,o。cityAScity500,o。telephoneAStelephon600,p。nameASname211,p。birthdateASbirthda311,p。owneridASownerid411,p。typeidAStypeid511,p。owneridASownerid410,p。idASid110FROMownersoLEFTOUTERJOINpetspONo。idp。owneridWHEREo。lastnameLIKEDavisSELECTpt。idasid130,pt。nameasname230FROMtypesptWHEREpt。id6SELECTpt。idasid130,pt。nameasname230FROMtypesptWHEREpt。id3复制代码 而在检查Lightrun快照控制台时,我们可以看到有两条记录被注册。 第一个快照看起来如下。 而第二个快照看起来是这样的。 请注意,由于大量使用了FetchType。EAGER策略,这两个快照对应于SpringPetclinic应用程序执行的二级查询。 很酷,对吗?结论 虽然你可以在测试过程中使用JPA关联获取验证器来检测这些N1查询问题,但如果你的任务是分析一个你从未见过的运行时系统,那么Lightrun就是一个发现各种问题及其发生原因的伟大工具。 特别是因为Java性能调优是我被雇用的最常见的原因之一,Lightrun是我的工具集的一个很好的补充。