簡體   English   中英

使用 MySQL 連接查詢很慢,有沒有辦法通過子查詢來改善慢速性能?

[英]Query with MySQL join is slow, is there a way to improve slow performance with subquery?

我正在使用 InnoDB 的全文搜索從 2 個表格、照片和相冊中檢索結果,如下所示:

SELECT p.media_extension, 
  p.media_vishash, 
  ANY_VALUE(a.title_url) as album_title_url, 
  ANY_VALUE(p.title_url) as photo_title_url, 
  p.media_time_created, 
  p.comments, 
  p.title, 
  p.text, 
  MATCH (p.title,p.text,p.tag_list,p.comment_list,p.media_filename) 
  AGAINST ('search query' IN BOOLEAN MODE) as score 
FROM albums a, 
  album_photo_map apm, 
  media p 
WHERE p.media_vishash = apm.media_vishash AND 
    apm.album_id = a.album_id AND 
    p.level <= 5 AND 
    a.level <= 5 AND 
    MATCH (p.title,p.text,p.tag_list,p.comment_list,p.media_filename) 
    AGAINST ('search query' IN BOOLEAN MODE) 
GROUP BY p.media_vishash 
ORDER BY p.datetime_created DESC, p.media_vishash DESC

在 0.80 秒內執行。

+----+-------------+-------+------------+----------+-----------------------------------------+----------+---------+--------------------+------+----------+----------------------------------------------+
| id | select_type | table | partitions | type     | possible_keys                           | key      | key_len | ref                | rows | filtered | Extra                                        |
+----+-------------+-------+------------+----------+-----------------------------------------+----------+---------+--------------------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | a     | NULL       | ALL      | PRIMARY,album_id_type,album_level,level | NULL     | NULL    | NULL               | 9147 |    49.99 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | p     | NULL       | fulltext | media_vishash,search                    | search   | 0       | const              |    1 |    33.33 | Using where                                  |
|  1 | SIMPLE      | apm   | NULL       | ref      | PRIMARY,album_id,media_vishash          | album_id | 130     | lifebox.a.album_id |   26 |    10.00 | Using where; Using index                     |
+----+-------------+-------+------------+----------+-----------------------------------------+----------+---------+--------------------+------+----------+----------------------------------------------+

與專輯表的連接確實減慢了查詢速度。 我嘗試在沒有加入的情況下重寫它,如下所示,性能非常好。

SELECT p.media_extension, 
    p.media_vishash, 
    ANY_VALUE(p.title_url) as photo_title_url, 
    p.media_time_created, 
    p.comments, 
    p.title, 
    p.text,
   MATCH   (p.title,p.text,p.tag_list,p.comment_list,p.media_filename) AGAINST ('search query' IN BOOLEAN MODE) as score 
FROM media p
WHERE MATCH (p.title,p.text,p.tag_list,p.comment_list,p.media_filename) 
    AGAINST ('search query' IN BOOLEAN MODE) 

在 0.01 秒內執行。

問題是我需要從專輯表中檢索 title_url,而第二個查詢中缺少此內容。

有沒有一種方法可以像在第一個查詢中一樣檢索 Albums.title_url,同時保持第二個查詢的出色性能,而無需將其拆分為多個查詢? 也許通過子選擇從專輯表中獲取 title_url?

創建下表,MySQL 8.0.27:

 CREATE TABLE `media` (
  `media_id` char(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `media_sha512` char(128) NOT NULL,
  `media_sha256` char(64) NOT NULL,
  `media_md5` char(32) NOT NULL,
  `media_filepath` varchar(250) NOT NULL,
  `media_vishash` char(128) NOT NULL,
  `media_filename` varchar(250) NOT NULL,
  `media_extension` varchar(10) NOT NULL,
  `media_path` varchar(250) NOT NULL,
  `media_folder` varchar(250) NOT NULL,
  `media_time_lastmod` bigint NOT NULL,
  `media_time_created` bigint DEFAULT NULL,
  `media_filesize` bigint DEFAULT NULL,
  `media_width` int DEFAULT NULL,
  `media_height` int DEFAULT NULL,
  `media_megapixels` float DEFAULT NULL,
  `media_mimetype` varchar(50) DEFAULT NULL,
  `media_type` varchar(25) DEFAULT NULL,
  `title` varchar(250) NOT NULL,
  `title_url` varchar(250) NOT NULL,
  `title_url_previous` varchar(250) DEFAULT NULL,
  `text` text,
  `year` int DEFAULT NULL,
  `day` int DEFAULT NULL,
  `month` int DEFAULT NULL,
  `sidecarimg_filepath` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `sidecarimg_md5` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `sidecarvideo_filepath` varchar(250) DEFAULT NULL,
  `sidecarvideo_md5` char(32) DEFAULT NULL,
  `sidecarxmp_filepath` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `sidecarxmp_md5` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `modified_filepath` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `modified_md5` char(32) DEFAULT NULL,
  `modified_time_lastmod` int DEFAULT NULL,
  `modified_time_created` int DEFAULT NULL,
  `datetime_created` datetime DEFAULT NULL,
  `datetime_modified` datetime DEFAULT NULL,
  `EXIF_Make` varchar(50) DEFAULT NULL,
  `EXIF_LensModel` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `EXIF_LensID` varchar(100) DEFAULT NULL,
  `EXIF_LensMount` varchar(100) DEFAULT NULL,
  `EXIF_LensFormat` varchar(100) DEFAULT NULL,
  `EXIF_Software` varchar(100) DEFAULT NULL,
  `EXIF_ByLine` varchar(100) DEFAULT NULL,
  `EXIF_Copyright` varchar(100) DEFAULT NULL,
  `EXIF_DateTimeOriginal` datetime DEFAULT NULL,
  `EXIF_ExposureTime` varchar(100) DEFAULT NULL,
  `EXIF_ShutterSpeed` varchar(100) DEFAULT NULL,
  `EXIF_FNumber` varchar(100) DEFAULT NULL,
  `EXIF_FocalLength` varchar(100) DEFAULT NULL,
  `EXIF_FocalLength35mm` varchar(100) DEFAULT NULL,
  `EXIF_ISO` varchar(100) DEFAULT NULL,
  `EXIF_DisplayProfile` varchar(100) DEFAULT NULL,
  `EXIF_Keywords` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `EXIF_Model` varchar(75) DEFAULT NULL,
  `EXIF_Orientation` varchar(100) DEFAULT NULL,
  `EXIF_ExposureProgram` varchar(100) DEFAULT NULL,
  `EXIF_BitsPerSample` varchar(100) DEFAULT NULL,
  `EXIF_FlashFire` varchar(100) DEFAULT NULL,
  `EXIF_ExposureCompensation` varchar(100) DEFAULT NULL,
  `EXIF_HDR` varchar(100) DEFAULT NULL,
  `EXIF_ColorTemperature` varchar(25) DEFAULT NULL,
  `EXIF_ColorSpace` varchar(20) DEFAULT NULL,
  `EXIF_ColorProfileDescription` varchar(50) DEFAULT NULL,
  `EXIF_CustomRendered` varchar(100) DEFAULT NULL,
  `EXIF_HistorySoftwareAgent` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `EXIF_WhiteBalance` varchar(100) DEFAULT NULL,
  `EXIF_Quality` varchar(20) DEFAULT NULL,
  `EXIF_SelfTimer` varchar(100) DEFAULT NULL,
  `EXIF_Contrast` varchar(100) DEFAULT NULL,
  `EXIF_Saturation` varchar(100) DEFAULT NULL,
  `EXIF_Sharpness` varchar(100) DEFAULT NULL,
  `EXIF_SerialNumber` varchar(100) DEFAULT NULL,
  `EXIF_ShutterCount` varchar(100) DEFAULT NULL,
  `EXIF_Rating` varchar(100) DEFAULT NULL,
  `EXIF_RatingPercent` varchar(100) DEFAULT NULL,
  `EXIF_Good` tinyint DEFAULT NULL,
  `EXIF_Subject` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `EXIF_Title` varchar(250) DEFAULT NULL,
  `EXIF_Description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
  `EXIF_Location` varchar(250) DEFAULT NULL,
  `GPS_Altitude_Ref` varchar(50) DEFAULT NULL,
  `GPS_Altitude` varchar(50) DEFAULT NULL,
  `GPS_Latitude` varchar(50) DEFAULT NULL,
  `GPS_Longitude` varchar(50) DEFAULT NULL,
  `GPS_Latitude_Dec` double DEFAULT NULL,
  `GPS_Longitude_Dec` double DEFAULT NULL,
  `GPS_datetime` datetime DEFAULT NULL,
  `is_video` tinyint(1) NOT NULL DEFAULT '0',
  `video_duration` varchar(50) DEFAULT NULL,
  `video_framerate` varchar(50) DEFAULT NULL,
  `video_bitrate` varchar(50) DEFAULT NULL,
  `video_resolution` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `video_codec` varchar(50) DEFAULT NULL,
  `video_pixfmt` varchar(50) DEFAULT NULL,
  `video_profile` varchar(50) DEFAULT NULL,
  `video_colorprimary` varchar(50) DEFAULT NULL,
  `video_rotation` varchar(5) DEFAULT NULL,
  `audio_codec` varchar(50) DEFAULT NULL,
  `audio_bitrate` varchar(50) DEFAULT NULL,
  `audio_channels` varchar(50) DEFAULT NULL,
  `audio_bits_per_sample` varchar(50) DEFAULT NULL,
  `audio_sample_rate` varchar(50) DEFAULT NULL,
  `comment_list` text,
  `tag_list` text,
  `timestamp_last_comment` int NOT NULL DEFAULT '0',
  `timestamp_last_view` int DEFAULT NULL,
  `views` int NOT NULL DEFAULT '0',
  `comments` int NOT NULL DEFAULT '0',
  `tags` int NOT NULL DEFAULT '0',
  `thumbs` json DEFAULT NULL,
  `thumbs_datetime_created` datetime DEFAULT NULL,
  `level` tinyint NOT NULL DEFAULT '2',
  PRIMARY KEY (`media_id`),
  UNIQUE KEY `media_vishash` (`media_vishash`) USING BTREE,
  UNIQUE KEY `media_sha512` (`media_sha512`) USING BTREE,
  KEY `index_rating` (`EXIF_Rating`),
  FULLTEXT KEY `search` (`title`,`text`,`tag_list`,`comment_list`,`media_filename`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

CREATE TABLE `album_photo_map` (
  `album_id` varchar(32) NOT NULL DEFAULT '',
  `media_vishash` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`album_id`,`media_vishash`) USING BTREE,
  KEY `album_id` (`album_id`),
  KEY `media_vishash` (`media_vishash`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

CREATE TABLE `albums` (
  `album_id` varchar(32) NOT NULL DEFAULT '',
  `title` varchar(100) NOT NULL DEFAULT '',
  `title_url` varchar(100) NOT NULL DEFAULT '',
  `location` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '',
  `text` text,
  `timestamp_created` int NOT NULL DEFAULT '0',
  `timestamp_modified` int DEFAULT '0',
  `timestamp_oldest` int DEFAULT NULL,
  `timestamp_newest` int DEFAULT NULL,
  `num_photos` int NOT NULL DEFAULT '0',
  `num_videos` int NOT NULL DEFAULT '0',
  `num_items` int NOT NULL DEFAULT '0',
  `type` tinyint(1) NOT NULL DEFAULT '1',
  `highlight_media_vishash` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `timestamp_last_view` int NOT NULL DEFAULT '0',
  `last_viewer` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '',
  `views` int NOT NULL DEFAULT '0',
  `min_level` tinyint(1) NOT NULL DEFAULT '2',
  `level` tinyint(1) NOT NULL DEFAULT '2',
  PRIMARY KEY (`album_id`),
  UNIQUE KEY `title` (`title`),
  UNIQUE KEY `title_url` (`title_url`),
  UNIQUE KEY `album_id_type` (`album_id`,`type`),
  KEY `highlight_photo_id` (`highlight_media_vishash`),
  KEY `lowest_level` (`min_level`),
  KEY `timestamp_created` (`timestamp_created`),
  KEY `type` (`type`),
  KEY `level` (`level`),
  FULLTEXT KEY `title_2` (`title`,`text`,`location`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

我使用 EXPLAIN 測試了您的第一個查詢,發現優化器想要重新排序表,因此它以album_photo_map ,然后連接到其他每個表的主鍵。 這意味着它根本沒有使用您的全文索引。

因此,我使用STRAIGHT_JOIN語法測試了查詢,以覆蓋優化器對表重新排序的趨勢。 這也導致我使用現代的JOIN... ON語法,無論如何你都應該這樣做。

EXPLAIN SELECT p.media_extension,
  p.media_vishash, 
  ANY_VALUE(a.title_url) as album_title_url, 
  ANY_VALUE(p.title_url) as photo_title_url,
  p.media_time_created,
  p.comments,
  p.title,
  p.text,
  MATCH (p.title,p.text,p.tag_list,p.comment_list,p.media_filename)
  AGAINST ('search query' IN BOOLEAN MODE) as score
FROM media p
STRAIGHT_JOIN album_photo_map apm ON p.media_vishash = apm.media_vishash
STRAIGHT_JOIN albums a ON apm.album_id = a.album_id
WHERE
    p.level <= 5 AND
    a.level <= 5 AND
    MATCH (p.title,p.text,p.tag_list,p.comment_list,p.media_filename)
    AGAINST ('search query' IN BOOLEAN MODE)
GROUP BY p.media_vishash
ORDER BY p.datetime_created DESC, p.media_vishash DESC

這導致更有利的優化。 它使用全文索引,然后使用索引連接到其他兩個表。

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: p
   partitions: NULL
         type: fulltext
possible_keys: media_vishash,search
          key: search
      key_len: 0
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: apm
   partitions: NULL
         type: index
possible_keys: PRIMARY,album_id,media_vishash
          key: album_id
      key_len: 130
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where; Using index
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY,album_id_type,level
          key: PRIMARY
      key_len: 130
          ref: test.apm.album_id
         rows: 1
     filtered: 100.00
        Extra: Using where

我們可以再改進一點。 由於首先讀取media表,因此需要通過album_photo_map在專輯照片media_vishash中進行查找。 但這不是后一個表主鍵的最左邊一列。 如果您重新排序主鍵,它將能夠進行更有效的連接。

alter table album_photo_map drop primary key, 
  add primary key(media_vishash,album_id);
PRIMARY KEY (`album_id`),
UNIQUE KEY `album_id_type` (`album_id`,`type`),

在 MySQL 中,PK 必須是唯一的並與數據聚集在一起。 因此,該唯一鍵是多余且不必要的。

擁有多個UNIQUE鍵是不尋常的。

請為您的兩個查詢提供EXPLAIN SELECT...

計時查詢時,請運行兩次。 第二次運行可能會快很多。 可能是由於第一次運行做了很多 I/O,而第二次發現緩存了所有必要的數據。

這里有一些嘗試:

SELECT ...
    FROM (your second query) AS a
    JOIN  album_photo_map AS apm  ON apm.album_id = a.album_id
    JOIN  media  AS p ON p.media_vishash = apm.media_vishash
    WHERE ... the rest of the conditions
    GROUP BY  p.datetime_created,      p.media_vishash
    ORDER BY  p.datetime_created DESC, p.media_vishash DESC

我選擇使GROUP BYORDER BY BY 匹配,以消除額外的排序。

我認為,這種重寫將迫使它首先使用FULLTEXT索引,這似乎是性能的關鍵。 (注意EXPLAIN是如何顯示第a被使用的。)

其他注意事項:

  • 將日期分成 3 列(年、月、日)通常是個壞主意。 如果您需要對日期進行范圍測試,您將看到這一點。
  • 對密鑰使用 md5s(或 UUID 或其他哈希)可能效率低下。 (此評論與有問題的 Select 無關。)
  • 在 apm 中,這是多余的:KEY album_id ( album_id )

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM