【问题标题】:How to improve my software project's speed?如何提高我的软件项目的速度?
【发布时间】:2010-05-18 19:02:44
【问题描述】:

我正在和我的同学用 Java 做一个学校软件项目。 我们将信息存储在远程数据库中。

当我们启动应用程序时,我们从数据库中提取所有信息并将其转换为对象以在我们的应用程序中使用(使用 java sql 语句)。 在应用程序中,我们编辑其中一些对象,然后当我们退出应用程序时 我们使用 Hibernate 保存或更新数据库中的信息。

如您所见,我们不使用 Hibernate 来获取信息,我们只是将其用于保存和更新。

我们有 2 个,但非常相似的问题。 在数据库中加载对象(当我们启动应用程序时)和保存对象(使用 Hibernate)(当关闭应用程序时)花费了太多时间。 而且我们的项目不是一个巨大的企业应用程序,它是一个很小的应用程序,我们只是管理一些学生、老师、家庭作业和测试。所以我们的数据库也非常非常小。 我们如何提高性能?

稍后编辑:如果我们使用本地数据库,它会运行得非常快,但它在远程数据库上运行会很慢

【问题讨论】:

  • 让这成为你的一个教训,在本地数据库上开发是一种糟糕的做法。您应该在将其投入生产之前很久就知道此类问题,如果您一直在运行网络管道,您就会知道。您应该在尽可能类似于生产环境的环境中进行开发(包括权限、服务器规格、相同版本的软件(我见过人们使用新版本的软件进行开发,然后想知道为什么它不在具有旧版本的生产环境中工作)、与生产环境大小相同的数据库等)

标签: java database performance hibernate


【解决方案1】:

您是说您将整个数据库加载到内存中然后对其进行操作吗?如果是这种情况,您为什么不简单地将数据库用作存储设备,并根据需要进行查找和操作(如果您喜欢使用 Hibernate,如果您不喜欢使用其他方法)?关键是确保您使用连接池,因为这将减少连接时间。

如果您正在这样做,那么您也可能遇到内存问题 - 首先,通过不在内存中缓存整个数据库,您将减少内存并将网络负载从开始/结束分散到需要发生的时间。

【讨论】:

  • 根据您的编辑,我猜这部分是因为机器和远程数据库之间的传输时间 - 即在启动/关闭时传输所有数据。
【解决方案2】:

这两句话对我来说是危险信号:

当我们启动应用程序时,我们会拉取 数据库中的所有信息 并将其转换为要使用的对象 在我们的应用程序中(使用 java sql 声明)。在我们编辑的应用程序中 其中一些对象,然后当我们 退出我们保存或更新的应用程序 数据库中的信息使用 休眠。

是否有要求在启动时将所有信息从数据库加载到内存中,或者为什么要等到关机才能将更改保存回数据库?

如果不是,我建议更改设计。如果您已经为 DB 中的表提供了 Hibernate 映射,我会为您的所有 CRUD(创建、读取、更新、删除)操作使用 Hibernate。而且,我只会根据需要加载应用中每个页面所需的数据。

如果您此时无法进行这种设计更改,我认为您必须仔细研究如何管理数据库连接。你在使用连接池吗?您是否打开多个连接?忘记释放它们?

还有一些东西要看。您如何使用 Hibernate 将实体保存到数据库?您是否对每个执行 getHibernateTemplate().get 然后对每个执行 entity.save 或 entity.update ?如果是这样,这意味着您还导致 Hibernate 在保存或更新之前为每个数据库对象运行选择查询。因此,本质上,您将加载每个数据库对象两次(一次在程序开始时,一次在保存之前)。要查看是否发生了这种情况,您可以打开 show_sql property 或使用 P6Spy 来查看 Hibernate 正在运行的确切查询。

【讨论】:

  • 如果我是他们的导师,他们会因为使用如此糟糕的架构而失败。
  • @HLGEM:这就是上课的全部意义——在进入行业之前了解这些类型的问题。我宁愿人们在学校里遇到他们,也不愿在“现实世界”中遇到他们——在“现实世界”中看到太多这种确切行为的例子
  • 嗯,是的,如果是我的项目就不会这样了,但是一些同学发现hibernate很麻烦,这就是为什么我们没有懒惰的获取,巫婆会解决一个问题。
  • 是的,当你在一个小组中工作时,这种事情很棘手。
  • @Blitzkr1eg - 这个问题在现实世界中也不一定会改变。我仍然必须向人们解释为什么经过验证的 X、Y 或 Z 技术是一件好事(tm)。 (源代码控制就是其中之一……啊!)
【解决方案3】:

对于你正在做的事情,你最好序列化你的对象并将它们写到一个平面文件中。

但是,出于 aperkins 给出的所有原因,您应该根据需要直接从数据库中读取/更新对象,而不是一次全部读取/更新对象。

另外,考虑一下如果您的应用程序崩溃会发生什么?如果您的所有更新都只保存在内存中,直到应用程序关闭,如果应用程序意外关闭,一切都会丢失。

【讨论】:

    【解决方案4】:

    从远程数据库服务器加载所有内容与从本地数据库服务器加载所有内容的区别在于网络延迟/管道大小。网络是一个比其他任何东西都小得多的管道。两个问题:首先,我们真正在谈论多少数据?第二,你的网速是多少? 10/100/1000?由于从网络协议到实际查询本身的各种因素,管道大小的 10% 到 20% 之间的数字将是开销。

    正如其他人所说,您的架构方式通常在“不要做”列表中名列前茅。启动时,只提取足够的数据来初始化应用程序。当用户完成它时,拉出你需要的那个任务。

    您拉动所有东西的唯一时间是它们在断开连接状态下工作。在这种情况下,您仍然不会将所有内容作为对象加载到应用程序中,您只需从本地数据存储中工作,该数据存储会不时与远程服务器同步。

    【讨论】:

    • 回答您的问题: 1. 数据库很小,~120 KB。那是 KilloBytes 2. 服务器的速度是 100 Mbits,就像 12MBytes 所以问题不在这里。
    • @Blitzkr1eg:仅仅因为它有一个 100Mb 的 NIC,并不意味着它实际上是以那个速率传输的。取决于许多其他因素(网络健康状况、驱动程序等),它的传输速度可能仅为 1Mb/秒。
    【解决方案5】:

    该项目非常完整。我们现在不能对其进行大规模重构。 保存时我尝试为休眠使用二级缓存。 EhCacheProvider.

    在 hibernate.xml 中: net.sf.ehcache.hibernate.EhCacheProvider

    我已经对缓存进行了配置,ehcache.xml:

    我已将 cache.jar 放在项目构建路径中 我已经为每个类设置了休眠属性并在映射中设置。 但是这个缓存似乎没有效果。我不知道它是否有效(如果它被使用)。

    【讨论】:

    • 如果您不使用 Hibernate 加载数据库对象,Ehcache 不会帮助您。它只会缓存通过 Hibernate 加载的实体,如果您再次请求相同的实体,它会阻止您往返到数据库。顺便说一句,这里有一些代码用于打印 1 个 ehcache.xml 条目的 ehcache 统计信息: import org.hibernate.SessionFactory;导入 org.hibernate.stat.SecondLevelCacheStatistics; SecondLevelCacheStatistics settingsStatistics = sessionFactory .getStatistics().getSecondLevelCacheStatistics(fullyQualEntityImplName);
    【解决方案6】:
    1. 尽量减少 SQL 查询的数量,因为每个查询都有自己的开销。
    2. 您可以启用数据库压缩,当有大量数据时,这应该会加快处理速度。
    3. 也许您多次连接到数据库?
    4. 检查远程数据库服务器的 ping 时间 - 可能是问题所在。

    【讨论】:

      【解决方案7】:

      由于您的应用程序在远程数据库服务器上运行时速度很慢,我认为性能损失是由于:

      对于休眠,您可以使用它的批处理功能并调整hibernate.batch_size

      在所有情况下,尤其是当您无法重构代码库的较大部分时,请使用 profiler(方法时间或 sql 查询)来查找瓶颈。我敢打赌,您会发现数千个查询,每个查询需要 10 毫秒 RTT),这些查询可以合并为一个。

      【讨论】:

      • 当我们启动应用程序时,我们打开一个连接。执行所有查询并关闭连接。
      【解决方案8】:

      您可以查看的其他一些内容:

      • 您可以为 JVM 分配更多内存
      • 使用jconsole 工具调查瓶颈所在。

      【讨论】:

        【解决方案9】:

        为什么你没有两个独立的线程?

        线程 1 将一一加载您的对象。 线程 2 将在加载对象时对其进行处理。

        您的应用在启动时看起来更具交互性。

        【讨论】:

        • 是的,这就是我想到的解决方案。现在我只是无法实现它。
        • 为什么你不能实现它?创建线程实际上并不像你认为的那么大的重构过程,特别是在 java 中,只要确保同步没有问题
        • 我是用线程做的,至少是为了节省。它远不是一个好的解决方案,但它的响应速度更快。
        【解决方案10】:

        复习基础知识永远不会有坏处:

        提高速度意味着减少时间(显然),为此,您会发现需要大量时间但可以被消除或替换为使用更少时间的活动。我所说的activity 的意思几乎总是一个函数调用、方法调用或属性调用,在特定的代码行 上为特定目的执行。如果可以调用 I/O,也可以调用计算,或两者兼而有之。如果其目的不是必需的,则可以对其进行优化。

        许多人使用分析器试图找到这些浪费时间的代码行,但大多数分析器错过了目标,因为他们查看的是函数而不是行,他们在 I/O 期间进入睡眠状态,并且他们担心“自我时间” ”。

        更多的人试图猜测可能是什么问题,或者他们要求其他人猜测,例如通过询问 SO。这样的猜测,就猜测的性质而言,有时是正确的——更多时候是错误的,但人们仍然在其中投入时间和资源。

        有一种非常简单的方法可以确定,无需猜测,可以有效优化哪些内容,here is one way to do it in Java

        【讨论】:

          【解决方案11】:

          感谢您的回答。他们非常有帮助。 我们完全解决了这个问题:

          重构了 LOAD 代码。现在它使用 Hibernate 和 Lazy Fetching。 重构了 SAVE 代码。现在它只保存修改过的数据以及修改后的数据。这样一来,我们就没有大结局了。

          我对这一切的进展感到惊讶。我们必须编写的新代码数量非常少。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-12-20
            • 2018-08-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-20
            • 1970-01-01
            相关资源
            最近更新 更多