shipmonk / doctrine-mysql-optimizer-hints

为 Doctrine 提供的定制 SQL 查询器,允许在不使用原生查询的情况下使用 MySQL 优化器提示

2.1.0 2024-08-08 13:24 UTC

This package is auto-updated.

Last update: 2024-09-02 11:22:44 UTC


README

此库提供了一种简单的方法,通过 MySQL 的优化器提示 将 SELECT 查询集成到使用 Doctrine 查询语言 编写的查询中,通过 自定义 SqlWalker。不再需要原生查询。

安装

composer require shipmonk/doctrine-mysql-optimizer-hints

示例用法

$result = $em->createQueryBuilder()
    ->select('u.id')
    ->from(User::class, 'u')
    ->andWhere('u.id = 1')
    ->getQuery()
    ->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
    ->setHint(OptimizerHintsHintHandler::class, ['SET_VAR(sql_mode=ONLY_FULL_GROUP_BY)'])
    ->getResult();

这将产生以下 SQL

SELECT /*+ SET_VAR(sql_mode=ONLY_FULL_GROUP_BY) */ u0_.id AS id_0
FROM user u0_
WHERE u0_.id = 1

请注意您放置的优化器提示,您基本上是在那里编写 SQL,但 MySQL 只在发生错误时产生警告。

用例

限制/扩展单个查询的最大执行时间

任何合理的应用程序都会使用一些全局的 max_execution_time 来避免查询运行数小时。但您可能希望为单个长时间运行的查询打破这一限制。通过 SET max_execution_time = 10000; 来做这样的事情是棘手的,因为您应该在查询结束后立即将其恢复到之前的值。这会导致围绕它的代码变得复杂,优化器提示免费为您做这件事

->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(OptimizerHintsHintHandler::class, ['MAX_EXECUTION_TIME(1000)'])

查询优化

有时,通过 强制使用某些索引 并不足以帮助 MySQL 优化器调整执行计划中表的顺序。连接顺序优化器提示 是通往这一目标的途径。最简单的用法是使用 JOIN_FIXED_ORDER() 强制表的顺序与您所写完全一致。

->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(OptimizerHintsHintHandler::class, ['JOIN_FIXED_ORDER()'])

测试不可见索引

当处理生产环境中的复杂查询优化时,您只能猜测您想出的新索引是否有帮助。从 MySQL 8.0 开始,您可以创建 不可见索引(这些由引擎维护但不使用)。但您可以为要测试的查询启用不可见索引

->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(OptimizerHintsHintHandler::class, ["SET_VAR(optimizer_switch = 'use_invisible_indexes=on')"])

扩大单个查询的 group_concat 限制

group_concat_max_len 的默认限制为 1024,但您可以将其设置得更大

->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(OptimizerHintsHintHandler::class, ["SET_VAR(group_concat_max_len = 4294967295)"])

与索引提示结合使用

自 2.0.0 版本起,您可以结合使用此库与 shipmonk/doctrine-mysql-index-hint

$result = $em->createQueryBuilder()
    ->select('u.id')
    ->from(User::class, 'u')
    ->andWhere('u.id = 1')
    ->getQuery()
    ->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
    ->setHint(OptimizerHintsHintHandler::class, ['MAX_EXECUTION_TIME(1000)'])
    ->setHint(UseIndexHintHandler::class, [IndexHint::force(User::IDX_FOO, User::TABLE_NAME)])
    ->getResult();