java - 插入海量数据时的 Hibernate 性能问题

标签 java mysql hibernate

我们会将大量数据(单一类型的实体)从 Amazon 的 DynamoDB 迁移到 MySQL 数据库中。我们正在使用 Hibernate 将此类映射到 mysql 实体。大约有 300 万个实体(不包括列表属性的行)。这是我们的类映射摘要:

@Entity
@Table(name = "CUSTOMER")
public class Customer {
    @Id
    @Column(name = "id")
    private String id;

    //Other properties in which all of them are primitive types/String

    @ElementCollection
    @CollectionTable(name = "CUSTOMER_USER", joinColumns = @JoinColumn(name = "customer_id"))
    @Column(name = "userId")
    private List<String> users;

    // CONSTRUCTORS, GETTERS, SETTERS, etc.
}

users 是一个字符串列表。我们创建了两个 mysql 表,如下所示:

CREATE TABLE CUSTOMER(id VARCHAR(100), PRIMARY KEY(id));
CREATE TABLE CUSTOMER_USER(customer_id VARCHAR(100), userId VARCHAR(100), PRIMARY KEY(customer_id, userId), FOREIGN KEY (customer_id) REFERENCES CUSTOMER(id));

注意:我们不会让 hibernate 生成任何 id 值,我们将我们的 ID 分配给保证唯一的客户实体。

这是我们的 hibernate.cfg.xml:

<hibernate-configuration>    
    <session-factory>    
    <property name="hibernate.dialect">   org.hibernate.dialect.MySQLDialect </property>    
    <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property>  
    <property name="hibernate.connection.url"> jdbc:mysql://localhost/xxx </property>    
    <property name="hibernate.connection.username"> xxx </property>    
    <property name="hibernate.connection.password"> xxx </property>
    <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
    <property name="hibernate.jdbc.batch_size"> 50 </property>
    <property name="hibernate.cache.use_second_level_cache">false</property>
    <property name="c3p0.min_size">30</property>
    <property name="c3p0.max_size">70</property>
    </session-factory> 
</hibernate-configuration>

我们正在创建一些线程,每个线程从 Dynamo 读取数据并通过 Hibernate 将它们插入到我们的 MySQl 数据库中。以下是每个线程的作用:

// Each single thread brings resultItems from DynamoDB
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
for(int i = 0; i < resultItems.size(); i++) {
    Customer cust = new Customer(resultItems.get(i));
    session.save(cust);
    if(i % BATCH_SIZE == 0) {
        session.flush();
        session.clear();
    }
}
tx.commit();
session.close();

我们有自己的性能监控功能,我们会持续记录整体读/写性能。问题是,迁移从读/写 1500 项/秒(平均)开始,但只要 CUSTOMER 和 CUSTOMER_USER 表中的行数增加,迁移速度就会一直变慢(几分钟后,读/写速度约为 500 项/秒)。我对 Hibernate 没有经验,这是我的问题:

  1. 对于像我们这样的多线程任务,hibernate.cfg.xml 应该是什么样的?我上面给出的内容是否适合这样的任务,或者有什么错误/遗漏的地方吗?
  2. 恰好有 50 个线程,每个线程执行以下操作:首先从 DynamoDB 读取,然后将结果插入 mysql 数据库,然后从 dynamo 读取,依此类推。因此,与 hibernate 通信的正常运行时间不是 100%。在这种情况下,您建议如何设置 c3p0 连接池大小的 min_size 和 max_size?为了能够理解这个概念,我是否也应该在 hibernate.cfg.xml 中设置剩余的 c3p0 相关标签?
  3. 如何才能最大限度地提高批量插入的速度?

NOTE 1 我没有写所有的属性,因为除了list of users 剩下的都是int, boolean, String等

注意 2 所有点都经过测试,对性能没有负面影响。当我们不向 mysql 数据库中插入任何内容时,读取速度在数小时内保持稳定。

注意 3 任何关于 mysql 表结构、配置设置、 session /事务、连接池数量、批处理大小等的建议/指导都将非常有帮助!

最佳答案

假设您在 hibernate 事务中除了将数据插入这两个表之外没有做任何其他事情,您可以使用 StatelessSession session = sessionFactory.openStatelessSession(); 而不是普通 session ,这样可以减少维护缓存的开销。但是你必须单独保存嵌套的集合对象。 引用https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/batch.html

所以它可能是这样的——

// Each single thread brings resultItems from DynamoDB
StatelessSession session = factory.openStatelessSession();
Transaction tx = session.beginTransaction();
for(int i = 0; i < resultItems.size(); i++) {
    Customer cust = new Customer(resultItems.get(i));   
    Long id = session.save(cust); // get the generated id
    // TODO: Create a list of related customer users and assign the id to all of them and then save those customer user objects in the same transaction.  
    if(i % BATCH_SIZE == 0) {
        session.flush();
        session.clear();
    }
}
tx.commit();
session.close();

关于java - 插入海量数据时的 Hibernate 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29105768/

相关文章:

java - Maven - 根据项目属性激活配置文件

mysql - 类型ORM。无法执行更新查询,因为未定义更新值。调用\"qb.set(...)\"方法指定更新值

mysql - 如何更新数据库中的日期间隔(Hibernate HQL)

hibernate - 是否可以停止 jpa 中的连接?

hibernate : attempted to assign id from null one-to-one property: employee

java - 子类构造函数中的默认值要传递给父类(super class)吗?

java - 从 C++ 到 Java 的简单转换

Java 声音可视化器

MySQL 表结构

php - SQL数据库不存储用户输入