尽管使用了索引,但 MySQL 地理空间查询非常慢

nen*_*007 8 mysql performance spatial query-performance

我需要按距离从 InnoDb 表中获取记录(不能完全正确)并按距离排序。该表有 1000 万条记录。

到目前为止,我最好的时间是 8 秒(没有按距离排序的 3 秒),这使得它无法使用。我该如何改进?

我有一个定义为 SRID 4326 的点列。我使用的是 MySQL 8.0.12。

SELECT mp.hash_id, 
ROUND(ST_Distance(ST_SRID(POINT(8.53955, 47.37706), 4326), mp.geo_pt), 2) AS distance
  FROM member_profile mp 
  WHERE
    MBRCONTAINS(ST_GeomFromText(
      CONCAT('POLYGON((', ST_X(POINT (8.53955, 47.37706)) - 0.43415340086831, ' ',
        ST_Y(POINT (8.53955, 47.37706)) - 0.43415340086831, ',',
        ST_X(POINT (8.53955, 47.37706)) + 0.43415340086831, ' ',
        ST_Y(POINT (8.53955, 47.37706)) - 0.43415340086831, ',',
        ST_X(POINT (8.53955, 47.37706)) + 0.43415340086831, ' ',
        ST_Y(POINT (8.53955, 47.37706)) + 0.43415340086831, ',',
        ST_X(POINT (8.53955, 47.37706)) - 0.43415340086831, ' ',
        ST_Y(POINT (8.53955, 47.37706)) + 0.43415340086831, ',',
        ST_X(POINT (8.53955, 47.37706)) - 0.43415340086831, ' ',
        ST_Y(POINT (8.53955, 47.37706)) - 0.43415340086831, ')) ')
           , 4326), geo_pt)
-- ST_Distance(ST_GeomFromText('POINT (8.53955 47.37706)', 4326), mp.geo_pt) <= 25000 -- need 16 sec
-- order by distance -- need 8 sec with MBRContains, 100 sec with ST_Distance
LIMIT 50;
Run Code Online (Sandbox Code Playgroud)

创建了空间索引:

CREATE SPATIAL INDEX geo_pt_index ON mp (geo_pt);
Run Code Online (Sandbox Code Playgroud)

EXPLAIN 显示我使用了我的 geo_pt 索引。

我的.cnf

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
innodb_buffer_pool_size = 12G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
key_buffer_size = 1G
secure-file-priv = ""
Run Code Online (Sandbox Code Playgroud)

这个服务器只分配给这个数据库,没有负载(除非我执行查询)。不存在 IOPS 瓶颈。innodb_buffer_pool_size 的大小可以将整个数据集保存在内存中。

服务器实例有 16 GB 内存,使用快速 NVMe SSD(没有 IOPS 瓶颈)。服务器只托管这个数据库,除了查询没有负载。使用了 30% 的磁盘。

SHOW GLOBAL STATUS输出:https : //pastebin.com/EMeNL8yT

SHOW GLOBAL VARIABLES输出:https : //pastebin.com/yxzYn10E

MySQL 调谐器输出:https : //pastebin.com/NRWFQDMQ

我今天从 8.0.11 更新到 8.0.12,但主要遵循早期 MySQL Tuner Recommendations 的所有相关建议。MySQL 更新是针对一些修复了空间搜索的 Bug 完成的,之前速度是一样的。

显示警告(查询执行后):

Level,Code,Message
Note,1003,/* select#1 */ select `***`.`mp`.`member_id` AS `member_id`,round(st_distance(st_pointfromtext('POINT(8.53955 47.37706)',4326),`***`.`mp`.`geo_pt`),2) AS `distance` from `***`.`member_profile` `mp` where mbrcontains(<cache>(st_geomfromtext(concat('POLYGON((',(st_x(point(8.53955,47.37706)) - 0.43415340086831),' ',(st_y(point(8.53955,47.37706)) - 0.43415340086831),',',(st_x(point(8.53955,47.37706)) + 0.43415340086831),' ',(st_y(point(8.53955,47.37706)) - 0.43415340086831),',',(st_x(point(8.53955,47.37706)) + 0.43415340086831),' ',(st_y(point(8.53955,47.37706)) + 0.43415340086831),',',(st_x(point(8.53955,47.37706)) - 0.43415340086831),' ',(st_y(point(8.53955,47.37706)) + 0.43415340086831),',',(st_x(point(8.53955,47.37706)) - 0.43415340086831),' ',(st_y(point(8.53955,47.37706)) - 0.43415340086831),')) '),4326)),`***`.`mp`.`geo_pt`) order by `distance` limit 50
Run Code Online (Sandbox Code Playgroud)

解释:

id,select_type,table,partitions,type,possible_keys,key,
key_len,ref,rows,filtered,Extra
1,SIMPLE,mp,\N,range,geo_pt_index,geo_pt_index,34,\N,23,100.00,Using where; Using filesort
Run Code Online (Sandbox Code Playgroud)

创建表:

CREATE TABLE `member_profile` (
  `member_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `hash_id` varchar(32)
        CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `geo_pt` point NOT NULL /*!80003 SRID 4326 */,
  PRIMARY KEY (`member_id`),
  UNIQUE KEY `hash_id` (`hash_id`),
  SPATIAL KEY `geo_pt_index` (`geo_pt`)
) ENGINE=InnoDB AUTO_INCREMENT=10498210
            DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Run Code Online (Sandbox Code Playgroud)

显示索引来自:

Table,Non_unique,Key_name,Seq_in_index,Column_name,Collation,
Cardinality,Sub_part,
Packed,Null,Index_type,Comment,Index_comment,Visible

member_profile,0,PRIMARY,1,member_id,A,9936492,\N,\N,,BTREE,,,YES
member_profile,0,hash_id,1,hash_id,A,9936492,\N,\N,YES,BTREE,,,YES
member_profile,1,geo_pt_index,1,geo_pt,A,9936492,32,\N,,SPATIAL,,,YES
Run Code Online (Sandbox Code Playgroud)

小智 5

“我有一个点列定义为 SRID 4326。我使用的是 MySQL 8.0.12。”

我也有类似的问题,将 SRID 更改为 0 可以显着提高性能。我不知道副作用是否令你难以忍受,但至少你应该尝试一下!如果您这样做,请不要忘记纬度和经度的其他顺序;)

KR皮特


Wil*_*uck 0

考虑 my.cnf [mysqld] 部分的建议

max_connect_errors=10  # from 100, why give a hacker/cracker so many chances?
thread_cache_size=30  # from 9 since MySQL needs 8 to get started
innodb_io_capacity_max=60000  # from 2000  use that NVME for performance
innodb_io_capacity=30000  # from 200 why stick with a low limit with NVME
key_buffer_size=16M  # from 1G conserve RAM for more useful purpose
innodb_buffer_pool_dump_pct=90  # from 25 to reduce WARM up time
innodb_change_buffer_max_size=15  # from 25% for your low chg,del,ins need
innodb_lru_scan_depth=128  # from 1025 to conserve CPU every SECOND
innodb_read_io_threads=64  # from 4 see dba.stackexchange Question 5666
innodb_write_io_threads=64  # from 4 see 9/12/11 RolondaMySQLDBA info
Run Code Online (Sandbox Code Playgroud)

请查看个人资料、网络配置文件以获取联系信息,包括 Skype ID 并取得联系。