我目前正在将 MariadDB 服务器从 10.3 (10.3.38-MariaDB-0ubuntu0.20.04.1) 升级到 10.9 (10.9.3-MariaDB-1:10.9.3+maria~ubu2004-log)
我已经在本地运行 10.9 版本一段时间,为将其放入生产环境做准备。当我一直在努力优化一些较大的查询时,我注意到在本地生产中的某些情况下没有使用索引,但实际上是这样。
据我了解,这是这些版本号和一些设置之间的变化,以及引擎试图“更智能”地了解使用优化的成本与仅继续进行不使用索引的表扫描的成本。很多谷歌搜索已经产生了关于以下内容的帖子:
eq_range_index_dive_limit, use_stat_tables, sort_buffer_size
我可能有点守旧,但是当我运行 EXPLAIN 时,我看到可能的键,但没有使用,这表明存在某种问题。假设 ANALYZE TABLE 的任何变体都没有导致索引开始被使用。升级后,我在测试生产机器上观察到相同的行为,因为索引使用情况与我在本地看到的内容重复。
我关心的一篇文章是这样一种情况:查询的实际结果没有被引擎很好地“测量”,强制索引是唯一的选择。这就是我担心的 - 在生产负载下将 10.9 放入我的生产环境(这对我来说很难进行可靠的模拟)并看到速度下降。
我该怎么办:
我想要一些关于你们中的一些人如何处理这个问题的反馈,或者甚至是关于调整以获得更多索引使用的建议?如果使用索引不如表扫描,我真的会遇到速度变慢的情况吗?
FWIW,我已经上下调整了上面这三个服务器选项的设置值,并且没有任何改变该简单 SELECT 语句的索引使用...
感谢您的输入,上下文如下。
MariaDB [pweb]> EXPLAIN extended select * from `accounting_transactions` where `accounting_transactions`.`cr_account` = 'f7d78ef5-ca59-44d1-9d67-70a83960f473' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: accounting_transactions
type: ALL
possible_keys: accounting_transactions_cr_account_index
key: NULL
key_len: NULL
ref: NULL
rows: 532030
filtered: 35.63
Extra: Using where
1 row in set, 1 warning (0.002 sec)
MariaDB [pweb]> EXPLAIN extended select * from `accounting_transactions` force index (accounting_transactions_cr_account_index ) where `accounting_transactions`.`cr_account` = 'f7d78ef5-ca59-44d1-9d67-70a83960f473'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: accounting_transactions
type: ref
possible_keys: accounting_transactions_cr_account_index
key: accounting_transactions_cr_account_index
key_len: 144
ref: const
rows: 189558
filtered: 100.00
Extra: Using index condition
1 row in set, 1 warning (0.001 sec)
MariaDB [pweb]> SELECT VERSION();
+-------------------------------------------+
| VERSION() |
+-------------------------------------------+
| 10.9.3-MariaDB-1:10.9.3+maria~ubu2004-log |
+-------------------------------------------+
1 row in set (0.001 sec)
MariaDB [pweb]> show create table accounting_transactions \G
*************************** 1. row ***************************
Table: accounting_transactions
Create Table: CREATE TABLE `accounting_transactions` (
`id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`event_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`amount` decimal(10,2) NOT NULL,
`dr_account` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`cr_account` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `accounting_transactions_event_id_index` (`event_id`),
KEY `accounting_transactions_dr_account_index` (`dr_account`),
KEY `accounting_transactions_cr_account_index` (`cr_account`),
CONSTRAINT `accounting_transactions_cr_account_foreign` FOREIGN KEY (`cr_account`) REFERENCES `accounting_accounts` (`id`),
CONSTRAINT `accounting_transactions_dr_account_foreign` FOREIGN KEY (`dr_account`) REFERENCES `accounting_accounts` (`id`),
CONSTRAINT `accounting_transactions_event_id_foreign` FOREIGN KEY (`event_id`) REFERENCES `accounting_events` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
1 row in set (0.002 sec)
MariaDB [pweb]> SHOW variables LIKE '%query_cache%';
+------------------------------+----------+
| Variable_name | Value |
+------------------------------+----------+
| have_query_cache | YES |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 16777216 |
| query_cache_strip_comments | OFF |
| query_cache_type | OFF |
| query_cache_wlock_invalidate | OFF |
+------------------------------+----------+
7 rows in set (0.002 sec)
MariaDB [pweb]> SHOW variables where Variable_name in ('eq_range_index_dive_limit', 'use_stat_tables', 'sort_buffer_size')\G
*************************** 1. row ***************************
Variable_name: eq_range_index_dive_limit
Value: 100000000
*************************** 2. row ***************************
Variable_name: sort_buffer_size
Value: 209715200
*************************** 3. row ***************************
Variable_name: use_stat_tables
Value: NEVER
3 rows in set (0.002 sec)
在某些基于实际查询时间的情况下 - 我注意到在不使用索引的情况下返回速度更快,我不得不说这是非常令人惊讶的。这支持选项1吗?
91785/538368 -- 使用了表的 17%。
当获取大量表时,进行表扫描通常会更快。当使用索引时,需要在索引的BTree和数据的BTree之间来回跳动。这种弹跳可能比简单地对数据进行线性扫描(丢弃 83% 的行)需要更长的时间。 唉,优化器没有足够的统计数据来说明哪种方式更快。
在这种情况下,结果集的大小可能太大,无法放入查询缓存。因此,即使它已打开,尝试使用 QC 也可能会导致额外的开销。您可以做的一件事就是缩小
CHAR(36)
BINARY(16)
。我的
UUIDs博客甚至在数据类型存在之前就讨论了这一点。 客户端到底要如何处理 91785 行?另一个提示:如果客户没有使用所有列,请仅拼出必要的列,而不是使用
SELECT *
。