무슨 일이 있어도 최대 절전 모드에서 MySQL INSERT 문을 배치할 수 없습니다.
저는 현재 잘 알려진 일반적인 Hibernate 삽입 배치 문제에 직면해 있습니다.
5백만 행의 배치를 저장해야 합니다.저는 먼저 훨씬 더 가벼운 페이로드로 시도하고 있습니다.2가지 유형의 엔티티만 삽입해야 하기 때문에(처음에는 A 유형의 모든 레코드, 다음에는 B 유형의 모든 레코드, 모두 공통된 C 유형을 가리킴)ManyToOne
parent), JDBC batch insert를 최대한 활용하고 싶습니다.
나는 이미 많은 문서를 읽었지만, 시도해 본 것은 하나도 없습니다.
- 배치 인서트를 사용하려면 엔티티 생성기를 사용해서는 안 된다는 것을 알고 있습니다.그래서 제거했습니다.
AUTO_INCREMENT
ID와 나는 속임수로 ID를 설정합니다.SELECT MAX(ID) FROM ENTITIES
그리고 매번 증가합니다. - 정기적으로 세션을 플러시해야 한다는 것을 알고 있습니다.코드는 미리 올리겠지만 어쨌든 나는 500개의 요소마다 트랜잭션을 수행합니다.
- 제가 해야 할 일이
hibernate.jdbc.batch_size
애플리케이션의 대용량 크기와 일치하기 때문에 에 설정했습니다.LocalSessionFactoryBean
(스프링 ORM 통합) - 연결 URL에서 일괄 명세서 다시 작성을 활성화해야 한다는 것을 알고 있습니다.
여기 내 엔티티가 있습니다.
공통 상위 엔티티입니다.이것은 단일 트랜잭션에서 가장 먼저 삽입됩니다.자동 증분 열은 여기서 신경 안 써요.배치 작업당 하나의 레코드만 해당
@Entity
@Table(...)
@SequenceGenerator(...)
public class Deal
{
@Id
@Column(
name = "DEAL_ID",
nullable = false)
@GeneratedValue(
strategy = GenerationType.AUTO)
protected Long id;
................
}
아이들 중 한 명 (한 회분당 2.5M 기록이라고 하자)
@Entity
@Table(
name = "TA_LOANS")
public class Loan
{
@Id
@Column(
name = "LOAN_ID",
nullable = false)
protected Long id;
@ManyToOne(
optional = false,
targetEntity = Deal.class,
fetch = FetchType.LAZY)
@JoinColumn(
name = "DEAL_ID",
nullable = false)
protected Deal deal;
.............
}
다른 아이들은 타자를 칩니다.다른 2.5M 기록들을 얘기해보죠.
@Entity
@Table(
name = "TA_BONDS")
public class Bond
{
@Id
@Column(
name = "BOND_ID")
@ManyToOne(
fetch = FetchType.LAZY,
optional = false,
targetEntity = Deal.class)
@JoinColumn(
name = "DEAL_ID",
nullable = false,
updatable = false)
protected Deal deal;
}
레코드를 삽입하는 단순 코드
long loanIdCounter = loanDao.getMaxId(), bondIdCounter = bondDao.getMaxId(); //Perform SELECT MAX(ID)
Deal deal = null;
List<Bond> bondList = new ArrayList<Bond>(COMMIT_BATCH_SIZE); //500 constant value
List<Loan> loanList = new ArrayList<Loan>(COMMIT_BATCH_SIZE);
for (String msg: inputStreamReader)
{
log.debug(msg.toString());
if (this is a deal)
{
Deal deal = parseDeal(msg.getMessage());
deal = dealManager.persist(holder.deal); //Called in a separate transaction using Spring annotation @Transaction(REQUIRES_NEW)
}
else if (this is a loan)
{
Loan loan = parseLoan(msg.getMessage());
loan.setId(++loanIdCounter);
loan.setDeal(deal);
loanList.add(loan);
if (loanList.size() == COMMIT_BATCH_SIZE)
{
loanManager.bulkInsert(loanList); //Perform a bulk insert in a single transaction, not annotated but handled manually this time
loanList.clear();
}
}
else if (this is a bond)
{
Bond bond = parseBond(msg.getMessage());
bond.setId(++bondIdCounter);
bond.setDeal(deal);
bondList.add(bond);
if (bondList.size() == COMMIT_BATCH_SIZE) //As above
{
bondManager.bulkInsert(bondList);
bondList.clear();
}
}
}
if (!bondList.isEmpty())
bondManager.bulkInsert(bondList);
if (!loanList.isEmpty())
loanManager.bulkInsert(loanList);
//Flush remaining items, not important
구현bulkInsert
:
@Override
public void bulkInsert(Collection<Bond> bonds)
{
// StatelessSession session = sessionFactory.openStatelessSession();
Session session = sessionFactory.openSession();
try
{
Transaction t = session.beginTransaction();
try
{
for (Bond bond : bonds)
// session.persist(bond);
// session.insert(bond);
session.save(bond);
}
catch (RuntimeException ex)
{
t.rollback();
}
finally
{
t.commit();
}
}
finally
{
session.close();
}
}
댓글에서 알 수 있듯이, 저는 stateful/stateless의 여러 조합을 시도해 보았습니다.session
. 아무 것도 안 됐어요.
나의dataSource
는 다음 URL을 가지고 있습니다.
<b:property name="jdbcUrl" value="jdbc:mysql://server:3306/db?autoReconnect=true&rewriteBatchedStatements=true" />
나의SessionFactory
<b:bean id="sessionFactory" class="class.that.extends.org.springframework.orm.hibernate3.LocalSessionFactoryBean" lazy-init="false" depends-on="dataSource">
<b:property name="dataSource" ref="phoenixDataSource" />
<b:property name="hibernateProperties">
<b:props>
<b:prop key="hibernate.dialect">${hibernate.dialect}</b:prop> <!-- MySQL5InnoDb-->
<b:prop key="hibernate.show_sql">${hibernate.showSQL}</b:prop>
<b:prop key="hibernate.jdbc.batch_size">500</b:prop>
<b:prop key="hibernate.jdbc.use_scrollable_resultset">false</b:prop>
<b:prop key="hibernate.cache.use_second_level_cache">false</b:prop>
<b:prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</b:prop>
<b:prop key="hibernate.cache.use_query_cache">false</b:prop>
<b:prop key="hibernate.validator.apply_to_ddl">false</b:prop>
<b:prop key="hibernate.validator.autoregister_listeners">false</b:prop>
<b:prop key="hibernate.order_inserts">true</b:prop>
<b:prop key="hibernate.order_updates">true</b:prop>
</b:props>
</b:property>
</b:bean>
프로젝트 전체 클래스가 확장되더라도LocalSessionFactoryBean
, 메소드를 재정의하지 않습니다(프로젝트 전체 메소드를 거의 추가하지 않음).
며칠 전부터 화가 납니다.저는 몇 개의 기사를 읽었지만 배치 삽입을 활성화하는 데 도움이 된 기사는 없었습니다.Spring context로 계측된 JUNit 테스트의 모든 코드를 실행합니다(따라서@Autowire
나의 반).제가 시도한 모든 시도들은 단지 많은 분리된 것들을 만들어낼 뿐입니다.INSERT
진술들
- https://stackoverflow.com/questions/12011343/how-do-you-enable-batch-inserts-in-hibernate
- https://stackoverflow.com/questions/3469364/faster-way-to-batch-saves-with-hibernate
- https://forum.hibernate.org/viewtopic.php?p=2374413
- https://stackoverflow.com/questions/3026968/high-performance-hibernate-insert
제가 무엇을 빠뜨리고 있나요?
쿼리가 다시 작성되고 있을 가능성이 있지만 Hibernate SQL 로그를 보면 알 수 없습니다.최대 절전 모드는 삽입 문을 다시 작성하지 않습니다. MySQL 드라이버가 삽입 문을 다시 씁니다.즉, Hibernate는 드라이버에 여러 개의 삽입문을 전송하고 드라이버는 이를 다시 작성합니다.따라서 Hibernate 로그에는 SQL Hibernate가 드라이버에 보낸 내용만 표시되고 드라이버가 데이터베이스에 보낸 SQL은 표시되지 않습니다.
MySQL의 프로파일을 활성화하여 이를 확인할 수 있습니다.연결 URL의 SQL 매개 변수:
<b:property name="jdbcUrl" value="jdbc:mysql://server:3306/db?autoReconnect=true&rewriteBatchedStatements=true&profileSQL=true" />
귀사와 유사한 예제를 사용하면 다음과 같이 출력할 수 있습니다.
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
Wed Feb 05 13:29:52 MST 2014 INFO: Profiler Event: [QUERY] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) duration: 1 ms, connection-id: 81, statement-id: 33, resultset-id: 0, message: insert into Person (firstName, lastName, id) values ('person1', 'Name', 1),('person2', 'Name', 2),('person3', 'Name', 3),('person4', 'Name', 4),('person5', 'Name', 5),('person6', 'Name', 6),('person7', 'Name', 7),('person8', 'Name', 8),('person9', 'Name', 9),('person10', 'Name', 10)
처음 10개의 라인은 Hibernate에 의해 기록되고 있지만 실제로 MySQL 데이터베이스로 전송되는 것은 아닙니다.마지막 줄은 MySQL 드라이버에서 가져온 것으로, 여러 값을 가진 단일 배치 삽입을 명확하게 보여주며 실제로 MySQL 데이터베이스로 전송됩니다.
언급URL : https://stackoverflow.com/questions/21530112/no-matter-what-i-cant-batch-mysql-insert-statements-in-hibernate
'source' 카테고리의 다른 글
ACF의 관련 게시물 수를 계산합니다. (0) | 2023.10.03 |
---|---|
까다로운 쿼츠.NET 시나리오 (0) | 2023.10.03 |
SQL 디코딩 Null 값 (0) | 2023.10.03 |
제스처 인식기 및 버튼 동작 (0) | 2023.10.03 |
MySQL에서 COUNT(id) 대 COUNT(*) (0) | 2023.10.03 |