当涉及存储函数时,有没有办法提高 Hibernate 生成的查询的性能?

Posted

技术标签:

【中文标题】当涉及存储函数时,有没有办法提高 Hibernate 生成的查询的性能?【英文标题】:Is there a way to improve the performance of Hibernate-generated queries when stored functions are involved? 【发布时间】:2019-02-26 16:21:45 【问题描述】:

我有一个页面,它以分页方式在网格中显示一些记录。我有表my_table 和实体MyTable 链接到它:

@SuppressWarnings("serial")
@Entity
@Table(name="my_table")
@Inheritance(strategy=InheritanceType.JOINED)
public class MyTable extends BaseEntity implements Auditable, Serializable 
    //...
    private Integer myAttribute; //This does not exist in the table
    //...
    @Formula(value = "(myFunction(attr1, attr2))")
    public Integer getMyAttribute() 
        return myAttribute;
    

    public void setMyAttribute(Integer myAttribute) 
        this.myAttribute = myAttribute;
    
    //...

当我打算按“普通”字段查询时,一切正常,但是当我尝试按 myAttribute 过滤时,例如:

queryInput.addAndCriterion(Restrictions.eq("myAttribute", v));

例如 v 是一个值为 123 的 Integer,搜索将超时。如果我直接在 mysql 中运行存储的函数,那么它会立即执行。我认为这段代码对每个项目发送一个单独的请求,这可以解释问题。有没有办法确保我可以以高性能的方式按我的存储函数进行过滤(也许对存储函数的调用将生成到查询中)?我需要定义一个标准,指定对于每条记录,需要调用某个存储函数并传递attr1attr2,它们是记录的字段吗?

【问题讨论】:

是否允许查询非持久性字段?打开 SQL 日志记录,看看发生了什么。我会考虑使用计算字段定义数据库视图。而不是使用 @Formula,您可以使用 JPA @SecondaryTable 从此视图填充字段,并可以像任何其他字段一样搜索/排序。 @AlanHay 谢谢你的评论。这似乎是一个潜在的解决方案。如果允许,我会考虑创建一个视图。我需要查看是否允许我调用存储的函数。 您在 cmets 中提到您希望 Hibernate 生成什么查询。您应该找出它实际生成的查询。如果你足够快,你可以在它执行时查看SHOW FULL PROCESSLIST,或者你可以SET GLOBAL general_log=ON; 并在 MySQL 服务器的查询日志中观察查询(记得之后关闭日志)。 @AlanHay 如果我搜索析取项,其中条件 1 是快速的,条件 2 取决于公式,那么搜索执行得又快又好,所以问题是超时。 首先,向我们展示生成的 SQL 的样子。我们可以帮助您改善这一点。 (但当然,您必须向后工作才能弄清楚如何让 Hibernate 实现新代码。) 【参考方案1】:

正如下面评论中所指出的,这个答案并不能解决 Hibernate 故障排除的问题,但 OP 还是喜欢它。

答案如下...


查询任何函数,存储函数或内置函数,始终是表扫描。

例如,这将无法使用create_date 上的索引,即使存在一个索引:

SELECT * FROM MyTable WHERE MONTH(create_date) = 2

任何时候使用索引列作为函数的参数都是如此。

MySQL 5.7 及更高版本的解决方法是对该表达式使用generated column,然后索引生成的列。

ALTER TABLE MyTable
  ADD COLUMN created_month INT AS (MONTH(create_date)),
  ADD INDEX (created_month);

完成后,您可以查询created_month = 2,甚至可以查询原始表达式MONTH(create_date) = 2,它将使用索引。

很遗憾,您只能通过内置 MySQL 函数使用此功能。

https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html 说:

生成的列表达式必须遵守以下规则。如果表达式包含不允许的构造,则会发生错误。

不允许存储函数和用户定义函数。

另一种解决方案是让您创建一个新的具体列来存储存储函数的结果,假设该值由其参数确定,并且不依赖于其他表中的数据状态。

ALTER TABLE MyTable
  ADD COLUMN myAttribute INT,
  ADD INDEX (myAttribute);

CREATE TRIGGER att_ins BEFORE INSERT ON MyTable 
FOR EACH ROW SET NEW.myAttribute = MyFunction(NEW.attr1, NEW.attr2);

CREATE TRIGGER att_upd BEFORE UPDATE ON MyTable 
FOR EACH ROW SET NEW.myAttribute = MyFunction(NEW.attr1, NEW.attr2);

然后您将查询新列而不是表达式。

这有点麻烦,但这是对存储函数的结果进行索引查找的唯一方法。

【讨论】:

我已经通过存储的函数限制 0、25(即时)或限制 0、1000(在 2.49 秒内执行)从给定表中进行了选择测试。问题不在于存储的函数本身。如果我手动编写查询,它会很快。问题是,当我通过 Hibernate 执行此操作时,与我希望它生成的查询相比,它非常慢。我希望 Hibernate 生成一个查询,例如 select c1, ..., cn from my_table where myFunction(attr1, attr2);向该表添加列是不受欢迎的选择,因为 my_table 已经有许多列和记录,并且值发生了变化。 我的意思是值变化​​非常频繁。所以,我有 my_table,它将在 where 子句中有一个存储函数,这将取决于 myFunction。 myFunction 依赖于 my_other_table 和 my_yet_another_table,两者都比 my_table 小很多,可以快速查询。正如您准确指出的那样,生成的列也不是一种选择。 "这有点麻烦,但这是根据存储函数的结果进行索引查找的唯一方法。"我的问题是我无法生成我想要的查询(由于我对 Hibernate 的了解非常有限),此时不需要索引查找。将来如果实际查询也会变慢,我将需要寻找替代解决方案,但此时如果 Hibernate 可以生成我需要的查询,它将解决我的问题。因此,这个答案并没有解决问题,但它是一个高质量的答案,值得一票。

以上是关于当涉及存储函数时,有没有办法提高 Hibernate 生成的查询的性能?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在执行存储过程或函数时提前确定要返回的结果集的数量?

当我们在 Xcode 中执行程序时,有没有办法查看函数调用的顺序?

当 Django Admin Popup(添加、更新、删除)完成时,有没有办法调用 Javascript 函数?

当用户在 android 应用程序中执行某个操作时,有没有办法知道所有函数被调用?

当查询被填充时,有没有办法在 Django 模板中呈现大型查询集?

有没有办法初始化一个不涉及编写构造函数的新结构变量?