InnoDbテーブルから距離で(正確ではない)レコードをフェッチし、距離でソートする必要があります。テーブルには1000万のレコードがあります。
私の最高の時間はこれまでのところ8秒(距離による順序なしで3秒)で、これは使用できません。どうすればこれを改善できますか?
SRID 4326として定義されたポイントカラムがあります。MySQL8.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;
空間インデックスが作成されました:
CREATE SPATIAL INDEX geo_pt_index ON mp (geo_pt);
EXPLAINは、geo_ptインデックスが使用されていることを示しています。
my.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 = ""
このサーバーはこのデータベースにのみ割り当てられており、負荷はありません(クエリを実行する場合を除く)。 IOPSのボトルネックはありません。 innodb_buffer_pool_sizeは、データセット全体をメモリに保持できるサイズになっています。
サーバーインスタンスには16 GBのメモリがあり、高速NVMe SSDを使用します(IOPSボトルネックはありません)。サーバーは、この1つのデータベースのみをホストし、クエリ以外の負荷はありません。ディスクの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の更新は、速度が同じになる前に、空間検索で修正されたバグに関して行われました。
表示警告(クエリ実行後):
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
説明:
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
テーブルの作成:
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
インデックスを表示:
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
「ポイントカラムがSRID 4326として定義されています。MySQL8.0.12を使用しています。」
同様の問題があり、SRIDを0に変更するとパフォーマンスが大幅に向上します。副作用が耐えられないかどうかはわかりませんが、少なくとも試してみてください。あなたがそうするなら、緯度と経度の他の順序を忘れないでください;)
KRピート
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
skype IDを含む連絡先情報については、プロファイル、ネットワークプロファイルを確認し、連絡を取ってください。
MySQL GISは常に遅いです。しかし、1,000万行のテーブルでORDER BY DISTANCEなしの3秒は遅いと言われていますか?検討する
ST_Distance() < upperlimit
を実行するのではなく、ST_Buffer( polygon, upperlimit )
を実行し、それを_ST_Contains
_の呼び出しで使用することを検討してください。ST_DWithin(geom,geom,upperlimit)
を使用することを検討してください。 PostGISは優れた索引付けを備えています。実際には、インデックス全体でこのすべてを実行できます KNNをサポートしているためサイドの問題
ハッシュなどの16進値にはasciiを使用します。
16進数のハッシュをバイナリにパックします。
hash_id BINARY(16)
HEX(hash_id) -- when reading
hash_id = UNHEX(...) -- when writing
AUTO_INCREMENT id
を削除して、hash_idを使用してください。
ハッシュを取り除く方が良いでしょう。データが大きくなりすぎてRAMに収まらない場合は、I/Oバウンドになります。
高速アルゴリズム
(最初の質問はGISに関するものですが、2番目の質問は「最も近い場所を見つける」ことの高速化に関するものです。)
LIMIT 50
と同等の場合、次のアルゴリズムはおそらく200行未満を処理します(そしてそれらのみの大圏距離を計算します)。 (79,901より良いですか?)
http://mysql.rjweb.org/doc.php/latlng (コードを含む)
変数/ステータスの分析
あなたはまだあまり走っていないようです。したがって、ここで言うことはあまりありません。
所見:
より重要な問題:
key_buffer_size = 50M
long_query_time = 2
Slowlogをオンにして、遅いクエリを識別できるようにします。
詳細およびその他の観察:
( (key_buffer_size - 1.2 * Key_blocks_used * 1024) / _ram ) = (1024M - 1.2 * 16 * 1024) / 16384M = 6.2%
-RAMの割合がkey_bufferで無駄になりました。--key_buffer_sizeを減らしてください。
( Key_blocks_used * 1024 / key_buffer_size ) = 16 * 1024 / 1024M = 0.00%
-key_bufferの使用率。最高水位標。 -不要なメモリ使用を回避するためにkey_buffer_sizeを小さくします。
( table_open_cache ) = 4,000
-キャッシュするテーブル記述子の数-通常は数百が適切です。
( Innodb_buffer_pool_pages_free / Innodb_buffer_pool_pages_total ) = 443,594 / 786432 = 56.4%
-現在使用されていないbuffer_poolの部分-innodb_buffer_pool_sizeが必要以上に大きいですか?
( Innodb_os_log_written / (Uptime / 3600) / innodb_log_files_in_group / innodb_log_file_size ) = 87,040 / (20998 / 3600) / 2 / 512M = 1.4e-5
-比率-(分を参照)
( Uptime / 60 * innodb_log_file_size / Innodb_os_log_written ) = 20,998 / 60 * 512M / 87040 = 2.16e+6
-InnoDBログローテーション間の分5.6.8以降、これは動的に変更できます。 my.cnfも必ず変更してください。 -(ローテーション間の60分の推奨はやや恣意的です。)innodb_log_file_sizeを調整します。 (AWSでは変更できません。)
( innodb_print_all_deadlocks ) = innodb_print_all_deadlocks = OFF
-すべてのデッドロックをログに記録するかどうか。 -デッドロックに悩まされている場合は、これをオンにします。注意:デッドロックが多い場合、ディスクに大量に書き込まれる可能性があります。
( join_buffer_size / _ram ) = 262,144 / 16384M = 0.00%
-スレッドごとに0-N。 JOINを高速化する可能性があります(クエリ/インデックスを修正する方が良い)(すべてのエンジン)インデックススキャン、範囲インデックススキャン、全テーブルスキャン、各全JOINなどに使用されます。サイズが大きい場合は、join_buffer_sizeを減らしてメモリ負荷を回避します。 RAMの1%未満を推奨します。小さい場合は、RAMの0.01%に増やして、クエリを改善します。
( query_prealloc_size / _ram ) = 8,192 / 16384M = 0.00%
-解析用。 RAMの割合
( query_alloc_block_size / _ram ) = 8,192 / 16384M = 0.00%
-解析用。 RAMの割合
( net_buffer_length / max_allowed_packet ) = 16,384 / 64M = 0.02%
( (Com_show_create_table + Com_show_fields) / Questions ) = (7 + 7) / 698 = 2.0%
-いたずらなフレームワーク-スキーマの再発見に多くの労力を費やしています。 -サードパーティベンダーに文句を言う。
( (Com_insert + Com_update + Com_delete + Com_replace) / Com_commit ) = (0 + 20 + 0 + 0) / 0 = INF
-コミットごとのステートメント(すべてのInnoDBを想定)-低:トランザクション内でクエリをグループ化するのに役立つ場合があります。高:長いトランザクションはさまざまなことを負担します。
( Select_scan / Com_select ) = 96 / 355 = 27.0%
-全テーブルスキャンを実行する選択の%。 (ストアドルーチンにだまされる可能性があります。)-インデックスを追加する/クエリを最適化する
( expire_logs_days ) = 0
-binlogを自動的にパージするまでの時間(この日数が経過した後)-大きすぎる(またはゼロ)=ディスク領域を消費します。小さすぎる=ネットワーク/マシンのクラッシュに迅速に対応する必要がある(log_bin = OFFの場合は関係ありません)
( slave_pending_jobs_size_max / max_allowed_packet ) = 128M / 64M = 2
-並列スレーブスレッドの場合-slave_pending_jobs_size_maxはmax_allowed_packet未満であってはなりません
( slow_query_log ) = slow_query_log = OFF
-遅いクエリをログに記録するかどうか。 (5.1.12)
( long_query_time ) = 10
-「遅い」クエリを定義するためのカットオフ(秒)。 -提案2
( back_log / max_connections ) = 151 / 151 = 100.0%
( Threads_created / Connections ) = 4 / 214 = 1.9%
-プロセス作成の迅速性-thread_cache_sizeを増やします(Windows以外)
異常に大きい:
Com_create_db = 0.17 /HR
Com_drop_db = 0.34 /HR
Com_show_profiles = 1.9 /HR
Innodb_buffer_pool_pages_flushed / max(Questions, Queries) = 0.365
Innodb_buffer_pool_pages_free = 443,594
Select_range / Com_select = 33.2%
Ssl_session_cache_size = 128
innodb_purge_threads = 4
innodb_undo_tablespaces = 2
max_error_count = 1,024
max_length_for_sort_data = 4,096
optimizer_trace_max_mem_size = 1.05e+6
slave_pending_jobs_size_max = 128MB
異常な文字列:
Ssl_session_cache_mode = SERVER
default_authentication_plugin = caching_sha2_password
event_scheduler = ON
explicit_defaults_for_timestamp = ON
ft_boolean_syntax = + -><()~*:&
have_query_cache = NO
have_ssl = YES
have_symlink = DISABLED
innodb_buffer_pool_dump_at_shutdown = ON
innodb_buffer_pool_load_at_startup = ON
innodb_fast_shutdown = 1
innodb_undo_log_truncate = ON
log_syslog = ON
master_info_repository = TABLE
optimizer_trace = enabled=off,one_line=off
optimizer_trace_features = greedy_search=on, range_optimizer=on, dynamic_range=on, repeated_subselect=on
relay_log_info_repository = TABLE
slave_rows_search_algorithms = INDEX_SCAN,HASH_SCAN
ssl_ca = ca.pem
ssl_cert = server-cert.pem
ssl_key = server-key.pem