【问题标题】:MySQL Query Optimization for JOIN Large TablesJOIN 大表的 MySQL 查询优化
【发布时间】:2015-09-02 11:34:47
【问题描述】:

我的 MySQL 查询有大数据访问的问题,当使用 join 优化查询时,它会在 122 秒内为一周的数据提供输出。然后对于一个月的数据,该过程需要 526 秒。 我想优化此查询以减少每年的处理时间,或者是否有任何方法可以优化 MySQL 设置?

表详细信息。 我参考了两个表,分别是 mdiaries 和 tv_diaries,在这两个表中我都索引了相关列,在 mdiaries 表中,tv_diaries 中有 2661331 行和 27074645 行。

mdiaries 表:

  INDEX area (area),
  INDEX date (date),
  INDEX district (district),
  INDEX gaDivision (gaDivision),
  INDEX member_id (member_id),
  INDEX tv_channel_id (tv_channel_id),

tv_diaries.

  INDEX area (area),
  INDEX date (date),
  INDEX district (district),
  INDEX member_id (member_id),
  INDEX timeslot_id (timeslot_id),
  INDEX tv_channel_id (tv_channel_id),

这是我的查询,执行需要 122 秒。

$sql = "SELECT COUNT(TvDiary.id) AS m_count,TvDiary.date,TvDiary.timeslot_id,TvDiary.tv_channel_id,TvDiary.district,TvDiary.area
FROM `mdiaries` AS Mdiary INNER JOIN `tv_diaries` AS TvDiary ON Mdiary.member_id = TvDiary.member_id
WHERE Mdiary.date >= '2014-01-01' AND Mdiary.date <= '2014-01-07'
AND TvDiary.date >= '2014-01-01' AND TvDiary.date <= '2014-01-07'
GROUP BY TvDiary.date,
TvDiary.timeslot_id,
TvDiary.tv_channel_id,
TvDiary.district,
TvDiary.area";

这是 my.cnf 文件。

    [mysqld]

## General
datadir                         = /var/lib/mysql
tmpdir                          = /var/lib/mysqltmp
socket                          = /var/lib/mysql/mysql.sock
skip-name-resolve
sql-mode                        = NO_ENGINE_SUBSTITUTION
#event-scheduler                = 1

## Networking
back-log                        = 100
#max-connections                = 200
max-connect-errors              = 10000
max-allowed-packet              = 32M
interactive-timeout             = 3600
wait-timeout                    = 600

### Storage Engines
#default-storage-engine         = InnoDB
innodb                          = FORCE

## MyISAM
key-buffer-size                 = 64M
myisam-sort-buffer-size         = 128M

## InnoDB
innodb-buffer-pool-size        = 16G
innodb_buffer_pool_instances    = 16
#innodb-log-file-size           = 100M
#innodb-log-buffer-size         = 8M
#innodb-file-per-table          = 1
#innodb-open-files              = 300

## Replication
server-id                       = 1
#log-bin                        = /var/log/mysql/bin-log
#relay-log                      = /var/log/mysql/relay-log
relay-log-space-limit           = 16G
expire-logs-days                = 7
#read-only                      = 1
#sync-binlog                    = 1
#log-slave-updates              = 1
#binlog-format                  = STATEMENT
#auto-increment-offset          = 1
#auto-increment-increment       = 2

## Logging
log-output                      = FILE
slow-query-log                  = 1
slow-query-log-file             = /var/log/mysql/slow-log
#log-slow-slave-statements
long-query-time                 = 2

##
query_cache_size        = 512M
query_cache_type        = 1
query_cache_limit       = 2M
join_buffer_size        = 512M
thread_cache_size       = 128

[mysqld_safe]
log-error                       = /var/log/mysqld.log
open-files-limit                = 65535

[mysql]
no-auto-rehash

【问题讨论】:

  • 只需使用左连接代替内连接,您将看到性能
  • 内连接通常比左连接快,所以这样写就已经正确了。我想知道您是否真的需要获取所有结果以及 LIMIT 是否会有所帮助
  • @SatenderK 我已经测试了 LEFT JOIN,但它比 LEFT JOIN 更好的 INNER JOIN
  • 放置日期过滤器后tv_diary表的计数是多少。
  • 日期期间 - '2014-01-01' 和 '2014-01-07' 之间,计数 - 141402 @ZafarMalik

标签: mysql sql innodb


【解决方案1】:

这是您的查询:

SELECT COUNT(t.id) AS m_count, t.date, t.timeslot_id, t.tv_channel_id,
       t.district, t.area
FROM `mdiaries` m INNER JOIN
     `tv_diaries` t
     ON m.member_id = t.member_id
WHERE m.date >= '2014-01-01' AND m.date <= '2014-01-07' AND
      t.date >= '2014-01-01' AND t.date <= '2014-01-07'
GROUP BY t.date, t.timeslot_id, t.tv_channel_id, t.district, t.area;

我将从复合索引开始:tv_diaries(date, member_id)mdiaries(member_id, date)

这个查询有问题,但这些可能会有所帮助。

【讨论】:

  • 谢谢!首先一个月数据需要 600 秒,但添加多个索引它会减少到 160 秒。我需要知道,我添加了 JOIN 查询,但是当我在没有 JOIN 的情况下进行检查时,它花费的时间比预期的要少,这是什么原因?
【解决方案2】:

尝试在GROUP BY 子句中引用的所有列上添加多列索引,如in the documentation 所述。

INDEX grp (date, timeslot_id, tv_channel_id, district, area)

【讨论】:

  • 这也提高了查询处理的效率。谢谢!
  • 哦,非常感谢,我有一个多键连接,需要几个小时,现在,它立即完成!
【解决方案3】:

不确定,但它可以为您提供更好的性能-

SELECT COUNT(t.id) AS m_count, t.date, t.timeslot_id, t.tv_channel_id, t.district, t.area
FROM `mdiaries` m 
JOIN 
(
SELECT t.id, t.date, t.timeslot_id, t.tv_channel_id, t.district, t.area, t.member_id 
FROM `tv_diaries` AS t
WHERE t.date >= '2014-01-01' AND t.date <= '2014-01-07' 
) t ON m.member_id = t.member_id
WHERE m.date >= '2014-01-01' AND m.date <= '2014-01-07' 
GROUP BY t.date, t.timeslot_id, t.tv_channel_id, t.district, t.area;

您还可以检查您的数据库配置设置,因为我看到以下问题-

  1. innodb_file_per_table=1 被注释:如果为真,则数据将存储在单个 ibd 文件中,而不是按表存储。

  2. tmp_table_size 和 max_heap_table_size 可以在您尝试从繁重的表中获取数据时提高性能。因此,如果您的查询正在磁盘上创建临时表,请尝试将它们都设置为至少 100M 以避免在磁盘上创建临时表。

  3. 当您使用 group by 时,如果增加 sort_buffer_size 变量会有所帮助。可以设置2M。

  4. join_buffer_size 太高应该在 2M 左右可以设置最大值。 8M 但不是 512M,因为它使用会话明智,所以吃掉你所有的记忆。

  5. 1234563 p>

【讨论】:

  • QC 不应设置得太高——它会使修剪成本更高。
  • 我的意思是说他设置的太高了,对不起,这里的意思似乎不同。现在我已经更正了。感谢 Rick 引起我的注意。
  • @RickJames:我从你那里学到了很多东西,在这里与其他人分享...... :)
【解决方案4】:

也许您可以使用物化视图来存储查询结果并定期刷新(每月?15 天?)

这不会优化您的查询,但您的咨询会更快(它不会再次计算计数)

【讨论】:

    猜你喜欢
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    相关资源
    最近更新 更多