MySQL:非常慢的更新/插入/删除查询挂在“查询结束”步骤
Posted
技术标签:
【中文标题】MySQL:非常慢的更新/插入/删除查询挂在“查询结束”步骤【英文标题】:MySQL: Very slow update/insert/delete queries hanging on "query end" step 【发布时间】:2012-09-01 05:17:10 【问题描述】:我有一个大而重的 mysql 数据库,它有时执行得很快,但有时却非常慢。所有的表都是InnoDB,服务器有32GB的RAM和数据库大小大约是40GB。
我的slow_query_log
中的前 20 个查询是 update
、insert
和 delete
查询,我不明白为什么它们这么慢(有时长达 120 秒!)
这是最常见的查询:
UPDATE comment_fallows set comment_cnt_new = 0 WHERE user_id = 1;
分析结果:
mysql> set profiling = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> update comment_fallows set comment_cnt_new = 0 where user_id = 1;
Query OK, 0 rows affected (2.77 sec)
Rows matched: 18 Changed: 0 Warnings: 0
mysql> show profile for query 1;
+---------------------------+----------+
| Status | Duration |
+---------------------------+----------+
| starting | 0.000021 |
| checking permissions | 0.000004 |
| Opening tables | 0.000010 |
| System lock | 0.000004 |
| init | 0.000041 |
| Searching rows for update | 0.000084 |
| Updating | 0.000055 |
| end | 0.000010 |
| query end | 2.766245 |
| closing tables | 0.000007 |
| freeing items | 0.000013 |
| logging slow query | 0.000003 |
| cleaning up | 0.000002 |
+---------------------------+----------+
13 rows in set (0.00 sec)
我正在使用主/服务器复制,因此启用了二进制日志。我放弃了我在互联网上找到的一条建议,并将flush_log_at_trx_commit
设置为0
,但没有任何区别:
mysql> show variables like '%trx%';
+-------------------------------------------+-------+
| Variable_name | Value |
+-------------------------------------------+-------+
| innodb_flush_log_at_trx_commit | 0 |
| innodb_use_global_flush_log_at_trx_commit | ON |
+-------------------------------------------+-------+
表结构:
CREATE TABLE `comment_fallows` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`part_id` int(11) DEFAULT NULL,
`article_id` int(11) DEFAULT NULL,
`request_id` int(11) DEFAULT NULL,
`comment_cnt` int(10) unsigned NOT NULL,
`comment_cnt_new` int(10) unsigned NOT NULL DEFAULT '0',
`last_comment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`,`last_comment_date`),
KEY `part_id` (`part_id`),
KEY `last_comment_date` (`last_comment_date`),
KEY `request_id` (`request_id`),
CONSTRAINT `comment_fallows_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `comment_fallows_ibfk_2` FOREIGN KEY (`part_id`) REFERENCES `fanfic_parts` (`id`) ON DELETE CASCADE,
CONSTRAINT `comment_fallows_ibfk_3` FOREIGN KEY (`request_id`) REFERENCES `requests` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=2239419 DEFAULT CHARSET=utf8
以及所有的 innodb 设置(服务器有 32 GB 的 RAM):
mysql> show variables like '%innodb%';
+-------------------------------------------+------------------------+
| Variable_name | Value |
+-------------------------------------------+------------------------+
| have_innodb | YES |
| ignore_builtin_innodb | OFF |
| innodb_adaptive_flushing | ON |
| innodb_adaptive_flushing_method | estimate |
| innodb_adaptive_hash_index | ON |
| innodb_adaptive_hash_index_partitions | 1 |
| innodb_additional_mem_pool_size | 16777216 |
| innodb_autoextend_increment | 8 |
| innodb_autoinc_lock_mode | 1 |
| innodb_blocking_buffer_pool_restore | OFF |
| innodb_buffer_pool_instances | 1 |
| innodb_buffer_pool_restore_at_startup | 0 |
| innodb_buffer_pool_shm_checksum | ON |
| innodb_buffer_pool_shm_key | 0 |
| innodb_buffer_pool_size | 21474836480 |
| innodb_change_buffering | all |
| innodb_checkpoint_age_target | 0 |
| innodb_checksums | ON |
| innodb_commit_concurrency | 0 |
| innodb_concurrency_tickets | 500 |
| innodb_corrupt_table_action | assert |
| innodb_data_file_path | ibdata1:10M:autoextend |
| innodb_data_home_dir | |
| innodb_dict_size_limit | 0 |
| innodb_doublewrite | ON |
| innodb_doublewrite_file | |
| innodb_fake_changes | OFF |
| innodb_fast_checksum | OFF |
| innodb_fast_shutdown | 1 |
| innodb_file_format | Antelope |
| innodb_file_format_check | ON |
| innodb_file_format_max | Antelope |
| innodb_file_per_table | ON |
| innodb_flush_log_at_trx_commit | 0 |
| innodb_flush_method | |
| innodb_flush_neighbor_pages | area |
| innodb_force_load_corrupted | OFF |
| innodb_force_recovery | 0 |
| innodb_ibuf_accel_rate | 100 |
| innodb_ibuf_active_contract | 1 |
| innodb_ibuf_max_size | 10737401856 |
| innodb_import_table_from_xtrabackup | 0 |
| innodb_io_capacity | 10000 |
| innodb_kill_idle_transaction | 0 |
| innodb_large_prefix | OFF |
| innodb_lazy_drop_table | 0 |
| innodb_lock_wait_timeout | 120 |
| innodb_locks_unsafe_for_binlog | OFF |
| innodb_log_block_size | 512 |
| innodb_log_buffer_size | 8388608 |
| innodb_log_file_size | 268435456 |
| innodb_log_files_in_group | 3 |
| innodb_log_group_home_dir | ./ |
| innodb_max_dirty_pages_pct | 90 |
| innodb_max_purge_lag | 0 |
| innodb_mirrored_log_groups | 1 |
| innodb_old_blocks_pct | 37 |
| innodb_old_blocks_time | 0 |
| innodb_open_files | 300 |
| innodb_page_size | 16384 |
| innodb_purge_batch_size | 20 |
| innodb_purge_threads | 1 |
| innodb_random_read_ahead | OFF |
| innodb_read_ahead | linear |
| innodb_read_ahead_threshold | 56 |
| innodb_read_io_threads | 8 |
| innodb_recovery_stats | OFF |
| innodb_recovery_update_relay_log | OFF |
| innodb_replication_delay | 0 |
| innodb_rollback_on_timeout | OFF |
| innodb_rollback_segments | 128 |
| innodb_show_locks_held | 10 |
| innodb_show_verbose_locks | 0 |
| innodb_spin_wait_delay | 6 |
| innodb_stats_auto_update | 1 |
| innodb_stats_method | nulls_equal |
| innodb_stats_on_metadata | ON |
| innodb_stats_sample_pages | 8 |
| innodb_stats_update_need_lock | 1 |
| innodb_strict_mode | OFF |
| innodb_support_xa | ON |
| innodb_sync_spin_loops | 30 |
| innodb_table_locks | ON |
| innodb_thread_concurrency | 16 |
| innodb_thread_concurrency_timer_based | OFF |
| innodb_thread_sleep_delay | 10000 |
| innodb_use_global_flush_log_at_trx_commit | ON |
| innodb_use_native_aio | ON |
| innodb_use_sys_malloc | ON |
| innodb_use_sys_stats_table | OFF |
| innodb_version | 1.1.8-rel25.1 |
| innodb_write_io_threads | 8 |
+-------------------------------------------+------------------------+
92 rows in set (0.00 sec)
我已经为这个问题苦苦挣扎了好几个星期,如果有任何关于如何解决这个问题的建议,我将非常感激。
为什么我的update
、insert
和delete
查询在query end
步骤上会这么慢?
更新
我已禁用查询缓存,但 update
、insert
和 delete
查询仍然非常非常慢(没有任何变化)
show variables like '%cache%';
+------------------------------+----------------------+
| Variable_name | Value |
+------------------------------+----------------------+
| binlog_cache_size | 4194304 |
| binlog_stmt_cache_size | 32768 |
| have_query_cache | YES |
| key_cache_age_threshold | 300 |
| key_cache_block_size | 1024 |
| key_cache_division_limit | 100 |
| max_binlog_cache_size | 18446744073709547520 |
| max_binlog_stmt_cache_size | 18446744073709547520 |
| metadata_locks_cache_size | 1024 |
| query_cache_limit | 16777216 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 0 |
| query_cache_strip_comments | OFF |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
| stored_program_cache | 256 |
| table_definition_cache | 400 |
| table_open_cache | 2048 |
| thread_cache_size | 8 |
+------------------------------+----------------------+
【问题讨论】:
当您使用 id 而不是 user_id "UPDATE comment_fallows set comment_cnt_new = 0 WHERE id = 1" 运行该查询时,它会有相同的延迟吗? @Pinchy,是的。事实上,它甚至更慢。 @SilverLight 哪个版本的MySql? 您是否尝试过pause replication,只是为了看看这是否是复制滞后问题?还可以查看this answer 以获取对Percona Toolkit 软件的一些引用,这可能有助于验证主数据和副本数据的一致性。 @CristianPorta 服务器版本:5.5.21-55-log 【参考方案1】:尝试设置值:
innodb_flush_log_at_trx_commit=2
innodb_flush_method=O_DIRECT (for non-windows machine)
innodb_buffer_pool_size=25GB (currently it is close to 21GB)
innodb_doublewrite=0
innodb_support_xa=0
innodb_thread_concurrency=0...1000 (try different values, beginning with 200)
参考资料:
MySQL docs 用于不同变量的描述。
MySQL Server Setting Tuning
MySQL Performance Optimization basics
希望对你有帮助...
【讨论】:
对我没用。仍然得到查询结束。难道我在服务器上没有任何交换磁盘吗?我正在运行 Windows 服务器... 很好,它有效。对于较小的安装,您可能需要较少的 innodb_thread_concurrency 和 innodb_buffer_pool_size,但通常这个建议是有效的。 这些设置对生产安全吗?我不这么认为。 这是一个鲁莽的危险和无关紧要的建议。我很惊讶它甚至被赞成。 我错了吗,25GB 池大小意味着 25GB 内存?你们都在内存这么大的服务器上运行吗?【参考方案2】:MySQL 处理查询缓存的方式似乎存在错误,导致类似行为(请参阅http://bugs.mysql.com/bug.php?id=28382)。
基本上发生的情况是,需要在任何修改数据的查询(INSERT、UPDATE、DELETE)之后更新缓存。如果缓存较大,则执行此操作需要很长时间,如果缓存较小,则速度更快。
因此,在引擎修复之前,解决方法是减小缓存大小。
【讨论】:
joocer,谢谢你的回答。我有 512 Mb 的查询缓存。当我禁用它时,事情确实变得更快了。但是 INSERT、UPDATE、DELETE 查询仍然很慢。 @SilverLight,只是为了确认一下,“查询结束”步骤的查询仍然很慢,还是其他步骤的性能完全改变了?【参考方案3】:如果您使用的是 DELL 服务器,则可能是硬件问题。 我解决了这个命令。
/opt/dell/srvadmin/bin/omconfig storage vdisk action=changepolicy 控制器=0 vdisk=0 writepolicy=fwb
【讨论】:
【参考方案4】:尝试根据innodb_buffer_pool_size
开始调整您的innodb_buffer_pool_instances
首先我认为你可以显着增加你的innodb_buffer_pool_size
...
innodb_buffer_pool_instances sysvar
InnoDB 用于缓存数据的内存缓冲区的大小(以字节为单位)和 其表的索引。默认值为 128MB,从 8MB 的历史默认值。最大值取决于 CPU 架构,32 位或 64 位。对于 32 位系统,CPU 架构和操作系统有时会施加较低的实用性 最大尺寸。
您将此值设置得越大,访问所需的磁盘 I/O 就越少 表中的数据。在专用数据库服务器上,您可以将其设置为 到机器物理内存大小的 80%。准备缩减规模 如果出现这些其他问题,则此值...
然后你可以调整innodb_buffer_pool_instances
,使用多个缓冲池很重要,你可以在这里阅读一个很好的测试用例:
MySQL Workload test
【讨论】:
【参考方案5】:我的测试环境有问题(不是DBA设置的)。最后我发现my.cnf中有一个conf:sync_binlog=1。我将此conf更改为0,它可以工作。 你可以试一试。
【讨论】:
【参考方案6】:这可能是由于磁盘写入速度慢。
在我们的例子中,这是因为运行 mysqld 的 Debian GNU/Linux 在 Hyper-V 中被虚拟化了,即使它被给予 SSD 存储 hdparm -t
也会产生糟糕的结果(10-20MB/s 而不是 600MB/s获取原始硬件)
【讨论】:
以上是关于MySQL:非常慢的更新/插入/删除查询挂在“查询结束”步骤的主要内容,如果未能解决你的问题,请参考以下文章
非常慢的 MySQL COUNT DISTINCT 查询,即使有索引——如何优化?