【问题标题】:ways to improve the performance of this scenario提高此场景性能的方法
【发布时间】:2014-11-20 03:20:08
【问题描述】:

我有一张包含大量数据的地图(大约有 300,000 条记录)

并如下迭代,

    for (Map.Entry<String, List<ClassOBj>> entry : testMap
                    .entrySet()) {
 // send email with map keys as email'id
 // email content is populated from the list
// Perform a sql update to the column with the dyanamic value generated here with the email'id
                                  }

如上所述,我担心在for 循环内执行上述操作会导致性能问题。

更新:

场景是。我正在迭代一个包含大量数据的地图,

在迭代它时,我得到了用户 ID,我必须计算用户 ID。例如,考虑userid+some constants,这应该在数据库表中更新。

并且还应该与我的地图中的列表值一起添加到电子邮件内容中

所以我认为批量更新是不可能的,我的理解是否正确?

我应该遵循这种方法吗?或选择其他想法

【问题讨论】:

  • 理想的情况是不要进行昂贵的资源调用——比如循环中的数据库调用。使用中间集合存储数据并使用批量更新/批量删除。如果是Hibernate,则在循环后执行flush
  • 感谢您的建议。正如我已经解释了执行的操作。您可以看到批量更新无法在此处生成动态值来执行
  • 使用密钥创建地图以存储电子邮件和值 - 如果您需要任何动态值。不做 SQL 更新,而是添加到 map(虽然它是动态的,但所有可以更新的值都在 map 中)。您可以考虑可以直接更新的地图值。
  • 更新了我的问题。希望现在很清楚
  • 循环做两件事? 1 发送电子邮件和 2 更新数据库?你能添加一些你在循环中做什么的虚拟例子吗?

标签: java performance for-loop map


【解决方案1】:

我的建议是你可以使用Stored procedure。或者使用可以使用批量更新来代替这个。

更多关于sql batch update

【讨论】:

    【解决方案2】:

    您应该使用 jdbc 批量更新功能。

    在遍历 map 时,您将批处理添加到准备好的语句中。当您添加(例如)2000 条记录时,您调用 stmt.batchUpdate() 它将快速更新 2000 条不同的记录。

    您可以在此处查看一些示例:

    http://www.mkyong.com/jdbc/jdbc-preparedstatement-example-batch-update/

    第二件事 - 如果可以,请在每次批处理更新后提交事务。 300k 记录的事务对于您的数据库配置来说可能太多了。将此更新拆分为多个事务将具有更好的性能 - 但前提是您不能对所有记录进行事务。

    【讨论】:

      【解决方案3】:

      不要在每个循环中更新数据库。尝试在完成循环后更新数据库。

      有不同的方式来优化大型数据库更新。 其中最好的是

      • 将修改后的值插入到临时表中
      • 从临时表更新原表

      还可以使用基于线程的排队机制来发送您的电子邮件

      【讨论】:

      • 感谢您的回答。我已经更新了我的问题。希望现在很清楚。你能帮忙做一下高级设计吗
      • 写入临时表仍会妨碍性能。因为它涉及数据库调用以及另外将数据复制到原始表中。这不是必需的。
      • @NachiketKate 感谢您的建议。如果您有答案,请随时添加答案:)
      • 我认为您有一些选项分布在给定的答案中。
      【解决方案4】:

      我会做这样的事情。为操作准备数据。

      我想你正在更新一个像 user 这样的表,它应该有一个唯一的 Id。

      Map<String, String> emailIds = new HashMap<String, String>();
      Map<String, String> emails = new HashMap<String, String>();
      for (Map.Entry<String, List<ClassOBj>> entry : testMap.entrySet()) {
       -- DONOT DO THIS// send email with map keys as email'id
       -- DONOT DO THIS// email content is populated from the list
       -- DONOT DO THIS// Perform a sql update to the column with the dyanamic value generated here with the email'id
      emails.put(emailId, content);
      emailIds.put(id, emailId);
      
      }
      
      bulkEmailSend(emails);
      bulkEmailUpdate(emailIds);
      

      bulkEmailSendbulkEmailUpdate 是应编写的用于进行适当调用的方法。

      因此,使用批量电子邮件发送和批量 emailId 更新方法将值更新回数据库。

      【讨论】:

        【解决方案5】:

        我会尽量总结上面提到的所有优点。

        你的选择是,

        1. 尽可能使用多线程,但请记住,多线程会带来额外的内存成本(堆转储和应用程序将关闭)。
        2. 另一个不错的选择是使用批量更新,但同样批量更新会在数据库端花费更多锁定时间。所以要明智地使用它,因为线程将等待一个线程完成更新,因为更新将占用排他锁,即无法共享。

        3. 尝试线程与异步作业(特别是邮件) 尝试使用不同的服务器/进程进行邮件处理并将邮件请求异步发送到邮件服务器,这非常快,因为您的邮件生成任务由其他进程处理(在现实生活中邮件交换可以接受一些延迟,因此您的慢速邮件服务器是允许的,但是应用服务器不是。)

        4. 如果可能的话,将完整的数据库更新逻辑移动到数据库服务器上的存储过程中,这样可以节省大量时间 (经验法则:始终让 A 处理 A 设计/优化的任务,即 DB 设计用于比 pgming 语言更快的 DB 操作。)

        无法针对您的需求给出具体答案,但我希望这有助于改进它:)

        【讨论】:

        • 谢谢 :) 。我会尽量听从你的建议
        【解决方案6】:

        for 循环需要时间有两个原因。
        1) 个人电子邮件通过减少传输连接来改善它
        2) 个人提交通过

        改进它

        所以理想是同时处理两者,我建议批量 1000 个,然后玩数字

        例子

        int BATCH_SIZE = 1000
        conn = DriverManager.getConnection("username","password");
        conn.setAutoCommit(false);
        Statement stmt = conn.createStatement(
                ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_UPDATABLE);
        int count = 0;
        
        Map<String, String> emails_map = new HashMap(BATCH_SIZE)<String, String>;
        for (Map.Entry<String, List<ClassOBj>> entry : testMap
                .entrySet()) {
            String email = get_useremail();
            String const_val = do_magic(); // this is how you are computing some constant
            String body = construct_body();
        
            count++;
            String SQL = "YOUR UPDATE STATEMENT";
            stmt.executeUpdate(SQL);  
            emails_map.put(email, body); // can create 
            if (count % BATCH_SIZE == 0) {
                //commits all transcations
                conn.commit();
                //bulk send emails sending 
                //http://stackoverflow.com/questions/13287515/how-to-send-bulk-mails-using-javax-mail-api-efficiently-can-we-use-reuse-auth
        
                bulk_emails_send(emails_map)
            }
        
        }
        
        
        public void bulk_emails_send(Map<String, String> emails_map) {
            // Get the default Session object through your setting
            Session session = Session.getDefaultInstance(properties);
            Transport t = session.getTransport();
            t.connect();
            try {
                for (String email_id in emails_map) {
                    Message m = new MimeMessage(session);
                    //add to, from , subject, body
                    m.saveChanges();
                    t.sendMessage(m, m.getAllRecipients());
                }
            } finally {
                t.close();
            }
        }
        

        【讨论】:

        • do_magic()方法是什么,能否详细说明发送批量邮件和批量更新
        • do_magic 它是如何计算常量的,批量/批量更新已经存在,还添加了批量电子邮件
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-28
        • 2021-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多