【问题标题】:MySQL table data in Memory for read operation内存中的 MySQL 表数据用于读取操作
【发布时间】:2015-05-04 22:20:18
【问题描述】:

这听起来很奇怪,但我们有一个包含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 |

在表中,我们有以下数据。总行数约 120 万行

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负载。

【问题讨论】:

  • FLOAT(m,n) 几乎总是“错误的”。它说 (1) 舍入到“n”个小数位,然后舍入到 FLOAT 的二进制精度。另外,如果数字大于 m & n 指定的值,它会说要做一些讨厌的事情。为什么有两个四舍五入?使用 DECIMAL(m,n) 或普通 FLOAT。

标签: mysql sql sql-server performance sqlite


【解决方案1】:

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

请向我们展示查询以及 SHOW CREATE TABLE。可能有一些技术可以大大加快查询速度。一个示例是使用汇总表将数据仓库“报告”的典型速度提高 10 倍。更简单的技术涉及“复合”索引或“覆盖”索引。或者改写 WHERE 子句以避免在函数调用中隐藏索引列。有时,即使重新编写 SELECT 以使用子查询也可以加快速度。

【讨论】:

    【解决方案2】:

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

    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 中过滤的内容的子查询开始:

    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(); 
    

    这种重新制定背后的原则是只使用一个索引(我提到的新索引)来选择ids,然后再找到所有其他的东西。这有两个“加速”:

    (a) 在进行所有过滤时,“其他东西”没有被拖走,并且

    (b) 使用索引需要扫描更少的东西。 (此外,我们正在过滤索引中的enableddigits。)

    请为您想进一步讨论的任何 SELECT 提供EXPLAIN SELECT ...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-22
      • 1970-01-01
      相关资源
      最近更新 更多