我试图通过加入四个现有表来创建一个新表。我的数据库是静态的,因此制作一个大的预处理表将简化编程,并在将来的查询中节省大量时间。我的查询在受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 | |
+----+-------------+-------------+--------+----------------------------+-----------------+---------+--------------------------+----------+---------------------------------+
答案 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。
谢谢大家。