我有一个简单的 SELECT mysql 请求,用于按距离对用户进行排序,如下所示:
SELECT
( 6371 * acos( cos( radians(48.85980226) ) * cos( radians( latitude ) ) * cos( radians
( longitude ) - radians(2.29202271) ) + sin( radians(48.85980226) ) * sin( radians( latitude
) ) ) ) AS distance,
id FROM `users`
HAVING distance <= '100'
ORDER BY distance ASC
我的数据库 (MySql 5.7) 中有大约 50.000 个用户。 当我将表设置为MyISAM时,请求速度还算合理,大约0.2秒;但如果我把引擎转为innodb,大约需要8s! 我确实需要使用 innodb,因为数据经常被写入和读取(MyISAM 导致很多“myisam 等待表级锁”)。 知道如何优化该查询的速度吗? 谢谢!
(对不起我的英语)
EDIT2:我将坐标类型从 DECIMAL 更改为 FLOAT,并且查询速度更快一点:5 秒而不是 8 秒...
Edit3(来自评论,带有边界框)
SELECT ( 6371 * acos( cos( radians(48.85980226) ) *
cos( radians( latitude ) ) * cos( radians ( longitude ) -
radians(2.29202271) ) + sin( radians(48.85980226) ) *
sin( radians( latitude ) ) ) ) AS distance,
uid
FROM users
WHERE longitude between 0.089154409442052 AND 4.4948910105579
AND latitude between 47.410526897681 AND 50.309077622319
HAVING distance <= '100'
ORDER BY distance ASC
编辑 4:这是我的表结构:
CREATE TABLE `users`
( `id` mediumint(9) NOT NULL AUTO_INCREMENT,
`uid` varchar(20) NOT NULL,
`token` varchar(70) NOT NULL,
`last_connection` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`email` varchar(255) NOT NULL,
`longitude` float NOT NULL,
`latitude` float NOT NULL,
`presentation` text NOT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
KEY `uid_token` (`uid`,`token`),
KEY `longitude` (`longitude`),
KEY `latitude` (`latitude`)
) ENGINE=InnoDB AUTO_INCREMENT=53004 DEFAULT CHARSET=utf8
字段last_connection
更新非常频繁。在线用户越多,查询速度就越慢...我猜由于更新,行被临时锁定并且查询速度变慢...:/
使用MyISAM时,搜索查询还可以,但是更新速度很慢(等待锁)
编辑 5 这是我的更新查询:
UPDATE `users` SET `last_connection` = CURRENT_TIMESTAMP WHERE `uid` = 'XXXX';
我改变了它并添加了限制1:
UPDATE `users` SET `last_connection` = CURRENT_TIMESTAMP WHERE `uid` = 'XXXX' LIMIT 1;
这似乎更快。我需要等待更多用户连接来检查差异是否很大
最佳答案
数据库引擎对每一行进行计算。
那么将已经计算出的值存储为变量怎么样?
SET @cos_point1 = cos(radians(48.85980226));
SET @rad_point1 = radians(2.29202271);
SET @sin_point1 = sin(radians(48.85980226));
SELECT
( 6371 * acos( @cos_point1 * cos( radians( latitude ) ) * cos( radians
( longitude ) - @rad_point1 ) + @sin_point1 * sin( radians( latitude
) ) ) ) AS distance,
id FROM `users`
HAVING distance <= 100
ORDER BY distance ASC;
我还有一个想法!
试试这个:
1)创建Memory类型的表users_geodata
(因为真正的数据将在users
表中,所以我们对临时表使用最快的引擎):
CREATE TABLE `users_geodata` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`latitude` float NOT NULL,
`longitude` float NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=Memory;
2) 安排执行此操作的同步:
REPLACE INTO users_geodata
SELECT id, latitude, longitude FROM users
3) 运行您的查询:
SELECT
( 6371 * acos( cos( radians(48.85980226) ) * cos( radians( latitude ) ) * cos( radians
( longitude ) - radians(2.29202271) ) + sin( radians(48.85980226) ) * sin( radians( latitude
) ) ) ) AS distance,
id FROM `users_geodata`
HAVING distance <= 100
ORDER BY distance ASC
关于mysql - 与 MyISAM 相比,innodb 的距离搜索非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38619856/