优化并加快MySQL查询选择

时间:2018-11-20 13:26:41

标签: mysql select query-optimization rdbms

我正在尝试找出哪种方法是优化MySQL数据库上当前选择查询的最佳方法。

我有2个MySQL表,它们之间的关系为一对多。其中一个是user表,其中包含唯一的用户列表,它约有 22krows 。一个是linedata表,其中包含每个用户的所有可能坐标,并且大约有 490k行

在这种情况下,我们可以假设两个表之间的外键id值。对于用户表,id也是自动递增的主键,而在行数据表中,它不是主键,因为我们可以为同一用户添加更多行。

创建STMT结构

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `isActive` tinyint(4) NOT NULL,
  `userId` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `gender` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21938 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE `linedata` (
  `id` int(11) NOT NULL,
  `userId` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `timestamp` datetime NOT NULL,
  `x` float NOT NULL,
  `y` float NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

选择查询

SELECT 
        u.id, 
        u.isActive, 
        u.userId,
        u.name,
        u.gender,
        u.age,
        GROUP_CONCAT(CONCAT_WS(', ',timestamp,x, y)
                     ORDER BY timestamp ASC SEPARATOR '; '
                    ) as linedata_0

        FROM user u 
        JOIN linedata l
        ON u.id=l.id
        WHERE DATEDIFF(l.timestamp, '2018-02-28T20:00:00.000Z') >= 0
          AND DATEDIFF(l.timestamp, '2018-11-20T09:20:08.218Z') <= 0
        GROUP BY userId;

EXPLAIN输出

+-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
    |   ID  |   SELECT_TYPE |   TABLE   |   TYPE    |   POSSIBLE_KEYS   |   KEY     |   KEY_LEN     |   REF     |   ROWS    |       EXTRA                                                |
    +-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
    |   1   |   SIMPLE      |   l      |   ALL   |   NULL         |   NULL |      NULL        |   NULL    |   491157   |   "Using where; Using temporary; Using filesort" |
    +-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
    |   1   |   SIMPLE      |   u      |   eq_ref  |   PRIMARY         |   PRIMARY |      4        |   l.id   |   1       |     NULL                                                   |
    +-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+

例如,如果我为过滤器单个用户添加另一个WHERE条件,则选择查询有效。假设我只想选择 200 个用户,然后我得到了大约 14秒作为执行时间。如果我仅选择前 100个用户,则大约需要 7秒。但是在只有日期时间范围条件的情况下,它似乎没有终点就加载了。有什么建议吗?

更新

在遵循Rick的建议之后,查询基准时间约为14秒。在EXPLAIN EXTENDED下面:

id,select_type,表,类型,可能的键,键,key_len,ref,行,已过滤,额外 1,PRIMARY,u,index,PRIMARY,PRIMARY,4,NULL,21959,100.00,NULL 1,PRIMARY,l,ref,id_timestamp_index,id_timestamp_index,4,u.id,14,100.00,“使用索引条件” 2,“ DEPENDENT SUBQUERY”,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,“未使用表”

我对表的某些值做了一些更改:

linedata table user table

用户表中的id可以与linedata表中的userId连接的地方。而且它们现在是整数。我们将只为用户表中的userId值提供字符串类型,因为它是一种长字符串标识符,例如 0000309ab2912b2fd34350d7e6c079846bb6c5e1f97d3ccb053d15061433e77a_0

因此,仅举一个简短的例子,我们将在userlinedata表中提供

+-------+-----------+-----------+-------------------+--------+---+
|   id  | isActive  |   userId  |       name        | gender |age|
+-------+-----------+-----------+-------------------+--------+---+
|   1   |   1       |  x4by4d   |   john            | m      | 22|
|   2   |   1       |  3ub3ub   |   bob             | m      | 50|
+-------+-----------+-----------+-------------------+--------+---+



+-------+-----------+-----------+------+---+
|   id  | userId    |timestamp  |  x   | y |
+-------+-----------+-----------+------+----+
|   1   |   1       | somedate  |  30  | 10 |
|   2   |   1       | somedate  |  45  | 15 |
|   3   |   1       | somedate  |  50  | 20 |
|   4   |   2       | somedate  |  20  |  5 |
|   5   |   2       | somedate  |  25  | 10 |
+-------+-----------+-----------+------+----+

我在行数据表中添加了由userIdtimestamp值组成的化合物索引

如果我添加由linedata + userId组成的复合主键,也许不是将timestamp表的ai id值用作主键?是否应该提高性能?

1 个答案:

答案 0 :(得分:2)

在讨论性能之前,我需要帮助您修复一些错误。

首先,'2018-02-28T20:00:00.000Z'在MySQL中不起作用。它必须为'2018-02-28 20:00:00.000',并且需要对时区进行一些操作。

然后,不要“隐藏函数中的列”。也就是说,DATEDIFF(l.timestamp ...)不能在timestamp上使用任何索引。

所以,而不是

    WHERE  DATEDIFF(l.timestamp, '2018-02-28T20:00:00.000Z') >= 0
      AND  DATEDIFF(l.timestamp, '2018-11-20T09:20:08.218Z') <= 0

做类似的事情

    WHERE  l.timestamp >= '2018-02-28 20:00:00.000'
      AND  l.timestamp  < '2018-11-20 09:20:08.218'

我对两个表感到困惑。两者都有iduserid,但是您加入了id。也许代替

CREATE TABLE `linedata` (
  `id` int(11) NOT NULL,
  `userId` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  ...

你的意思

CREATE TABLE `linedata` (
  `id` int(11) NOT NULL  AUTO_INCREMENT,  -- (the id for `linedata`)
  `userId` int NOT NULL,   -- to link to the other table
  ...
  PRIMARY KEY(id)
...

然后每个linedata可能会有几行user行。

那时,这个

    JOIN  linedata l  ON u.id=l.id

成为

    JOIN  linedata l  ON u.id=l.userid

现在,为了提高性能:linedata需要INDEX(userid, timestamp)-按此顺序。

现在,考虑一下输出。您要查询的行数最多为22K,在一列中可能有数百个“ ts,x,y”串在一起。什么会收到这么多数据?会窒息吗?

并且GROUP_CONCAT默认限制为1024个字节。这将允许大约50点。如果9天之内“用户”的排名超过50,请在运行查询之前考虑增加group_concat_max_len

要使其更快地工作,请按以下方式重新设置:

SELECT  u.id, u.isActive, u.userId, u.name, u.gender, u.age,
        ( SELECT  GROUP_CONCAT(CONCAT_WS(', ',timestamp, x, y)
                      ORDER BY timestamp ASC
                      SEPARATOR '; ')
        ) as linedata_0
    FROM  user u
    JOIN  linedata l  ON u.id = l.userid
    WHERE  l.timestamp >= '2018-02-28 20:00:00.000'
      AND  l.timestamp  < '2018-11-20 09:20:08.218';

另一件事。您可能希望能够通过name查找用户;因此添加INDEX(name)

哦,VARCHAR(255)的{​​{1}}到底是什么?? id通常是整数。