You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MariaDB 10.3迁移升级至10.5后JOIN查询异常缓慢求助

问题:MariaDB升级后JOIN查询性能骤降,索引未被正确利用

将运行MariaDB 10.3.27-MariaDB-0+deb10u1的Galera集群数据库迁移至新硬件并升级到10.5.18-MariaDB-0+deb11u1-log版本后,使用mysqldump导出导入的所有JOIN查询速度极慢。示例查询如下:

SELECT *
  FROM claim_notes
         INNER JOIN claims
                 ON claim_notes.claim_id = claims.id
         INNER JOIN users
                 ON claim_notes.user_id = users.id
 WHERE claim_notes.email_queue = 1

该查询在旧集群耗时0.7秒,新集群则需3分51秒。新旧环境的数据库Schema(含所有索引)及数据集完全一致,排除查询优化不佳或数据集差异导致的问题。通过EXPLAIN发现优化器执行计划差异显著:

旧环境执行计划

+------+-------------+-------------+--------+------------------------------------------------------+-------------+---------+---------------------------------------+------+-------+
| id   | select_type | table       | type   | possible_keys                                        | key         | key_len | ref                                   | rows | Extra |
+------+-------------+-------------+--------+------------------------------------------------------+-------------+---------+---------------------------------------+------+-------+
|    1 | SIMPLE      | claim_notes | ref    | claim_id,email_queue,user_id,claim_type_user_dtstamp | email_queue | 1       | const                                 |    3 |       |
|    1 | SIMPLE      | users       | eq_ref | PRIMARY                                              | PRIMARY     | 4       | guido_commercial.claim_notes.user_id  |    1 |       |
|    1 | SIMPLE      | claims      | eq_ref | PRIMARY                                              | PRIMARY     | 4       | guido_commercial.claim_notes.claim_id |    1 |       |
+------+-------------+-------------+--------+------------------------------------------------------+-------------+---------+---------------------------------------+------+-------+
3 rows in set (0.001 sec)

新环境执行计划

+------+-------------+-------------+------+------------------------------------------------------+-------------+---------+-------+------+------------------------------------+
| id   | select_type | table       | type | possible_keys                                        | key         | key_len | ref   | rows | Extra                              |
+------+-------------+-------------+------+------------------------------------------------------+-------------+---------+-------+------+------------------------------------+
|    1 | SIMPLE      | claims      | ALL  | PRIMARY                                              | NULL        | NULL    | NULL  | 1    |                                    |
|    1 | SIMPLE      | users       | ALL  | PRIMARY                                              | NULL        | NULL    | NULL  | 1    | Using join buffer (flat, BNL join) |
|    1 | SIMPLE      | claim_notes | ref  | claim_id,email_queue,user_id,claim_type_user_dtstamp | email_queue | 1       | const | 5    | Using where                        |
+------+-------------+-------------+------+------------------------------------------------------+-------------+---------+-------+------+------------------------------------+
3 rows in set (0.000 sec)

所有关联字段(如claim_notes.claim_id、claims.id等)均已建立索引,但新环境中2个表未使用索引。已尝试设置eq_range_index_dive_limit为0但无效。相关表结构(省略无关字段)如下:

CREATE TABLE `claims` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `updated` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `created` timestamp NOT NULL DEFAULT current_timestamp(),
  `amount` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=173985 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

CREATE TABLE `claim_notes` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `claim_id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL,
  `email_queue` tinyint(4) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `claim_id` (`claim_id`),
  KEY `email_queue` (`email_queue`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=26559123 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `active` tinyint(4) DEFAULT NULL,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `active` (`active`)
) ENGINE=InnoDB AUTO_INCREMENT=576 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

解决方案

1. 重新生成表统计信息

mysqldump导入后,统计信息可能不完整或不准确,导致优化器误判执行计划。执行以下语句更新统计信息:

ANALYZE TABLE claims, users, claim_notes;

2. 调整优化器参数,禁用BNL Join

MariaDB 10.4+默认启用Block Nested Loop(BNL)Join,当统计信息不准确时,优化器可能选择低效的BNL方式。临时关闭测试:

SET SESSION optimizer_switch='block_nested_loop=off';

若测试有效,可在配置文件(my.cnf/my.ini)中添加以下配置永久生效:

optimizer_switch='block_nested_loop=off'

3. 强制指定执行顺序(临时应急方案)

使用STRAIGHT_JOIN强制优化器按照旧环境的表顺序执行查询,确保利用主键索引:

SELECT *
FROM claim_notes
STRAIGHT_JOIN claims ON claim_notes.claim_id = claims.id
STRAIGHT_JOIN users ON claim_notes.user_id = users.id
WHERE claim_notes.email_queue = 1;

4. 验证字符集与排序规则一致性

确认所有表的字符集和排序规则完全一致,避免隐性转换导致索引失效:

SHOW CREATE TABLE claims;
SHOW CREATE TABLE users;
SHOW CREATE TABLE claim_notes;

确保CHARSET和COLLATE字段完全匹配。

5. 检查InnoDB缓冲池配置

新硬件的innodb_buffer_pool_size若设置过小,会导致索引无法缓存,影响查询性能。查看当前配置:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

对于专用数据库服务器,建议设置为物理内存的50%-70%,修改后重启服务生效。


内容的提问来源于stack exchange,提问作者Nick Coons

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:48:13