bli*_*ile 6 mysql aws mysql-5.6 mysql-5.7 amazon-rds
我有一个相当简单的查询,在从 MySQL 5.6.35 升级到 MySQL 5.7.12(在 AWS RDS 上运行)后变得非常慢。
SELECT DISTINCT
Name,d.id,deviceType,issuedBy, description,avNum,CompanyName,
BrandName,dwNumber,quant,discDate,Type
FROM table_one d
JOIN table_two i ON d.id = i.id;
Run Code Online (Sandbox Code Playgroud)
在 5.6 中,此查询在 90 秒内完成。升级到 5.7 后,需要 30 多分钟。每个表大约有 200 万行。
我首先比较了 5.6 和 5.7 之间的优化器设置:
# 5.6
index_merge=on
index_merge_union=on
index_merge_sort_union=on
index_merge_intersection=on
engine_condition_pushdown=on
index_condition_pushdown=on
mrr=on
mrr_cost_based=on
block_nested_loop=on
batched_key_access=off
materialization=on
semijoin=on
loosescan=on
firstmatch=on
subquery_materialization_cost_based=on
use_index_extensions=on
Run Code Online (Sandbox Code Playgroud)
# 5.7
Optimizer settings in 5.7:
index_merge=on
index_merge_union=on
index_merge_sort_union=on
index_merge_intersection=on
engine_condition_pushdown=on
index_condition_pushdown=on
mrr=on
mrr_cost_based=on
block_nested_loop=on
batched_key_access=off
materialization=on
semijoin=on
loosescan=on
firstmatch=on
subquery_materialization_cost_based=on
use_index_extensions=on
condition_fanout_filter=on
derived_merge=on
duplicateweedout=on
Run Code Online (Sandbox Code Playgroud)
我看到的唯一变化是 5.7 中的最后三个选项。我关闭它们如下:
# 5.6
index_merge=on
index_merge_union=on
index_merge_sort_union=on
index_merge_intersection=on
engine_condition_pushdown=on
index_condition_pushdown=on
mrr=on
mrr_cost_based=on
block_nested_loop=on
batched_key_access=off
materialization=on
semijoin=on
loosescan=on
firstmatch=on
subquery_materialization_cost_based=on
use_index_extensions=on
Run Code Online (Sandbox Code Playgroud)
这没有效果。接下来,我查看了 innodb 设置的差异(我的 5.7 实例具有更多内存,因此池大小差异)。
# my innodb changes 5.6 -> 5.7
innodb_adaptive_hash_index_parts added in 5.7, set to ‘8’
innodb_additional_mem_pool_size set to 8388608 in 5.6, removed in 5.7
innodb_buffer_pool_dump_at_shutdown changed from ‘OFF’ to ‘ON’
innodb_buffer_pool_dump_pct added in 5.7, set to ’25’
innodb_buffer_pool_load_at_startup changed from ‘OFF’ to ‘ON’
innodb_buffer_pool_size changed from 2,804,940,800 to 11,811,160,064
innodb_checksum_algorithm changed from ‘innodb’ to ‘crc32’
innodb_deadlock_detect added in 5.7, set to ‘ON’
innodb_default_row_format added in 5.7, set to ‘dynamic’
innodb_file_format changed from ‘Antelope’ to ‘Barracuda’
innodb_file_format_max changed from ‘Antelope’ to ‘Barracuda’
innodb_fill_factor added in 5.7 set to ‘100’
innodb_flush_sync added in 5.7 set to ‘ON’
innodb_log_checksums added in 5.7 set to ‘ON’
innodb_log_write_ahead_size added in 5.7 set to 8192
innodb_max_undo_log_size added in 5.7 set to 1,073,741,824
innodb_mirrored_log_groups removed in 5.7, set to ‘1’ in 5.6
innodb_numa_interleave added in 5.7 set to ‘OFF’
innodb_page_cleaners added in 5.7 set to ‘4’
innodb_purge_rseg_truncate_frequency added in 5.7 set to ‘128’
innodb_strict_mode changed from ‘OFF’ to ‘ON’
innodb_temp_data_file_path added in 5.7, set to 'ibtmp1:12M:autoextend'
innodb_undo_log_truncate added in 5.7, set to ‘OFF’
Run Code Online (Sandbox Code Playgroud)
我查看了参考手册 并尝试关闭以下内容:
innodb_strict_mode=‘OFF’
innodb_deadlock_detect=‘OFF’
innodb_flush_sync=‘OFF’
innodb_log_checksums=‘OFF’
Run Code Online (Sandbox Code Playgroud)
同样,没有效果。
我希望有人能告诉我这里发生了什么?该查询是更大工作流的一部分,因此变得无法使用。如果可能的话,我想恢复 5.6 的行为,但由于我有限的 DBA 知识,我没有什么可以尝试的了。我希望有人能指出我正确的方向或给我更多的调查途径。
下面是我的桌子。许多列的宽度很大,它们大多被定义为VARCHAR(X) DEFAULT NULL
因为数据源不干净(我无法控制源)。请注意,我需要 utf8 数据,但我在对慢查询进行故障排除时使用 latin1,以消除可能导致慢的原因。(我们需要从 5.6 -> 5.7 迁移的原因是 5.7 中更大的索引大小,它可以容纳我们更大的 UTF8 列大小)。
(另外,我应该指出该id
字段来自源数据,并且是一长串可变宽度的字母数字字符(最多约 30 个字符),所以我需要它是一个 VARCHAR)。
# 5.7
Optimizer settings in 5.7:
index_merge=on
index_merge_union=on
index_merge_sort_union=on
index_merge_intersection=on
engine_condition_pushdown=on
index_condition_pushdown=on
mrr=on
mrr_cost_based=on
block_nested_loop=on
batched_key_access=off
materialization=on
semijoin=on
loosescan=on
firstmatch=on
subquery_materialization_cost_based=on
use_index_extensions=on
condition_fanout_filter=on
derived_merge=on
duplicateweedout=on
Run Code Online (Sandbox Code Playgroud)
SET optimizer_switch='condition_fanout_filter=off';
SET optimizer_switch='derived_merge=off';
SET optimizer_switch='duplicateweedout=off';
Run Code Online (Sandbox Code Playgroud)
两者之间的 EXPLAIN 也相同:
explain extended SELECT DISTINCT ...
******************** 1. row *********************
id: 1
select_type: SIMPLE
table: d
type: ALL
possible_keys: PRIMARY
key:
key_len:
ref:
rows: 1596593
filtered: 100.00
Extra: Using temporary
******************** 2. row *********************
id: 1
select_type: SIMPLE
table: i
type: ref
possible_keys: idx_table_two_id
key: idx_table_two_id
key_len: 203
ref: mydb.d.id
rows: 1
filtered: 100.00
Extra:
Run Code Online (Sandbox Code Playgroud)
更新#1:
这里有更多信息。我在两台服务器上都启用了性能模式。5.7 服务器显示了很多hash_table_locks
:
# my innodb changes 5.6 -> 5.7
innodb_adaptive_hash_index_parts added in 5.7, set to ‘8’
innodb_additional_mem_pool_size set to 8388608 in 5.6, removed in 5.7
innodb_buffer_pool_dump_at_shutdown changed from ‘OFF’ to ‘ON’
innodb_buffer_pool_dump_pct added in 5.7, set to ’25’
innodb_buffer_pool_load_at_startup changed from ‘OFF’ to ‘ON’
innodb_buffer_pool_size changed from 2,804,940,800 to 11,811,160,064
innodb_checksum_algorithm changed from ‘innodb’ to ‘crc32’
innodb_deadlock_detect added in 5.7, set to ‘ON’
innodb_default_row_format added in 5.7, set to ‘dynamic’
innodb_file_format changed from ‘Antelope’ to ‘Barracuda’
innodb_file_format_max changed from ‘Antelope’ to ‘Barracuda’
innodb_fill_factor added in 5.7 set to ‘100’
innodb_flush_sync added in 5.7 set to ‘ON’
innodb_log_checksums added in 5.7 set to ‘ON’
innodb_log_write_ahead_size added in 5.7 set to 8192
innodb_max_undo_log_size added in 5.7 set to 1,073,741,824
innodb_mirrored_log_groups removed in 5.7, set to ‘1’ in 5.6
innodb_numa_interleave added in 5.7 set to ‘OFF’
innodb_page_cleaners added in 5.7 set to ‘4’
innodb_purge_rseg_truncate_frequency added in 5.7 set to ‘128’
innodb_strict_mode changed from ‘OFF’ to ‘ON’
innodb_temp_data_file_path added in 5.7, set to 'ibtmp1:12M:autoextend'
innodb_undo_log_truncate added in 5.7, set to ‘OFF’
Run Code Online (Sandbox Code Playgroud)
此外,我看到很多时间都花在wait/io/table/sql/handler
了hash_table_locks
. 他们是时间的前两个消费者(以皮秒为单位):
innodb_strict_mode=‘OFF’
innodb_deadlock_detect=‘OFF’
innodb_flush_sync=‘OFF’
innodb_log_checksums=‘OFF’
Run Code Online (Sandbox Code Playgroud)
当我在5.6服务器上重复这个时,我没有看到这样的hash_table_locks
耗时。
更新#2:
要检查磁盘是否是瓶颈,我执行了以下操作。
我安装了两个相同的 EC2 实例。它们都是 i3.large(2 vCPU / 15.25 GB RAM)以及 1x425 GB SSD 磁盘。首先,我安装了 MySQL 5.7.25。在第二个实例中,我安装了 MariaDB 10.2.21。我为两者保留了开箱即用的配置。在 MySQL 上查询仍然需要 30 分钟,但 MariaDB 实例只需要 30 秒!InnoDB 的版本几乎相同:MySQL 运行 5.7.25,MariaDB 运行 5.7.24。在我看来,这一定是 MySQL 中的配置问题,而不是 Innodb 或磁盘的限制。
另一个更新:在 MariaDB 实例中,EXPLAIN 略有不同。有一个“using where”子句,表是颠倒的。我尝试使用 STRAIGHT_JOIN 来更改表顺序,但这并没有改变任何东西:
CREATE TABLE `table_one` (
`id` varchar(200) NOT NULL DEFAULT '',
`RecordKey` varchar(50) DEFAULT NULL,
`VersionStat` varchar(200) DEFAULT NULL,
`Status` varchar(200) DEFAULT NULL,
`VersionNumber` varchar(50) DEFAULT NULL,
`VersionDate` varchar(20) DEFAULT NULL,
`PublishDate` varchar(20) DEFAULT NULL,
`DistStart` varchar(20) DEFAULT NULL,
`DistCommStat` varchar(2000) DEFAULT NULL,
`BrandName` varchar(100) DEFAULT NULL,
`VersionModelNumber` varchar(100) DEFAULT NULL,
`Catalog` varchar(100) DEFAULT NULL,
`dwNumber` varchar(100) DEFAULT NULL,
`CompanyName` varchar(500) DEFAULT NULL,
`DeviceCount` varchar(200) DEFAULT NULL,
`description` varchar(3000) DEFAULT NULL,
`Exemption` varchar(1100) DEFAULT NULL,
`PreMarket` varchar(1500) DEFAULT NULL,
`DevDRMT` varchar(1000) DEFAULT NULL,
`DTKit` varchar(200) DEFAULT NULL,
`Combination` varchar(250) DEFAULT NULL,
`Usage` varchar(500) DEFAULT NULL,
`SingleBatch` varchar(50) DEFAULT NULL,
`SerialNumber` varchar(250) DEFAULT NULL,
`ManuDate` varchar(20) DEFAULT NULL,
`ExpDate` varchar(20) DEFAULT NULL,
`Donation` varchar(50) DEFAULT NULL,
`LabeldWithMLO` varchar(50) DEFAULT NULL,
`NLabledMLO` varchar(50) DEFAULT NULL,
`MLOStatus` varchar(1000) DEFAULT NULL,
`BTT` varchar(50) DEFAULT NULL,
`OPP` varchar(50) DEFAULT NULL,
`BRC` varchar(50) DEFAULT NULL,
`PriorUse` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Run Code Online (Sandbox Code Playgroud)
我还应用了以下威尔逊建议的更改,但这些更改并未解决问题。
更新 #3:
我发现了一些看起来很重要的东西。我在 5.6 和 5.7 服务器上启用了查询跟踪器。我在这里附上了他们的输出:
5.6:https : //pastebin.com/KSTeTDdy
5.7:https : //pastebin.com/3SaXNdCU
最后,我注意到临时表的创建方式不同:
# 5.6
{
"converting_tmp_table_to_myisam": {
"cause": "memory_table_size_exceeded",
"tmp_table_info": {
"table": "intermediate_tmp_table",
"row_length": 4570,
"key_length": 4585,
"unique_constraint": true,
"location": "disk (MyISAM)",
"record_format": "packed"
}
}
}
Run Code Online (Sandbox Code Playgroud)
# 5.7
{
"converting_tmp_table_to_ondisk": {
"cause": "memory_table_size_exceeded",
"tmp_table_info": {
"table": "intermediate_tmp_table",
"row_length": 4575,
"key_length": 8,
"unique_constraint": true,
"location": "disk (InnoDB)",
"record_format": "packed"
}
}
}
Run Code Online (Sandbox Code Playgroud)
5.6 使用 MyISAM 作为 tmp 表,5.7 使用 InnoDB。 也许这就是为什么我wait/synch/sxlock/innodb/hash_table_locks
在 5.7 中看到如此多的事件?这可能与 InnoDB 行级锁定有关吗?问题是,是否可以恢复旧行为进行测试?
我在 5.7 服务器上找到internal_tmp_disk_storage_engine
并将其更改为MyISAM
。这也不能解决问题。我可以看到在 /tmp 下创建的临时表:
# ls -ltrh /tmp
-rw-rw---- 1 mysql mysql 56K Apr 26 02:27 #sql_f55_0.MAI
-rw-rw---- 1 mysql mysql 368M Apr 26 02:27 #sql_f55_0.MAD
Run Code Online (Sandbox Code Playgroud)
现在肯定在使用MyISAM,但是查询仍然很慢。我确实注意到在所有服务器 5.7、5.6 和 MariaDB 上,此文件大小达到 368M,然后停止变大。与 5.7 (<1MB/s) 相比,文件在 5.6/MariaDB (~10-15 MB/s) 中的增长速度要快得多。
小智 1
您增加了 RAM,将 innodb 缓冲池大小从 ~2G 增加到 ~11G,即总 RAM 的 ~70% 对我来说似乎没问题。
由于执行计划和数据库配置看起来相同,您可以查看您的硬件配置。从硬件方面确认两件事:
为了安全起见,你可以把桌子放在一边吗?如果表大小较小,您还可以对其进行优化以消除任何碎片。要获取碎片报告,您可以在数据库上运行mysqltuner工具。
希望这可以帮助。
谢谢,桑吉娃
归档时间: |
|
查看次数: |
5267 次 |
最近记录: |