我有一个rest服务,该服务接受具有400_000条记录的xml,每个记录包含以下字段:code,type,price
。
在数据库(MySql)中,我有一个名为PriceData的表,该表具有2_000_000行。剩下的目的是:根据XML中的code,type
从数据库中选择所有PriceData,用XML中的价格替换每个PriceData的价格,如果没有这个code,type
的PriceData,则使用提供的价格创建新价格。
现在它的工作方式如下:从数据库中选择一个PriceData到XML中的第一个记录,设置新价格或创建新PriceData,保存PriceData,这些步骤重复400_000次(大约需要5分钟)。
我想加快这个过程。
第一次尝试:
从PriceData中逐步选择1000个元素,并在选择所有元素时进行更新:
码:
private void updateAll(final List<XmlData> prices/*data from xml*/) {
int end= 1000;
int begin= 0;
final List<PriceData> models = new ArrayList<>();
while(end != prices.size() || begin !=end){
models.addAll(dao.findByPrices(prices.subList(begin,end)));
begin = end;
end +=1000;
}
final Map<String,XmlData> xmlData= prices.stream()
.collect(Collectors.toMap(this::keyForPriceDate,e->e));
final Map<String,PriceData> modelMap = models.stream()
.collect(Collectors.toMap(this::keyForRowModel,e->e));
final List<PriceData> modelsToSave = new ArrayList<>();
for(final String key : xmlData.keySet()){
final XmlData price = xmlData.get(key);
PriceData model = modelMap.get(key);
if(model == null){
model = onEmptyPriceData(price);
}
model.setPrice(price.getPrice());
modelsToSave.add(model);
}
modelService.saveAll(modelsToSave);
}
我将两个列表转换为地图,以了解PriceData是否存在(xmlData和modelMap的键创建为
(code+type)
)findByPrices方法以以下格式创建查询
select * from PriceData where (code =123 and type ='qwe') or (...)//and this `Or` repeats 1000 times
现在需要2分钟。
第二次尝试:
Select all PriceData from db (2 millions)
并使用上面的算法
需要3分钟。第一次尝试会更好,但是将来我的休息时间可能会达到500_000,我想知道在这种情况下哪种尝试会更好,或者也许有更好的方法可以完成此任务。
我的选择方法
public List<PriceData> findBy(final List<XmlData> selectData) {
final StringBuilder query = new StringBuilder("SELECT * from PriceData ");
query.append("WHERE \n");
final Iterator<PriceRowSelectData> selectDataIterator = selectData.iterator();
while(selectDataIterator.hasNext()){
final PriceRowSelectData data = selectDataIterator.next();
query.append("( \n")
.append("productCode = "+ data.getProductId()+" \n")
.append(" AND type = "+ data.getPriceind()+" \n")
.append(" ) \n");
if(selectDataIterator.hasNext()){
query.append("OR \n");
}
}
final SearchResult<PriceRowModel> searchRes = search(query.toString());
/*
Here i use custom mapper that map list of result to my object
*/
return searchRes.getResult();
}
最佳答案
您应该将MySQL INSERT ... ON DUPLICATE KEY UPDATE
语句与JDBC批处理结合使用。当然,这假定code,type
是主键,或者至少是唯一索引。
private void updateAll(final List<XmlData> prices) throws SQLException {
String sql = "INSERT INTO PriceData (code, type, price)" +
" VALUES (?,?,?)" +
" ON DUPLICATE KEY" +
" UPDATE price = ?";
try (PreparedStatement stmt = this.conn.prepareStatement(sql)) {
int batchSize = 0;
for (XmlData price : prices) {
if (batchSize == 1000) { // flush batch every 1000
stmt.executeBatch();
batchSize = 0;
}
stmt.setInt (1, price.getCode());
stmt.setString (2, price.getType());
stmt.setBigDecimal(3, price.getPrice());
stmt.setBigDecimal(4, price.getPrice());
stmt.addBatch();
batchSize++;
}
if (batchSize != 0)
stmt.executeBatch();
}
}
您可以旋转批处理大小,但不进行刷新将占用大量内存。我认为每批1000条语句是不错的,但是我没有数字支持。