简体   繁体   English

交易中春季的性能优化

[英]performance optimization in spring with transaction

I am making one enterprise application. 我正在制作一个企业应用程序。 In which i have task to parse one csv file that contain suppose 50,000 k records. 在其中我有任务来解析一个包含50,000 k条记录的csv文件。 This csv file supplied by the end user at the time of registration. 最终用户在注册时提供的此csv文件。 I made one program to parse the csv file into java object and then save all these object into database. 我编写了一个程序,将csv文件解析为java对象,然后将所有这些对象保存到数据库中。 This file contain mobile number and before save the csv file as a java object it firstly validated for mobile number. 该文件包含手机号码,并且在将csv文件另存为java对象之前,首先对其进行手机号码验证。 either it exist on database or not. 它是否存在于数据库中。 It it is exist then it fails the validation and stop the exection. 如果存在,则验证失败并停止执行。 Now Suppose two different user called A and B send a request for registration.Controller listen this request by the following code. 现在假设两个不同的用户A和B发送一个注册请求,控制器通过以下代码侦听该请求。 controller layer 控制器层

@Transactional
    @RequestMapping(value = "/saveCsvData")
    public ModelAndView saveVMNDataFromCsv(@ModelAttribute("vmn") @Valid VMN vmn,   BindingResult result, HttpSession session, HttpServletRequest request) {

    String response = parseCsvService.parseAndPersistCsv(vmn.getVmnFile(),vmn.getNumberType(), request);
}

on the controller method i have user @Transactional annotation so that this method can complete its work completely.This controller call the helper call to read line by line from csv file and put them into java object.After getting the list of VMN Object with the help of loop i call service method which again call dao method for ecah line. 在控制器方法上,我有用户@Transactional批注,以便此方法可以完全完成其工作。此控制器调用helper调用,以从csv文件逐行读取并将其放入Java对象中。循环帮助我调用服务方法,该方法再次为ecah行调用dao方法。 helper class 助手班

public String parseAndPersistCsv(MultipartFile csvFile,String numberType, HttpServletRequest request){
            List<VMN> vmnList = new ArrayList<VMN>();
                if(save){
                    for(VMN vmn : vmnList){
                    System.out.println("Remote Host :" + request.getRemoteHost());
                    System.out.println("Remote Add :" + request.getRemoteAddr());

                    vmnService.saveVmn(vmn, numberType); 
                    }
                    response = constantService.getSuccess();
                }
}

service Layer 服务层

public String saveVmn(final VMN vmn, String numberType) {
    vmnService.saveVmn(vmn, numberType); 
}

At Dao Layer method looks like this. 在道层方法看起来像这样。 This method insert record into multiple tables as it can be seen in method code. 在方法代码中可以看到,此方法将记录插入到多个表中。

Dao Layer 道层

public String saveVmn(final VMN vmn, String numberType) {
String result = "error";
try {
final StringBuffer sql = new StringBuffer();
sql.append(constantService.getInsertInto());
sql.append(VMNTableSingleton.getInstance().getTableName());
sql.append(" (");
sql.append(VMNTableSingleton.getInstance().getVmnNo());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getNumberType());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getOperator());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getCircle());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getBuyingPrice());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getRecurringPrice());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getCreationDate());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getUpdationDate());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getActive());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getStatus());
sql.append(")");
sql.append(constantService.getValues());
sql.append(" (?,?,?,?,?,?,?,?,?,?)");
logger.info("Saving Vmn..." + sql);
KeyHolder keyHolder = new GeneratedKeyHolder();
int response = jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
ps.setObject(1, vmn.getVmnNo());
ps.setObject(2, vmn.getNumberType());
ps.setObject(3, vmn.getOperator());
ps.setObject(4, vmn.getCircle());
ps.setObject(5, vmn.getBuyingPrice());
ps.setObject(6, vmn.getBuyingPrice());
ps.setObject(7, new Date());
ps.setObject(8, new Date());
ps.setObject(9, true);
ps.setObject(10, vmn.getStatus());
return ps;
}
}, keyHolder);
logger.info("Saved Successfully");
if (response == 1) {
if(vmn.getMappedVmn() != null){
Long vmnId = keyHolder.getKey().longValue();
if(vmnId > 0){
StringBuffer mappedsql = new StringBuffer();
mappedsql.append(constantService.getInsertInto());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getTableName());
mappedsql.append(" (");
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getDidId());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getMappedId());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getType());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getCreationDate());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getModifiedDate());
mappedsql.append(")");
mappedsql.append(constantService.getValues());
mappedsql.append(" (?,?,?,?,?)");
logger.info("Mapping... DID with VMN");
int mappedresponse = jdbcTemplate.update(mappedsql.toString(),
new Object[] {vmn.getMappedVmn().getVmnId(),vmnId ,vmn.getNumberType(),new Date(),new Date()});
logger.info("Mapped Successfully");
if(mappedresponse == 1){
stringBuffer updatesql = new StringBuffer();
updatesql.append(constantService.getUpdate());
updatesql.append(VMNTableSingleton.getInstance().getTableName());
updatesql.append(constantService.getSet());
updatesql.append(VMNTableSingleton.getInstance().getStatus());
updatesql.append(constantService.getEqual());
updatesql.append(constantService.getQuestionMark());
updatesql.append(constantService.getComma());
updatesql.append(VMNTableSingleton.getInstance().getAllocationDateTime());
updatesql.append(constantService.getEqual());
updatesql.append(constantService.getQuestionMark());
updatesql.append(constantService.getWhere());
updatesql.append(VMNTableSingleton.getInstance().getVmnId());
updatesql.append(constantService.getEqual());
updatesql.append(constantService.getQuestionMark());
logger.info("Updating Vmn..." + updatesql);
jdbcTemplate.update(updatesql.toString(),
new Object[] { constantService.getMapped(),new Date(), vmn.getMappedVmn().getVmnId()});
logger.info("Saved Successfully");
}
}
}
result = "success";
} else {
result = "error";
}
} catch (Exception ex) {
}
return result;
}

Now when i have sent to request toward controller for registration. 现在,当我向控制器发出请求注册时。 then i have seen on console both thread accessing method one by by one. 然后我在控制台上看到了两种线程访问方法的一个接一个。 By this code inside for loop. 通过此代码内的for循环。

System.out.println("Remote Host :" + request.getRemoteHost()); System.out.println(“远程主机:” + request.getRemoteHost()); //10.0.0.0114 System.out.println("Remote Add :" + request.getRemoteAddr());//110.0.0.115 //10.0.0.0114 System.out.println(“ Remote Add:” + request.getRemoteAddr()); // 110.0.0.115

Not this can be dirty read problem because if one thread is reading data then other might be inserting. 这不是很脏的读取问题,因为如果一个线程正在读取数据,则可能正在插入另一个线程。 so to resolve this i have used sync block. 因此,为解决此问题,我使用了同步块。 Like this 像这样

synchronized (this) {
                if(save){
                    for(VMN vmn : vmnList){
                    System.out.println("Remote Host :" + request.getRemoteHost());
                    System.out.println("Remote Add :" + request.getRemoteAddr());

                    vmnService.saveVmn(vmn, numberType); 
                    }
                    response = constantService.getSuccess();
                }               
            }

Now my question is, is this is the right way to do this or it can be done in some other way too. 现在我的问题是,这是执行此操作的正确方法,还是可以通过其他方法完成。

this can be dirty read problem because if one thread is reading data then other might be inserting 这可能是脏读取问题,因为如果一个线程正在读取数据,那么其他线程可能正在插入

If your concern is visibility then I think this can be taken care of by transaction isolation configured ( usually READ COMMITTED by default) where a thread can only see the committed data and the data it is trying to update. 如果您关注的是可见性,那么我认为可以通过配置事务隔离(默认情况下通常为“ READ COMMITTED”)来解决,其中线程只能看到已提交的数据和它试图更新的数据。

Also you should consider using batchUpdate method of JDBCTemplate which uses jdbc batching feature. 您还应该考虑使用JDBCTemplate的batchUpdate方法,该方法使用jdbc批处理功能。 Read and check the existence of number in batches and then update in batches. 批量读取并检查编号是否存在,然后批量更新。

Typically such features are handled well by Spring Batch framework. 通常, Spring Batch框架可以很好地处理此类功能。 Your use case fits the description of a Spring job which can utilize inbuilt csv readers, processors and chunk based processing to achieve high volume processing of data. 您的用例符合Spring作业的描述,该作业可以利用内置的csv读取器,处理器和基于块的处理来实现大量数据处理。 The job can be triggered from UI using Spring MVC controllers. 可以使用Spring MVC控制器从UI触发作业。 For more details you can take a look here . 有关更多详细信息,请在此处查看

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM