mysql - MySQL 5.7 简单计数查询有时快有时很慢
问题描述
【1】这里是一个简单的计数查询:
SELECT SQL_NO_CACHE count(*) FROM fees WHERE 1 = 1 AND flag = 0 AND bmonth = '201906';
【2】查询结果:
+----------+
| count(*) |
+----------+
| 1021863 |
+----------+
1 row in set, 1 warning (34.77 sec)
【3】有时是(大多数时候):
+----------+
| count(*) |
+----------+
| 1021863 |
+----------+
1 row in set, 1 warning (1.89 sec)
【4】几分钟后,像第一个结果显示一样变得很慢。
【5】查询将使用以下索引,而uk_customer 包含bmonth 列:
+----+-------------+----------------------+------------+------+--------------------------------------------+----------------+---------+-------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------------+------------+------+--------------------------------------------+----------------+---------+-------+---------+----------+-------------+
| 1 | SIMPLE | fees | NULL | ref | uk_warehouse,uk,uk_calcu | uk_customer | 4 | const | 2028586 | 10.00 | Using where |
+----+-------------+----------------------+------------+------+--------------------------------------------+----------------+---------+-------+---------+----------+-------------+
1 row in set, 2 warnings (0.00 sec)
==================================================== ======================
【6】服务器内存5G,缓冲池大小4G。和索引大小:
+--------------------------------------------------+---------------------------+------------+--------------+
| Table_Name | Index_Name | Page_Count | Size_in_MB |
+--------------------------------------------------+---------------------------+------------+--------------+
| `test`.`fees` | PRIMARY | 46503 | 567.04021835 |
| `test`.`fees` | uk_customer | 2521 | 34.86496449 |
| `test`.`fees` | uk_calcu | 3178 | 30.72235107 |
【7】innodb设置如下:
innodb_page_size = 16384
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 2
innodb_buffer_pool_load_at_startup = 1
innodb_buffer_pool_dump_at_shutdown = 1
innodb_lru_scan_depth = 2048
innodb_lock_wait_timeout = 5
lock_wait_timeout=5
innodb_io_capacity = 1000
innodb_io_capacity_max = 6000
innodb_flush_method = O_DIRECT
innodb_file_format = Barracuda
innodb_file_format_max = Barracuda
#innodb_undo_logs = 128
innodb_undo_tablespaces = 2
innodb_flush_neighbors = 0
innodb_log_file_size = 128M
innodb_log_files_in_group = 2
innodb_log_buffer_size = 16777216
#innodb_max_undo_log_size = 2147483648
innodb_purge_threads = 4
innodb_large_prefix = 1
innodb_thread_concurrency = 16
innodb_print_all_deadlocks = 1
innodb_strict_mode = 1
innodb_sort_buffer_size = 256k
innodb_write_io_threads = 4
innodb_read_io_threads = 4
innodb_file_per_table = 1
当前缓冲区使用情况:
+---------------------------------------+---------------+
| Variable_name | Value |
+---------------------------------------+---------------+
| Innodb_buffer_pool_read_ahead_rnd | 0 |
| Innodb_buffer_pool_read_ahead | 753136482 |
| Innodb_buffer_pool_read_ahead_evicted | 185375 |
| Innodb_buffer_pool_read_requests | 1355375133341 |
| Innodb_buffer_pool_reads | 2418853408 |
+---------------------------------------+---------------+
5 rows in set (0.00 sec)
页面使用:
+----------------------------------+----------+
| Variable_name | Value |
+----------------------------------+----------+
| Innodb_buffer_pool_pages_data | 246447 |
| Innodb_buffer_pool_pages_dirty | 2738 |
| Innodb_buffer_pool_pages_flushed | 84827986 |
| Innodb_buffer_pool_pages_free | 3982 |
| Innodb_buffer_pool_pages_misc | 11699 |
| Innodb_buffer_pool_pages_total | 262128 |
+----------------------------------+----------+
6 rows in set (0.00 sec)
==================================================== ===================== 【8】但在slave服务器上运行良好且速度非常快,几乎都是2+秒。
请帮忙,如何调整性能?
解决方案
这就是“缓存”的效果。InnoDB 基本上有一个主缓存,即“buffer_pool”,其大小由innodb_buffer_pool_size
. 总数据集有多大?
更多详细信息:
- 由于 buffer_pool 中没有缓存任何内容,因此必须从磁盘读取内容,因此需要 35 秒。
- 如果您很快再次运行查询,则不需要 I/O,因为这些内容仍在缓存中。因此 2 秒。
- 如果在您再次运行之前有一段时间间隔,其他数据会进入 buffer_pool,从而排除查询的块(可能是所有块)。不是回到35秒。
治愈:
- 不要运行“大”查询。计算一百万行可能需要 100MB 的缓存。我不能说第 3 项的大查询是什么。
- 更好的索引。对于那个查询,
INDEX(flag, bmonth)
是最优的并且需要更少的数据,从而加快了非缓存 (35s) 版本的速度。此外,由于更小,它的数据不太可能被撞出缓存。 - 避免交换。5GB RAM 中的 4GB buffer_pool 可能会导致交换。这对于 MySQL 的性能来说是很糟糕的。我强烈建议您将 buffer_pool_size 降低到 3G。
- 更多 RAM(和更大的 buffer_pool)。
- 找到其他“大”查询并努力减少使用数据。
- 质疑需要一百万左右的精确计数。这涉及 UI 更改以及可能“估计”计数的方法。
- 汇总表——每天晚上统计前一天发生了多少;将其保存在汇总表中,并针对它运行更快的查询。(我可以进一步讨论这个问题。)
缓冲区和页面使用指标是“计数器”,需要除以Uptime
“每秒”。进一步分析:http: //mysql.rjweb.org/doc.php/mysql_analysis
汇总表:http: //mysql.rjweb.org/doc.php/summarytables
推荐阅读
- c# - 使用反射从类中填充字典
- dictionary - 如何创建项目并将其添加到字典
- slack-api - 如何触发 Slack im_open 事件?
- ruby-on-rails - Rails 生成带有选项和选项组的选择
- asp.net-mvc-5 - ScriptBundle 说对象引用未设置为对象的实例
- python - 用文本文件中的相应行替换列表列表中的数字
- selenium - 错误:UnsupportedOperationError:在桥接模式下不支持相对于视口的指针移动
- spring - 如何使用 Apche Camel 向 WebSphere MQ 发送消息并从 MQ 队列接收消息
- javascript - 如何将 AJAX 中的 JSON 文件与 Django 链接?
- c# - 区分可选参数和强制参数