我有一个销售数据表,每天平均插入1,329,415行。我必须每天从表中以不同格式生成报告。但是从表中查询太慢了。这是我的SHOW CREATE TABLE命令的输出。
CREATE TABLE `query_manager_table` (
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`region_id` int(2) NOT NULL,
`rtslug` varchar(10) DEFAULT NULL,
`dsid` int(3) NOT NULL,
`dpid` int(3) NOT NULL,
`route_number` int(4) NOT NULL,
`route_id` int(11) NOT NULL,
`rtlid` int(11) NOT NULL,
`retailer_code` varchar(16) DEFAULT NULL,
`platform_code` varchar(16) DEFAULT NULL,
`prid` int(4) NOT NULL,
`skid` int(4) NOT NULL,
`group` int(4) NOT NULL,
`family` int(4) NOT NULL,
`volume` float DEFAULT NULL,
`value` float(7,2) DEFAULT NULL,
`date` date NOT NULL DEFAULT '0000-00-00',
`outlets` int(4) NOT NULL,
`visited` int(4) NOT NULL,
`channel` int(3) DEFAULT NULL,
`subchannel` int(3) DEFAULT NULL,
`tpg` int(4) DEFAULT NULL,
`ioq` int(10) DEFAULT NULL,
`sales_time` int(11) DEFAULT NULL,
PRIMARY KEY (`dpid`,`route_id`,`rtlid`,`prid`,`skid`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY LIST (YEAR(date) * 100 + QUARTER(date))
(PARTITION y2017q1 VALUES IN (201701) ENGINE = InnoDB,
PARTITION y2017q2 VALUES IN (201702) ENGINE = InnoDB,
PARTITION y2017q3 VALUES IN (201703) ENGINE = InnoDB,
PARTITION y2017q4 VALUES IN (201704) ENGINE = InnoDB,
PARTITION y2018q1 VALUES IN (201801) ENGINE = InnoDB,
PARTITION y2018q2 VALUES IN (201802) ENGINE = InnoDB,
PARTITION y2018q3 VALUES IN (201803) ENGINE = InnoDB,
PARTITION y2018q4 VALUES IN (201804) ENGINE = InnoDB,
PARTITION y2019q1 VALUES IN (201901) ENGINE = InnoDB,
PARTITION y2019q2 VALUES IN (201902) ENGINE = InnoDB,
PARTITION y2019q3 VALUES IN (201903) ENGINE = InnoDB,
PARTITION y2019q4 VALUES IN (201904) ENGINE = InnoDB) */
现在,我只想通过查询以下信息来了解9月1日至9月9日零售商的销售情况-
SELECT
query_manager_table.dpid,
query_manager_table.route_id,
query_manager_table.rtlid,
query_manager_table.prid,
SUM(query_manager_table.`volume`) AS sales,
1 AS memos
FROM
query_manager_table
WHERE
query_manager_table.date BETWEEN '2018-09-01'
AND '2018-09-08'
GROUP BY
query_manager_table.dpid,
query_manager_table.rtlid,
query_manager_table.date
但是大约需要500-700秒。我已经添加了dpid IN (1,2,.....)
和prid IN (1,2,....)
,因为两个文件都被添加为主键。然后300秒后输出。我做错了什么?
+----+-------------+---------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
| 1 | SIMPLE | query_manager_table | ALL | PRIMARY | NULL | NULL | NULL | 129065467 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
当我在条件中添加所有dpid和prid时,EXPAIN看起来像
+----+-------------+---------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| 1 | SIMPLE | query_manager_table | range | PRIMARY | PRIMARY | 4 | NULL | 128002 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
有什么方法可以优化表或查询? 如果我为第一个运行EXPLAIN PARTITIONS SELECT ...,则得到-
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
| 1 | SIMPLE | query_manager_table | y2017q1,y2017q2,y2017q3,y2017q4,y2018q1,y2018q2,y2018q3,y2018q4,y2019q1,y2019q2,y2019q3,y2019q4 | ALL | PRIMARY | NULL | NULL | NULL | 127129410 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
第二个我得到-
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| 1 | SIMPLE | query_manager_table | y2017q1,y2017q2,y2017q3,y2017q4,y2018q1,y2018q2,y2018q3,y2018q4,y2019q1,y2019q2,y2019q3,y2019q4 | range | PRIMARY | PRIMARY | 4 | NULL | 153424 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
答案 0 :(得分:1)
INDEXes
用于提高SELECTs
中的效率。
根据定义,一个PRIMARY KEY
(在MySQL中)是唯一的INDEX
。它应该具有最少的一组唯一标识一行的列。
任何唯一索引(包括PK)也是一个“唯一性约束”,这可以防止插入多个具有相同set(如果有值)的行。
索引从“左起”使用。也就是说,对于INDEX(a,b)
,如果a
没用,它将不会进入b
。
PARTITION BY LIST
实际上是没有用的。即使有,也很少会提高性能。您向我们展示了一些查询;让我们看看更多的典型查询,以便我们可以帮助您进行索引和分区。
WHERE
query_manager_table.date BETWEEN '2018-09-01'
AND '2018-09-08'
乞求INDEX(date)
。在复合索引中,不会到达“范围”之后的列。也就是说,在INDEX(date, x, y)
中,对date
进行一定范围的测试(例如WHERE
中的8天),将不允许它使用x
或{{ 1}}。另一方面,y
将使用更多索引。
WHERE date = '2018-09-01' AND x=1
-请勿在{{1}}或float(7,2)
上使用(m,n)
选项。而是切换到FLOAT
。
DOUBLE
始终为4个字节。请参见DECIMAL
(1个字节),INT
(2个字节)等。仅此一项,就可以将表的大小减半。
对此进行解释:
TINYINT
管理对伪距SMALLINT
使用第一个(记住:'leftmost'),但是由于下一个PRIMARY KEY (`dpid`,`route_id`, ...
WHERE ... AND dpid IN (...) AND ...
,因此不能在PK中使用其他任何内容。
这说明了为什么第二个IN
具有较小的“行”。另外,请注意“ key_len”中的“ 4”,即route_id
中的字节数。
在进行了一些更改之后,请回来,以便我们可以讨论使用摘要表来加快处理速度。但是,“修改”可能会导致此优化的复杂性。
您有多少RAM? EXPLAIN
的值是什么?
除非必须使用,否则请不要使用GUID。由于随机性,它们会使大型表上的动作变慢。
答案 1 :(得分:0)
我不会结合实际的数据字段来构成主键。我将只有一个字段,并使用一个自动递增的整数或该值的GUID。必须经过六个字段来标识唯一记录比经过一个要花费更多的时间,并且正如您所说的,如果用户输入关键数据,则存在重复字段的风险。
如果出于商业原因要使这六个字段在一起时唯一,则还应制定例程以识别插入的记录是否与这些字段重复。如果您要批量插入,则需要在插入记录后执行此操作,而不是在插入记录时对其进行检查。您还需要对这六个字段建立索引,以加快查询重复项的速度。
对于您的SELECT
查询,您可能希望为WHERE
子句中的字段建立索引。无论如何,您都需要阅读执行计划并尝试使用不同的索引和键结构(可能更容易对数据的一部分进行操作)。 Google提供了“ mysql执行计划”,以获取大量信息。