内存中的MySQL表数据用于读操作

时间:2015-03-04 02:43:39

标签: mysql sql sql-server performance sqlite

听起来很奇怪,但我们的表格中包含millions行数据,我们正在使用JOINANDORDER 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负载。

2 个答案:

答案 0 :(得分:0)

你的百万行可能已经在内存中了。也就是说,MyISAM和InnoDB具有缓存技术,这些技术通常可以有效地加载并保持加载所需的索引和/或数据。

请向我们展示查询,以及SHOW CREATE TABLE。可能有一些技术可以大大加快查询速度。一个例子是数据仓库的典型10倍加速"报告"通过使用汇总表。更简单的技术涉及"化合物"索引,或覆盖"指数。或者重新描述WHERE子句以避免在函数调用中隐藏索引列。有时,即使重新构造SELECT以使用子查询也可以加快它的速度。

答案 1 :(得分:0)

三种可能的加速。 (我说“可能”,因为查询有多复杂。)

  1. 添加INDEX(date_start),INDEX(date_end)

  2. 注意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)
    
  3. 从包含l中过滤的内容的子查询开始:

  4. 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。