【问题标题】:What causes Spring Boot Fail-safe cleanup (collections) to occur是什么导致 Spring Boot Fail-safe cleanup (collections) 发生
【发布时间】:2019-05-01 13:51:33
【问题描述】:

我有一个 Java Spring Boot 应用程序,其中包含与以下异常相关的以下实体

产品

@Entity
@Table(
        name = "product",
        indexes = @Index(
                name = "idx_asin",
                columnList = "asin",
                unique = true
        )
)
public class SProduct implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Column(name = "asin", unique = false, nullable = false, length = 10)
    private String asin;
    @Column(name = "rootcategory")
    private Long rootcategory;
    @Column(name = "imageCSV", unique = false, nullable = true, length = 350)
    private String imagesCSV;
    @Column(name = "title", unique = false, nullable = true, length = 350)
    private String title;
    private Date created;
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "mainProduct", cascade = CascadeType.ALL)
    private Set<FBT> fbts;
    @OneToOne(fetch = FetchType.EAGER, mappedBy = "downloadProductId", cascade = CascadeType.ALL)
    private Download download;

FBT

@Entity
@Table(
    name = "fbt",
    uniqueConstraints={@UniqueConstraint(columnNames = {"main_product_id" , "collection"})},
    indexes = {@Index(
        name = "idx_main_product_id",
        columnList = "main_product_id",
        unique = false),
        @Index(
        name = "idx_product_fbt1id",
        columnList = "product_fbt1_id",
        unique = false),
        @Index(
        name = "idx_product_fbt2id",
        columnList = "product_fbt2_id",
        unique = false)
        }
)
public class FBT implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @ManyToOne
    @JoinColumn(name = "main_product_id")
    private SProduct mainProduct;
    @ManyToOne
    @JoinColumn(name = "product_fbt1_id")
    private SProduct sproductFbt1;
    @ManyToOne
   
    @JoinColumn(name = "product_fbt2_id")
    private SProduct sproductFbt2;
    @Column(name = "bsr", nullable = false)
    private int bsr;
    private Date collection;

我的 fbt 存储库中有以下查询

  FBT findByMainProductAndCollection(SProduct mainProduct,Date collection);

当mainProduct和collection的数据存在于数据库中时,导致以下消息输出异常,否则返回null。

  <message>HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@69b7fcfc&lt;rs=HikariProxyResultSet@325408381 wrapping com.mysql.jdbc.JDBC42ResultSet@108693fa&gt;</message>
  <message>HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries</message>
  <message>HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@47c40535&lt;rs=HikariProxyResultSet@2005129089 wrapping com.mysql.jdbc.JDBC42ResultSet@9894f70&gt;</message>
  <message>HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries</message>
  <message>HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@5b0cd175&lt;rs=HikariProxyResultSet@1598144514 wrapping com.mysql.jdbc.JDBC42ResultSet@6a7ff475&gt;</message>
  <message>HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries</message>
  <message>HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@f67e2cc&lt;rs=HikariProxyResultSet@319200129 wrapping com.mysql.jdbc.JDBC42ResultSet@215b8a6&gt;</message>
  <message>HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries</message>
  <message>HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@5961afc0&lt;rs=HikariProxyResultSet@1772496904 wrapping com.mysql.jdbc.JDBC42ResultSet@5956a59b&gt;</message>
  <message>HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries</message>
  <message>HHH000100: Fail-safe cleanup (collections) : 

我决定放弃上述内容并编写一个@query 来计数,因为我只需要确定数据是否存在,这阻止了让我认为我应该更改所有代码以使用@query 的问题。

 @Query("select count(*) as count from FBT where main_product_id = :id and collection= :collection")
    int countByMainProductIdAndCollection(@Param("id") long id, @Param("collection") Date collection);

尽管当产品已经存在于数据库中时,这种情况在更新到某个 SProduct 的数据库时似乎也是随机发生的。

SProductRepo.saveAndFlush(s);

我说随机运行相同代码的 11 个应用程序以随机​​间隔退出上述消息。代码没有生成异常,并且使用导致失败的相同代码发生了 10000 次成功的数据库更新。代码在尝试更新之前工作的数据库时停止。

""2018-12-28 00:56:06 [KeepaAPI-RetryScheduler] WARN  org.hibernate.engine.loading.internal.LoadContexts - HHH000100: Fail-safe cleanup (collections) : org.hibernate.eng
ine.loading.internal.CollectionLoadContext@5c414639<rs=HikariProxyResultSet@1241510017 wrapping Result set representing update count of 13>
""2018-12-28 00:56:06 [KeepaAPI-RetryScheduler] WARN  org.hibernate.engine.loading.internal.CollectionLoadContext - HHH000160: On CollectionLoadContext#cleanup, localLoa
dingCollectionKeys contained [1] entries
""2018-12-28 00:56:06 [KeepaAPI-RetryScheduler] WARN  org.hibernate.engine.loading.internal.LoadContexts - HHH000100: Fail-safe cleanup (collections) : org.hibernate.eng
ine.loading.internal.CollectionLoadContext@5595c065<rs=HikariProxyResultSet@2140082434 wrapping Result set representing update count of 14>
""2018-12-28 00:56:06 [KeepaAPI-RetryScheduler] WARN  org.hibernate.engine.loading.internal.CollectionLoadContext - HHH000160: On CollectionLoadContext#cleanup, localLoa
dingCollectionKeys contained [1] entries
""2018-12-28 00:56:06 [KeepaAPI-RetryScheduler] WARN  org.hibernate.engine.loading.internal.LoadContexts - HHH000100: Fail-safe cleanup (collections) : org.hibernate.eng
ine.loading.internal.CollectionLoadContext@2956fe24<rs=HikariProxyResultSe

此外,SProduct findByAsin(String asin) 查询会导致同样的问题,但是数据库中的查询可以正常工作,并且这曾经在 spring boot 中工作。

mysql> select * from product where asin="B004FXJOQO";
| id | asin       | created    | imagecsv                                                                        | rootcategory | title                                                                                                        |  9 | B004FXJOQO | 2018-08-04 | 41T0ZwTvSSL.jpg,61V90AZKbGL.jpg,51AdEGCTZqL.jpg,51LDnCYfR0L.jpg,71bbIw43PjL.jpg |       228013 | Dual Voltage Tester, Non Contact Tester for High and Low Voltage with 3-m Drop Protection Klein Tools NCVT-2 |
1 row in set (0.00 sec)

我想知道生成此类消息的一般原因是什么?

尽管我的插入语句周围的 try catch 语句是我代码中最后执行的语句,但为什么它们会停止我的应用程序?

是否有日志调试设置可用于确定生成消息的确切原因?

有没有办法关闭或控制此功能?

Pom

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <maven-dependency-plugin.version>2.10</maven-dependency-plugin.version>
        <maven.test.skip>true</maven.test.skip>
    </properties>
    <repositories>
        <repository>
            <id>Keepa</id>
            <name>Keepa Repository</name>
            <url>https://keepa.com/maven/</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency> 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.18</version>
        </dependency>
        <dependency>
            <groupId>com.google.api-client</groupId>
            <artifactId>google-api-client</artifactId>
            <version>1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.oauth-client</groupId>
            <artifactId>google-oauth-client-jetty</artifactId>
            <version>1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-oauth2</artifactId>
            <version>v1-rev120-1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.oauth-client</groupId>
            <artifactId>google-oauth-client-java6</artifactId>
            <version>1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.oauth-client</groupId>
            <artifactId>google-oauth-client</artifactId>
            <version>1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-gmail</artifactId>
            <version>v1-rev48-1.22.0</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.5.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-nop</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.54</version>
        </dependency>
        <dependency>
            <groupId>com.myjeeva.digitalocean</groupId>
            <artifactId>digitalocean-api-client</artifactId>
            <version>2.16</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <dependency>
            <groupId>com.keepa.api</groupId>
            <artifactId>backend</artifactId>
            <version>LATEST</version>
        </dependency>
        <dependency>
            <groupId>org.jdeferred</groupId>
            <artifactId>jdeferred-core</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>22.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build

我将内存从 1gb 增加到 2gb,但内存只有可用内存的 30%。

有什么想法吗?

【问题讨论】:

  • 如果您不需要数据,请尝试existsByMainProductAndCollection()
  • 您是否也遇到过上述问题? " java.lang.OutOfMemoryError: 超出 GC 开销限制"

标签: java mysql hibernate spring-boot


【解决方案1】:

您的应用程序似乎正在加载大量数据。

方法

FBT findByMainProductAndCollection(SProduct mainProduct,Date collection);

将加载所有匹配的数据。但是您只需要 count 尝试准确返回数据计数而不是所有数据的查询。

一种方法是使用您提到的查询,另一种方法是

Long countByMainProductAndCollection(SProduct mainProduct, Date collection);

或者

Boolean existsByMainProductAndCollection(SProduct mainProduct, Date collection)

【讨论】:

  • 是的,对于 SProductRepo.saveAndFlush(s),我只保存一个 SProduct。想了解那里的问题,并为赏金回答这些问题。
  • 奇怪的是这些 findby 方法应该只返回几条记录。执行 SProductRepo.findByAsin(asin); 时遇到同样的问题但是,数据库中只有一条带有该 asin 的记录,我可以查询它。考虑到 150 万个 asin,这是一个超时问题吗?
【解决方案2】:

首先,这是org.hibernate.engine处理的Hibernate Error,与Spring Boot无关。

如果您使用 HQL 查询获取大量数据(例如数万个实体),则可能会发生这种情况。

如果您映射了具有许多子实体的一对多关联,并且由于双向映射,结果集无限复制,也可能出现这种情况。

有关高性能 JPA 提示,请参阅下面的链接。

https://vladmihalcea.com/14-high-performance-java-persistence-tips/

【讨论】:

    【解决方案3】:

    你可以试试@Fetch(value = SELECT)吗?

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "mainProduct", cascade = CascadeType.ALL)
    @Fetch(value=FetchMode.SELECT)
    private Set<FBT> fbts;
    

    【讨论】:

      【解决方案4】:

      在我的情况下,这是因为实体递归地调用彼此的哈希码,如果您使用 lombock 删除它并自己制作。将调试器的断点放在两个哈希码的方法上。 你会发现他们在互相呼唤。 例如从第一个实体的哈希码方法中删除第二个实体的链接。

      【讨论】:

      • 如果您正在使用@Data,请将其删除并重试。 Data 等价于 Getter Setter RequiredArgsConstructor ToString EqualsAndHashCode。
      • 使用lombock,可以使用exclude属性和EqualsAndHashCode注解来消除导致溢出的字段
      • 谢谢!这是唯一对我有用的解决方案!我已经为我的 POJO 实现了 equals 和 hashCode,它开始为我工作了!!!
      【解决方案5】:

      我在使用 Set 时遇到了这个问题,但是当我更改为 List 时它已解决 你应该使用

        private List<FBT> fbts;
      

      【讨论】:

        【解决方案6】:

        在我的例子中,我使用了一个自动生成的 Repository 方法,该方法返回了 List&lt;MyData&gt;。事实证明,2000 个实体对于 Hibernate 来说太多了。我通过将其替换为返回 Stream&lt;MyData&gt; 的方法来解决此问题,然后将实体映射到手工制作的结构 MyDataDetached

        【讨论】:

          【解决方案7】:

          在我的情况下,我在运行时收到 "java.lang.StackOverflowError: null"(以前还有多个 "Fail-safe cleanup (collections)" 日志消息)具有双向关系 @ManyToOne@OneToMany 的特定实体上的 JPA save 方法。奇怪的是,它只在 Oracle 和 PostgreSQL 上失败,但在 MySQL、MariaDB 和 SQLServer 上运行良好。

          所以问题是 lombok@OneToMany 中的 HashCode > 实体。

          我解决了它在级别类的实体中设置此注释:

          @EqualsAndHashCode(exclude = "nameAttributeInThisClassWithOneToMany")

          【讨论】:

          • 拯救了我的一天,这太棒了!
          猜你喜欢
          • 2019-01-09
          • 1970-01-01
          • 1970-01-01
          • 2010-09-19
          • 2013-06-10
          • 2015-07-22
          • 1970-01-01
          • 1970-01-01
          • 2018-09-20
          相关资源
          最近更新 更多