我正在尝试找出哪种方法是优化MySQL数据库上当前选择查询的最佳方法。
我有2个MySQL表,它们之间的关系为一对多。其中一个是user
表,其中包含唯一的用户列表,它约有 22krows 。一个是linedata
表,其中包含每个用户的所有可能坐标,并且大约有 490k行。
在这种情况下,我们可以假设两个表之间的外键是id
值。对于用户表,id也是自动递增的主键,而在行数据表中,它不是主键,因为我们可以为同一用户添加更多行。
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;
+-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
| 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,“未使用表”
我对表的某些值做了一些更改:
用户表中的id
可以与linedata表中的userId
连接的地方。而且它们现在是整数。我们将只为用户表中的userId值提供字符串类型,因为它是一种长字符串标识符,例如 0000309ab2912b2fd34350d7e6c079846bb6c5e1f97d3ccb053d15061433e77a_0 。
因此,仅举一个简短的例子,我们将在user
和linedata
表中提供
+-------+-----------+-----------+-------------------+--------+---+
| 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 |
+-------+-----------+-----------+------+----+
我在行数据表中添加了由userId
和timestamp
值组成的化合物索引。
如果我添加由linedata
+ userId
组成的复合主键,也许不是将timestamp
表的ai id值用作主键?是否应该提高性能?
答案 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'
我对两个表感到困惑。两者都有id
和userid
,但是您加入了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通常是整数。