MySQL挂在大型SELECT上

时间:2015-03-02 22:42:52

标签: mysql

我试图通过加入四个现有表来创建一个新表。我的数据库是静态的,因此制作一个大的预处理表将简化编程,并在将来的查询中节省大量时间。我的查询在受WHERE限制时工作正常,但似乎要么挂起,要么太慢以至于没有注意到任何进展。

这是工作查询。结果只需几秒钟。

SELECT group.group_id, MIN(application.date), person.person_name, pers_appln.sequence
FROM group
JOIN application ON group.appln_id=application.appln_id
JOIN pers_appln ON pers_appln.appln_id=application.appln_id
JOIN person ON person.person_id=pers_appln.person_id
WHERE group_id="24601"
GROUP BY group.group_id, pers_appln.sequence
;

如果我只删除WHERE行,它将会运行几天而无法显示。在开头添加CREATE TABLE newtable AS也是一样的。它永远不会超过0%的进展。

group,application和person表都使用MyISAM引擎,而pers_appln使用InnoDB。列都已编入索引。表格大小范围从大约4000万到1.5亿行。我知道它相当大,但我不认为它会造成这么大的问题。电脑目前有4GB内存。

任何想法如何使这项工作?


这里是SHOW CREATE TABLE信息。没有视图或虚拟表:

CREATE TABLE `group` (
  `APPLN_ID` int(10) unsigned NOT NULL,
  `GROUP_ID` int(10) unsigned NOT NULL,
  KEY `idx_appln` (`APPLN_ID`),
  KEY `idx_group` (`GROUP_ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8


CREATE TABLE `application` (
  `APPLN_ID` int(10) unsigned NOT NULL,
  `APPLN_AUTH` char(2) NOT NULL DEFAULT '',
  `APPLN_NR` varchar(20) NOT NULL DEFAULT '',
  `APPLN_KIND` char(2) DEFAULT '',
  `DATE` date DEFAULT NULL,
  `IPR_TYPE` char(2) DEFAULT '',
  PRIMARY KEY (`APPLN_ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8



CREATE TABLE `person` (
  `PERSON_ID` int(10) unsigned NOT NULL,
  `PERSON_CTRY_CODE` char(2) NOT NULL,
  `PERSON_NAME` varchar(300) DEFAULT NULL,
  `PERSON_ADDRESS` varchar(500) DEFAULT NULL,
  KEY `idx_person` (`PERSON_ID`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 MAX_ROWS=30000000 AVG_ROW_LENGTH=100


CREATE TABLE `pers_appln` (
  `PERSON_ID` int(10) unsigned NOT NULL,
  `APPLN_ID` int(10) unsigned NOT NULL,
  `SEQUENCE` smallint(4) unsigned DEFAULT NULL,
  `PLACE` smallint(4) unsigned DEFAULT NULL,
  KEY `idx_pers_appln` (`APPLN_ID`),
  KEY `idx_person` (`PERSON_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY HASH (appln_id)
PARTITIONS 20 */

这是我查询的EXPLAIN

+----+-------------+-------------+--------+----------------------------+-----------------+---------+--------------------------+----------+---------------------------------+
| id | select_type | table       | type   | possible_keys              | key             | key_len | ref                      | rows     | Extra                           |
+----+-------------+-------------+--------+----------------------------+-----------------+---------+--------------------------+----------+---------------------------------+
|  1 | SIMPLE      | person      | ALL    | idx_person                 | NULL            | NULL    | NULL                     | 47827690 | Using temporary; Using filesort |
|  1 | SIMPLE      | pers_appln  | ref    | idx_application,idx_person | idx_person      | 4       | mydb.person.PERSON_ID    |        1 |                                 |
|  1 | SIMPLE      | application | eq_ref | PRIMARY                    | PRIMARY         | 4       | mydb.pers_appln.APPLN_ID |        1 |                                 |
|  1 | SIMPLE      | group       | ref    | idx_application            | idx_application | 4       | mydb.pers_appln.APPLN_ID |        1 |                                 |
+----+-------------+-------------+--------+----------------------------+-----------------+---------+--------------------------+----------+---------------------------------+

3 个答案:

答案 0 :(得分:0)

验证key_buffer_size是否约为200M且innodb_buffer_pool_size约为1200M。也许他们可能更大,但要确保你没有交换。

group应该有PRIMARY KEY(appln_id, group_id)INDEX(group_id, appln_id),而不是它拥有的两个KEY。

pers_appln应该有INDEX(person_id, appln_id)INDEX(appln_id, person_id)而不是它拥有的两个密钥。如果可能的话,其中一个应该是PRIMARY KEY,但要注意PARTITIONing。

一个小小的改进是将那些CHAR(2)字段更改为字符集ascii - 假设你并不真的需要utf8。这会将字段从每行6个字节缩减到2个字节。

PARTITIONing可能根本没有帮助。 (不,我不能说删除PARTITIONing会加快它的速度。)

如果这些建议不够,请提供EXPLAIN SELECT ...

的输出

修改

转换为InnoDB 为所有表指定PRIMARY KEY将有所帮助。这是因为InnoDB"集群"带数据的PRIMARY KEY。你现在拥有的是MyISAM索引与其数据之间的大量反弹 - 实际上是数亿次。假设不是所有东西都可以缓存在你的小4GB中,这意味着很多磁盘I / O.如果非WHERE版本需要一周时间运行,我不会感到惊讶。即使使用InnoDB,也会有I / O,但有些可以避免,因为:
1.使用PK进入表格获取数据而不会触及另一个磁盘 2.我提出的额外索引将避免命中数据,再次避免额外的磁盘命中 (数百万个引用*"额外的磁盘命中" =几天的时间。)

如果将所有表切换到InnoDB,则应将key_buffer_size降低到20M并将innodb_buffer_pool_size提升到1500M。 (这些是近似的;不要将它们提高得太高以至于有任何交换。)

答案 1 :(得分:0)

请向我们展示InnoDB的CREATE TABLE - 我想确保每个表都有一个PRIMARY KEY和哪个列。在这种特殊情况下,PRIMARY KEY会产生差异。

对于person,MyISAM版本只有KEY(person_id)。如果您没有更改转换中的密钥,InnoDB将发明一个PRIMARY KEY。当JOIN到该表时,InnoDB将(1)向下钻取key的BTree以找到发明的PK值,然后(2)向下钻取PK +数据BTree以找到该行。相反,如果person_id可能是PK,那么JOIN的运行速度会快两倍。可能更快 - 取决于表的大小以及在索引/数据中需要多少跳转。也就是说,两个BTree查找增加了缓存上的压力(buffer_pool)。

每张桌子有多大? innodb_buffer_pool_size的最终值是多少?一旦您将所有内容从MyISAM更改为InnoDB,请将key_buffer_size设置为40M或更低,并将innodb_buffer_pool_size设置为可用RAM的大约70%。如果所有表的数据+索引大小都小于buffer_pool,那么(一旦启动了缓存),查询就不必进行任何I / O.这很容易加速10倍。

pers_appln是多对多的关系?然后,可能

PRIMARY KEY(appln_id, person_id),
INDEX(person_id, appln_id) -- if you need to go the other direction, too.

答案 2 :(得分:0)

我找到了解决方案:切换到SSD。我的表创建时间从大约45天增加到16小时。以前,数据库花了所有时间用于硬盘驱动器I / O,甚至几乎不使用5%的CPU或RAM。

谢谢大家。