繁体   English   中英

MySQL SUM查询非常慢

[英]MySQL SUM Query is extremely slow

有一个表称为具有约600万行的transactions 下面的查询计算当前用户余额。 这是我启用slow_query_log = 'ON'后的日志:

# Time: 170406  9:51:48
# User@Host: root[root] @  [xx.xx.xx.xx]
# Thread_id: 13  Schema: main_db  QC_hit: No
# Query_time: 38.924823  Lock_time: 0.000034  Rows_sent: 1  Rows_examined: 773550
# Rows_affected: 0
SET timestamp=1491456108;
SELECT SUM(`Transaction`.`amount`) as total
    FROM `main_db`.`transactions` AS `Transaction`
    WHERE `Transaction`.`user_id` = 1008
      AND `Transaction`.`confirmed` = 1
    LIMIT 1;

如您所见,它花费了~38 seconds

这是transactions表EXPLAIN:

在此处输入图片说明

该查询有时运行很快(大约1秒左右),有时却非常慢!

任何帮助将不胜感激。

PS:

它的InnoDB和transactions表具有频繁的INSERT和SELECT操作。

我尝试使用SQL_NO_CACHE运行查询,但有时还是很快,有时还是很慢。

transactions表架构:

CREATE TABLE `transactions` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `ref_id` varchar(40) COLLATE utf8_persian_ci NOT NULL,
  `payment_id` tinyint(3) unsigned NOT NULL,
  `amount` decimal(10,1) NOT NULL,
  `created` datetime NOT NULL,
  `private_note` varchar(6000) COLLATE utf8_persian_ci NOT NULL,
  `public_note` varchar(200) COLLATE utf8_persian_ci NOT NULL,
  `confirmed` tinyint(3) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=13133663 DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci

MySQL在具有12GB RAM和9个逻辑CPU内核的VPS上运行。

这是my.cnf的一部分:

# * InnoDB
#
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
# Read the manual for more InnoDB related options. There are many!
default_storage_engine  = InnoDB
# you can't just change log file size, requires special procedure
innodb_buffer_pool_size = 9G
innodb_log_buffer_size  = 8M
innodb_file_per_table   = 1
innodb_open_files       = 400
innodb_io_capacity      = 400
innodb_flush_method     = O_DIRECT
innodb_thread_concurrency = 0
innodb_read_io_threads = 64
innodb_write_io_threads = 64


# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address           = 127.0.0.1
#
# * Fine Tuning
#
max_connections         = 500
connect_timeout         = 5
wait_timeout            = 600
max_allowed_packet      = 16M
thread_cache_size       = 128
sort_buffer_size        = 4M
bulk_insert_buffer_size = 16M
tmp_table_size          = 32M
max_heap_table_size     = 32M

(对不起,我要发表所有好的评论。我希望我已经添加足够的内容来证明要求“答案”。)

表格中是否有600万行? 但是,具有该user_id 773K行呢?

9GB buffer_pool? 该表大约有4GB的数据? 因此,如果没有其他需要解决的问题,它就适合buffer_pool。 SHOW TABLE STATUS并检查“ Data_length”。)

现有的INDEX(user_id)可能为20MB,很容易实现。

如果user_ids充分分散在表周围,则查询可能实际上需要获取数据的每个16KB块。 因此,具有原始索引的原始查询将如下所示:

  1. 扫描给定user_id的索引。 这将是总努力的一小部分。
  2. 对于索引中的每个条目,(随机)查找记录。 这发生了150万次。 使用“冷”缓存,这很容易花费38秒或更长时间。 重新启动后不久的“慢速”时间在哪里? 还是其他东西耗尽了缓存? 使用“热”缓存时,它全部是CPU(无I / O),因此1秒钟是合理的。

如果更改为最佳“覆盖” INDEX(user_id, confirmed, amount) ,则情况会有所变化...

  • “覆盖”是指整个查询将在索引中执行。 (此复合索引可能更像40MB,但与数据相比仍然很小。)
  • 在“冷”缓存中,仅需要40MB的内容即可获取-比38s好得多。
  • 在“热”缓存(这次只有40MB)中,它可能会在半秒钟内运行。

如果WHERE子句中也有日期范围,我将推动构建和维护“摘要表”。 这样可以将类似的查询速度提高10倍。

如果您添加开头的综合指数user_id ,你应该 (不应该DROP上只是user_id说明为冗余的索引。 (如果不删除它,通常会浪费磁盘空间。)

至于在生产中...

  • 如果您有足够新的MySQL版本,请使用ALTER TABLE ... ALGORITHM=INPLACE ... ,这对于添加/删除索引的影响最小。
  • 对于旧版本,请参阅pt-online-schema-change 它要求没有其他触发器,并且停机时间非常短。 触发器“透明”地处理了200次写入/分钟。

在MySQL 5.6和MariaDB 10.0中添加了ALGORITHM=INPLACE

(是的,我要添加另一个答案。理由:它以不同的方式解决了潜在的问题。)

潜在的问题似乎是,有一个不断增长的“交易”表,可以从中获得各种统计信息,例如SUM(amount) 随着表的增长,这种性能只会越来越差。

此答案的基础将以两种方式查看数据:“历史”和“当前”。 Transactions就是历史。 一个新表将是每个用户的“ Current总计”。 但是我看到了多种方法。 每项都涉及某种形式的小计,以避免添加773K行以获得答案。

  • 传统的银行业务方式...每天晚上汇总一天的Transactions并将其添加到Current
  • 物化视图的方式...每次向Transactions添加一行,都会增加Current
  • 混合:将每日小计保存在“汇总表”中。 总结这些分类汇总得到SUM经过昨晚。

在我的摘要表博客中有更多讨论。

请注意,银行或混合方式的最新余额有些棘手:

  1. 获取昨晚的金额
  2. 添加当天发生的所有交易。

任何方法会比扫描所有行773K为用户快得多 ,但它会更复杂的代码。

您可能要尝试的一件事是添加一个复合索引,以查看它是否可以加快查询的选择部分:

ALTER TABLE `transactions` ADD INDEX `user_confirmed` (`user_id`, `confirmed`);

另外,正如@wajeeh在评论中指出的,在这里LIMIT子句是不必要的,因为您已经在调用聚合函数。

如果您也可以在问题中发布表架构,将很有帮助。

看看这个答案有没有选择方法而不会导致锁定MySQL?

以及本文: 一致的非锁定读取

就我而言,就像@billynoah提到的那样,该表必须像Log表一样执行许多写操作,因此这可能对您有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM