听起来很奇怪,但我们的表格中包含millions
行数据,我们正在使用JOIN
,AND
,ORDER BY
等操作运行查询,因此查询运行缓慢,查询扫描全表并查找最佳匹配结果,当前单个查询需要2到3秒,这是不可接受的:(
我可以在Memory
中加载完整的表并执行读操作吗?我们不是在表格中写入数据,因此我们不关心power off
上的数据丢失。如果需要,我们可以再次在内存中快速加载表。
我已尝试使用mysqltunner
进行所有自定义等但没有帮助。
你是否建议在内存中加载表有助于read
操作?
我们的其他解决方案是sqlite
将文件放在memory
上并对其运行查询。
| lcr | CREATE TABLE `lcr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`digits` varchar(15) DEFAULT NULL,
`rate` float(11,5) unsigned DEFAULT NULL,
`intrastate_rate` float(11,5) unsigned DEFAULT NULL,
`intralata_rate` float(11,5) unsigned DEFAULT NULL,
`carrier_id` int(11) NOT NULL,
`lead_strip` int(11) NOT NULL,
`trail_strip` int(11) NOT NULL,
`prefix` varchar(16) NOT NULL,
`suffix` varchar(16) NOT NULL,
`lcr_profile` int(11) NOT NULL DEFAULT '0',
`date_start` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
`date_end` datetime NOT NULL DEFAULT '2030-12-31 00:00:00',
`quality` float(10,6) NOT NULL,
`reliability` float(10,6) NOT NULL,
`cid` varchar(32) NOT NULL DEFAULT '',
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`lrn` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `carrier_id` (`carrier_id`),
KEY `digits` (`digits`),
KEY `lcr_profile` (`lcr_profile`),
KEY `rate` (`rate`),
KEY `digits_profile_cid_rate` (`digits`,`rate`) USING BTREE,
CONSTRAINT `carrier_id` FOREIGN KEY (`carrier_id`) REFERENCES `carriers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1130954 DEFAULT CHARSET=latin1 |
在表格中我们有以下数据。总行数为1.2百万左右
mysql> select * from lcr LIMIT 0,10;
+-----+--------+---------+-----------------+----------------+------------+------------+-------------+--------+--------+-------------+---------------------+---------------------+----------+-------------+-----+---------+-----+
| id | digits | rate | intrastate_rate | intralata_rate | carrier_id | lead_strip | trail_strip | prefix | suffix | lcr_profile | date_start | date_end | quality | reliability | cid | enabled | lrn |
+-----+--------+---------+-----------------+----------------+------------+------------+-------------+--------+--------+-------------+---------------------+---------------------+----------+-------------+-----+---------+-----+
| 1 | 91 | 0.00010 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2015-09-11 15:53:39 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 2 | 91 | 0.01000 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2015-09-11 15:53:39 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 8 | 1 | 0.00700 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2013-09-11 16:56:38 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 0 | 0 |
| 9 | 91 | 0.00100 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2015-09-11 15:53:39 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 0 | 0 |
| 130 | 1844 | 0.00000 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2013-12-08 20:03:45 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 131 | 998 | 0.07070 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2014-02-11 09:20:00 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 132 | 9989 | 0.09490 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2014-02-11 09:20:00 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 133 | 99899 | 0.09490 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2014-02-11 09:20:00 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 134 | 99898 | 0.09490 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2014-02-11 09:20:00 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
| 135 | 99897 | 0.09490 | NULL | NULL | 13 | 0 | 0 | | | 0 | 2014-02-11 09:20:00 | 2015-02-28 00:00:00 | 0.000000 | 0.000000 | | 1 | 0 |
+-----+--------+---------+-----------------+----------------+------------+------------+-------------+--------+--------+-------------+---------------------+---------------------+----------+-------------+-----+---------+-----+
10 rows in set (0.00 sec)
mysql> SELECT l.digits AS lcr_digits, c.carrier_name AS lcr_carrier_name, l.rate AS lcr_rate_field, cg.prefix AS lcr_gw_prefix, cg.suffix AS lcr_gw_suffix, l.lead_strip AS lcr_lead_strip, l.trail_strip AS lcr_trail_strip, l.prefix AS lcr_prefix, l.suffix AS lcr_suffix, cg.codec AS lcr_codec, l.cid AS lcr_cid FROM lcr l JOIN carriers c ON l.carrier_id=c.id JOIN carrier_gateway cg ON c.id=cg.carrier_id WHERE c.enabled = '1' AND cg.enabled = '1' AND l.enabled = '1' AND ( (digits IN (16623033747, 1662303374, 166230337, 16623033, 1662303, 166230, 16623, 1662, 166, 16, 1) AND lrn = false) OR (digits IN (16623033747, 1662303374, 166230337, 16623033, 1662303, 166230, 16623, 1662, 166, 16, 1) AND lrn = true)) AND CURRENT_TIMESTAMP BETWEEN date_start AND date_end ORDER BY digits DESC, rate, rand();
+------------+------------------+----------------+---------------+-------------------------+----------------+-----------------+------------+------------+-----------+---------+
| lcr_digits | lcr_carrier_name | lcr_rate_field | lcr_gw_prefix | lcr_gw_suffix | lcr_lead_strip | lcr_trail_strip | lcr_prefix | lcr_suffix | lcr_codec | lcr_cid |
+------------+------------------+----------------+---------------+-------------------------+----------------+-----------------+------------+------------+-----------+---------+
| 1662303 | abc | 0.00395 | | @sip.abc.com | 0 | 0 | | | | |
| 1 | xyz | 0.00980 | 73734599* | @sip.xyz.com | 0 | 0 | | | | |
| 1 | Silver | 0.01020 | | @sip.Silver.com | 0 | 0 | | | | |
+------------+------------------+----------------+---------------+-------------------------+----------------+-----------------+------------+------------+-----------+---------+
3 rows in set (2.06 sec)
它使用查询提供的数字扫描digit
并从其他表中查找值,但这些表非常小,只有少数条目,它花了2 sec
但是当多个查询运行时它会达到CPU负载。
答案 0 :(得分:0)
你的百万行可能已经在内存中了。也就是说,MyISAM和InnoDB具有缓存技术,这些技术通常可以有效地加载并保持加载所需的索引和/或数据。
请向我们展示查询,以及SHOW CREATE TABLE。可能有一些技术可以大大加快查询速度。一个例子是数据仓库的典型10倍加速"报告"通过使用汇总表。更简单的技术涉及"化合物"索引,或覆盖"指数。或者重新描述WHERE子句以避免在函数调用中隐藏索引列。有时,即使重新构造SELECT以使用子查询也可以加快它的速度。
答案 1 :(得分:0)
三种可能的加速。 (我说“可能”,因为查询有多复杂。)
添加INDEX(date_start),INDEX(date_end)
注意lrn
与此OR无关:
AND ( (digits IN (16623033747, 1662303374, 166230337, 16623033,
1662303, 166230, 16623, 1662, 166, 16, 1)
AND lrn = false)
OR (digits IN (16623033747, 1662303374, 166230337, 16623033,
1662303, 166230, 16623, 1662, 166, 16, 1)
AND lrn = true)
)
并将其简化为
AND digits IN (16623033747, 1662303374, 166230337, 16623033,
1662303, 166230, 16623, 1662, 166, 16, 1)
从包含l
中过滤的内容的子查询开始:
SELECT id FROM lcr AS l WHERE l.enabled = '1' AND ( (digits IN (16623033747, 1662303374, 166230337, 16623033, 1662303, 166230, 16623, 1662, 166, 16, 1) AND lrn = false) OR (digits IN (16623033747, 1662303374, 166230337, 16623033, 1662303, 166230, 16623, 1662, 166, 16, 1) AND lrn = true) ) AND CURRENT_TIMESTAMP BETWEEN date_start AND date_end;
和INDEX(enabled, digits, lrn, date_start, date_end, id)
一起 - 这需要从启用开始(因为'='),但其余的顺序并不重要。
然后,围绕它构建剩下的查询,包括“自联接”回到lcr:
SELECT l.digits AS lcr_digits, c.carrier_name AS lcr_carrier_name,
l.rate AS lcr_rate_field, cg.prefix AS lcr_gw_prefix,
cg.suffix AS lcr_gw_suffix, l.lead_strip AS lcr_lead_strip,
l.trail_strip AS lcr_trail_strip, l.prefix AS lcr_prefix,
l.suffix AS lcr_suffix, cg.codec AS lcr_codec, l.cid AS lcr_cid
FROM ( <the subquery above> ) l2
JOIN lcr l USING(id)
JOIN carriers c ON l.carrier_id=c.id
JOIN carrier_gateway cg ON c.id=cg.carrier_id
WHERE c.enabled = '1'
AND cg.enabled = '1'
ORDER BY l.digits DESC, l.rate, rand();
这种重新制定背后的原则是仅使用索引(我提到的新索引)选择id
,然后达到所有其他东西。这有两个“加速”:
(a)在进行所有过滤时,“其他东西”没有被拖走,
(b)使用索引需要扫描更少的东西。 (另外,我们正在过滤enabled
,也可能在索引中过滤digits
。)
请提供EXPLAIN SELECT ...
任何您想要进一步讨论的SELECT。