MySQL挂在大型SELECT上

MySQL挂在大型SELECT上,mysql,Mysql,我试图通过连接四个现有表来创建一个新表。我的数据库是静态的,所以制作一个大的预处理表将简化编程,并在将来的查询中节省大量时间。当使用WHERE限制时,我的查询工作正常,但似乎要么挂起,要么走得太慢,没有注意到任何进展 这是工作查询。结果只需要几秒钟 SELECT group.group_id, MIN(application.date), person.person_name, pers_appln.sequence FROM group JOIN application ON group.ap

我试图通过连接四个现有表来创建一个新表。我的数据库是静态的,所以制作一个大的预处理表将简化编程,并在将来的查询中节省大量时间。当使用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也会做同样的事情。它从不超过0%的进度

组、应用程序和人员表都使用MyISAM引擎,而pers_appln使用InnoDB。所有列都已编制索引。表格大小从大约4000万行到1.5亿行不等。我知道它相当大,但我不认为它会造成这么大的问题。这台计算机目前有4GB的ram

有没有办法让这一切顺利进行

下面是显示创建表信息。没有视图或虚拟表:

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 */
以下是对我的问题的解释:

+----+-------------+-------------+--------+----------------------------+-----------------+---------+--------------------------+----------+---------------------------------+
| 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 |                                 |
+----+-------------+-------------+--------+----------------------------+-----------------+---------+--------------------------+----------+---------------------------------+

验证密钥缓冲池大小约为200M,innodb缓冲池大小约为1200M。也许他们可以更大,但请确保你没有交换

组应具有主键appln_id、组id和索引group_id、appln_id,而不是它具有的两个键

pers_appln应该有INDEXperson_id、appln_id和INDEXappln_id、person_id,而不是它的两个键。如果可能,其中一个应该是主键,但要注意分区

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

分区可能根本没有帮助。不,我不能说删除分区会加快速度

如果这些建议没有足够的帮助,请提供解释选择

编辑

转换为InnoDB并为所有表指定主键将有所帮助。这是因为InnoDB将主键与数据聚集在一起。你现在看到的是MyISAM索引和它的数据之间的大量跳跃——实际上是数亿次。假设不是所有东西都可以缓存在小型4GB中,这意味着大量的磁盘I/O。如果非WHERE版本需要一周时间才能运行,我不会感到惊讶。即使使用InnoDB,也会有I/O,但会避免一些I/O,因为: 1.使用PK访问表可以在没有再次命中磁盘的情况下获取数据。 2.我建议的额外索引将避免命中数据,再次避免额外的磁盘命中。 数以百万计的引用*额外的磁盘命中=几天的时间


如果将所有表切换到InnoDB,则应将键缓冲区大小降低到20M,并将InnoDB缓冲区池大小提高到1500M。这些是近似值;请向我们展示使用InnoDB创建表-我想确保每个表都有一个主键,以及哪些列是主键。主键在这种特殊情况下起着很大的作用

对于person,MyISAM版本只有一个KEYperson\u id。如果在转换过程中没有更改密钥,InnoDB将发明一个主键。当连接到该表时,InnoDB将1向下钻取BTree中的键以查找该发明的PK值,然后2向下钻取PK+数据BTree以查找该行。相反,如果person_id可以成为PK,那么该连接的运行速度将是PK的两倍。可能更快,这取决于表有多大以及需要在索引/数据中跳转多少。也就是说,两个BTree查找增加了缓存缓冲池的压力

每张桌子有多大?innodb_buffer_pool_size的最终值是多少?一旦您将所有内容从MyISAM更改为InnoDB,请将key_buffer_size设置为40M或更小,并将InnoDB_buffer_pool_size设置为可用RAM的70%左右。如果所有表的数据+索引大小都小于缓冲池,那么一旦缓存被初始化,查询就不必进行任何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.

我找到了解决方案:切换到SSD。我的表创建时间从估计的45天增加到16小时。以前,数据库的所有时间都花在硬盘I/O上,几乎不使用5%的CPU或RAM


谢谢大家。

您的机器是否有足够的内存来执行此操作?你可能会遇到一堆磁盘操作系统。另外,序列属于哪个表?能否在组id、序列上创建索引?在如此大的表之间创建此叉积将产生一个非常大的中间表。@Barmar叉积是什么意思?@Rachie您需要为查询中的每个表提供EXPLAIN+SHOW create table。联接是表之间的叉积,按ON条件筛选。我更改了密钥缓冲区和池大小
e、 没有效果。我添加了解释选择。有多少百分比的组行的组id=24601?该解释是否反映了?还是没有?WHERE子句?解释中没有WHERE。我只使用WHERE来帮助编写和测试查询。你想得到一个4000万行的结果集吗?不管怎样,这都需要很多时间。我的建议会加快速度,但仍然需要几分钟,也许几个小时。