J'ai besoin d'aller chercher des enregistrements à partir d'une table InnoDb par distance (ne doit pas être exactement) et trier par distance. La table compte 10 millions d'enregistrements.
Mon meilleur temps est jusqu'à présent 8 sec (3 secondes sans commande par distance), ce qui ne le fait pas. Comment je pourrais améliorer cela?
J'ai une colonne de point définie comme srid 4326. J'utilise 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;
Un indice spatial a été créé:
CREATE SPATIAL INDEX geo_pt_index ON mp (geo_pt);
Explique me montre que mon index Geo_PT est utilisé.
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 = ""
Ce serveur n'est attribué que pour cette base de données, aucune charge sur elle (sauf lorsque j'exécute une requête). Il n'y a pas de goulot d'étranglement iops. Innodb_Buffer_pool_Size est dimensionné pour contenir l'ensemble de l'ensemble de données en mémoire.
L'instance de serveur dispose de 16 Go de mémoire, utilise Fast NVME SSD (il n'y a pas de goulot d'étranglement iops). Le serveur héberge uniquement cette base de données et dispose de la requête sans charge. 30% du disque est utilisé.
SHOW GLOBAL STATUS
Sortie: https://pastebin.com/emenl8yt
SHOW GLOBAL VARIABLES
Sortie: https://pastebin.com/yxzyn10e
Sortie Tuner MySQL: https://pastebin.com/nrwfqdmq
J'ai aujourd'hui mis à jour de 8.0.11 à 8.0.12, mais j'ai suivi principalement toutes les propositions connexes des recommandations de syntoniseur MySQL antérieures. La mise à jour MySQL a été effectuée concernant un bogue fixe avec la recherche spatiale avant la vitesse de la vitesse.
Afficher les avertissements (après exécution de requête):
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
EXPLIQUE:
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
Créer une table:
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
Afficher l'index de:
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
"J'ai une colonne de point définie comme srid 4326. J'utilise MySQL 8.0.12."
J'ai un problème similaire et modifier le Srid sur 0 améliore considérablement la performance. Je ne sais pas si l'effet secondaire est insupportable pour vous, mais au moins vous devriez essayer! N'oubliez pas l'autre ordre du Lat et Lon si vous faites cela;)
Kr Pete
Suggestions à prendre en compte pour votre section 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
veuillez examiner le profil, profil de réseau pour les informations de contact, y compris les identifiants Skype et contactez-vous.
Problèmes secondaires
Utilisez ASCII pour des valeurs hexagonales telles que les hachages.
Pack Hex Hays en binaire:
hash_id BINARY(16)
HEX(hash_id) -- when reading
hash_id = UNHEX(...) -- when writing
Débarasse-toi du AUTO_INCREMENT id
et simplement utiliser hash_id.
Il serait préférable de se débarrasser du hachage - si les données deviennent trop grandes pour s'adapter à la RAM, elle deviendra I/O-liée.
algorithme plus rapide
(Bien que la principale question concerne le SIG, la question secondaire consiste à accélérer "trouver le plus proche".)
Pour l'équivalent de LIMIT 50
, l'algorithme suivant touchera probablement moins de 200 rangées (et calculera de grandes distances de cercle pour seulement celles). (Mieux que 79,901?)
http: //mysql.rjweb.org/doc.php/latlng (code inclus)
Analyse des variables/statut
Il semble que vous n'ayez pas encore beaucoup couru encore. Par conséquent, il n'y a pas beaucoup à dire ici:
Observations:
Les problèmes les plus importants:
key_buffer_size = 50M
long_query_time = 2
Allumez le ralentisseur afin que vous puissiez identifier les requêtes lentes.
Détails et autres observations:
( (key_buffer_size - 1.2 * Key_blocks_used * 1024) / _ram ) = (1024M - 1.2 * 16 * 1024) / 16384M = 6.2%
- Pourcentage de RAM gaspillé dans Key_Buffer. - Diminuer Key_Buffer_Size.
( Key_blocks_used * 1024 / key_buffer_size ) = 16 * 1024 / 1024M = 0.00%
- Pourcentage de key_buffer utilisé. Marque haute de l'eau. - Basse key_buffer_size pour éviter une utilisation inutile de la mémoire.
( table_open_cache ) = 4,000
- Nombre de descripteurs de table à cache - plusieurs centaines sont généralement bonnes.
( Innodb_buffer_pool_pages_free / Innodb_buffer_pool_pages_total ) = 443,594 / 786432 = 56.4%
- PCT de buffer_pool actuellement pas utilisé - innodb_buffer_pool_size est plus gros que nécessaire?
( Innodb_os_log_written / (Uptime / 3600) / innodb_log_files_in_group / innodb_log_file_size ) = 87,040 / (20998 / 3600) / 2 / 512M = 1.4e-5
- Ratio - (voir minutes)
( Uptime / 60 * innodb_log_file_size / Innodb_os_log_written ) = 20,998 / 60 * 512M / 87040 = 2.16e+6
- minutes entre les rotations de journaux Innodb commençant par 5.6.8, cela peut être modifié de manière dynamique; Assurez-vous également de changer mon.cnf. - (La recommandation de 60 minutes entre les rotations est quelque peu arbitraire.) Ajustez Innodb_Log_File_Size. (Ne peut pas changer d'AWS.)
( innodb_print_all_deadlocks ) = innodb_print_all_deadlocks = OFF
- Si vous souhaitez connecter toutes les impasses. - Si vous êtes en proie à des blocages, tournez-le. Attention: Si vous avez beaucoup d'impasse, cela peut écrire beaucoup sur le disque.
( join_buffer_size / _ram ) = 262,144 / 16384M = 0.00%
- 0-N par fil. Peut accélérer les jointures (mieux pour corriger les requêtes/index) (tous les moteurs) utilisés pour la numérisation d'index, la numérisation de l'index de plage, la numérisation de la table complète, chaque jointure complète, etc. - si grande, diminuez Join_Buffer_Size pour éviter la pression de la mémoire. Suggérer moins de 1% de RAM. Si petit, augmentez à 0,01% de RAM pour améliorer certaines requêtes.
( query_prealloc_size / _ram ) = 8,192 / 16384M = 0.00%
- Pour analyser. PCT de RAM
( query_alloc_block_size / _ram ) = 8,192 / 16384M = 0.00%
- Pour analyser. PCT de 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%
- Cadre Naughty - Dépenser beaucoup d'efforts de redécouvert le schéma. - se plaindre au fournisseur tiers.
( (Com_insert + Com_update + Com_delete + Com_replace) / Com_commit ) = (0 + 20 + 0 + 0) / 0 = INF
- déclarations par commis (en supposant que tout InnoDB) - faible: pourrait aider à regrouper les requêtes dans les transactions; Haut: longueurs de transactions varient diverses choses.
( Select_scan / Com_select ) = 96 / 355 = 27.0%
-% de sélection de la table de la table complète. (Peut être dupe de routines stockées.) - Ajouter des index/optimiser les requêtes
( expire_logs_days ) = 0
- combien de temps purge automatiquement Binlog (après plusieurs jours) - trop grand (ou zéro) = consomme de l'espace disque; Trop petit = besoin de réagir rapidement au crash de réseau/machine. (Non pertinent si log_bin = Off)
( slave_pending_jobs_size_max / max_allowed_packet ) = 128M / 64M = 2
- Pour les filets esclaves parallèles - slave_pending_jobs_size_max ne doit pas être inférieur à max_allowed_packet
( slow_query_log ) = slow_query_log = OFF
- Qu'il s'agisse de connecter des requêtes lentes. (5.1.12)
( long_query_time ) = 10
- coupure (secondes) pour définir une requête "lente". - suggérer 2
( back_log / max_connections ) = 151 / 151 = 100.0%
( Threads_created / Connections ) = 4 / 214 = 1.9%
- Rapidité de la création de processus - Augmentez le thread_cache_size (non-Windows)
anormalement grand:
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
Cordes anormales:
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