簡體   English   中英

即使使用索引,Doctrine / MySQL 查詢也很慢

[英]Doctrine / MySQL Slow query even when using indexes

我稍微清理了這個問題,因為它變得非常大且無法閱讀。

在我的本地主機上運行。

如下圖所示,當從包含 15000 行的表 Job 中選擇時,查詢需要755.15 ms (where 條件返回 6650)

表 Company 包含 1000 行。 表 geo__name 包含大約 84300 行,並沒有給我任何問題,所以我相信問題出在數據庫結構或其他方面。

這兩個表的結構如下:

表工作是:

CREATE TABLE `job` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `company_id` int(11) NOT NULL,
  `activity_sector_id` int(11) DEFAULT NULL,
  `status` int(11) NOT NULL,
  `active` datetime NOT NULL,
  `contract_type_id` int(11) NOT NULL,
  `salary_type_id` int(11) NOT NULL,
  `workday_id` int(11) NOT NULL,
  `geoname_id` int(11) NOT NULL,
  `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `minimum_experience` int(11) DEFAULT NULL,
  `min_salary` decimal(7,2) DEFAULT NULL,
  `max_salary` decimal(7,2) DEFAULT NULL,
  `zip_code` int(11) DEFAULT NULL,
  `vacancies` int(11) DEFAULT NULL,
  `show_salary` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `created_at` (`created_at`,`active`,`status`) USING BTREE,
  CONSTRAINT `FK_FBD8E0F823F5422B` FOREIGN KEY (`geoname_id`) REFERENCES `geo__name` (`id`),
  CONSTRAINT `FK_FBD8E0F8398DEFD0` FOREIGN KEY (`activity_sector_id`) REFERENCES `activity_sector` (`id`),
  CONSTRAINT `FK_FBD8E0F85248165F` FOREIGN KEY (`salary_type_id`) REFERENCES `job_salary_type` (`id`),
  CONSTRAINT `FK_FBD8E0F8979B1AD6` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`),
  CONSTRAINT `FK_FBD8E0F8AB01D695` FOREIGN KEY (`workday_id`) REFERENCES `workday` (`id`),
  CONSTRAINT `FK_FBD8E0F8CD1DF15B` FOREIGN KEY (`contract_type_id`) REFERENCES `job_contract_type` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

餐桌公司是:

CREATE TABLE `company` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `logo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `website` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  `phone` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `cifnif` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `type` int(11) NOT NULL,
  `subscription_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQ_4FBF094FA76ED395` (`user_id`),
  KEY `IDX_4FBF094F9A1887DC` (`subscription_id`),
  KEY `name` (`name`(191)),
  CONSTRAINT `FK_4FBF094F9A1887DC` FOREIGN KEY (`subscription_id`) REFERENCES `subscription` (`id`),
  CONSTRAINT `FK_4FBF094FA76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

查詢如下:

SELECT 
  j0_.id AS id_0, 
  j0_.status AS status_1, 
  j0_.title AS title_2, 
  j0_.min_salary AS min_salary_3, 
  j0_.max_salary AS max_salary_4, 
  c1_.id AS id_5, 
  c1_.name AS name_6, 
  c1_.logo AS logo_7, 
  a2_.id AS id_8, 
  a2_.name AS name_9, 
  g3_.id AS id_10, 
  g3_.name AS name_11, 
  j4_.id AS id_12, 
  j4_.name AS name_13, 
  j5_.id AS id_14, 
  j5_.name AS name_15, 
  w6_.id AS id_16, 
  w6_.name AS name_17 
FROM 
  job j0_ 
  INNER JOIN company c1_ ON j0_.company_id = c1_.id 
  INNER JOIN activity_sector a2_ ON j0_.activity_sector_id = a2_.id 
  INNER JOIN geo__name g3_ ON j0_.geoname_id = g3_.id 
  INNER JOIN job_salary_type j4_ ON j0_.salary_type_id = j4_.id 
  INNER JOIN job_contract_type j5_ ON j0_.contract_type_id = j5_.id 
  INNER JOIN workday w6_ ON j0_.workday_id = w6_.id 
WHERE 
  j0_.active >= CURRENT_TIMESTAMP 
  AND j0_.status = 1 
ORDER BY 
  j0_.created_at DESC

執行上述查詢時,我有以下結果:

在 MYSQL 工作台中: 0.578 sec / 0.016 sec 755.15 ms 0.578 sec / 0.016 sec在 Symfony 分析器中: 755.15 ms

問題是:這個查詢的持續時間是否正確? 如果沒有,如何提高查詢速度? 似乎太多了。

在此處輸入圖片說明

Symfony 調試工具欄(如果有幫助):

在此處輸入圖片說明

如下圖所示,我只獲取了我真正需要的數據:

在此處輸入圖片說明

解釋查詢:

在此處輸入圖片說明

時間線:

在此處輸入圖片說明

MySQL 服務器無法處理施加在其上的負載。 這可能是由於資源爭用,或者因為它沒有進行適當的調整,也可能是您的硬盤驅動器的問題。

Symfony 運行緩慢的原因有很多。

1.服務器故障

首先,可能是服務器故障。 服務器性能可能會影響您的查詢時間。

2. 數據大小和延遲渲染

然后是數據大小。 如下圖所示,對我的一個項目的查詢有 50Mb 的數據大小(目前大約 20k 行)。
在 HTML 中解析 50Mb 可能需要一些時間,主要是因為循環。
盡管如此,還是有一些解決方案,比如延遲渲染。

在此處輸入圖片說明

延遲渲染非常簡單,而不是在你的樹枝中解析數據,
將所有數據發送到 javascript 變量,並在加載 DOM 后使用 javascript 解析/呈現數據。

3.查詢優化

正如我在評論中所寫,您可以查看以下問題,我解釋了為什么自定義查詢很重要。
Doctrine 關系是否會影響應用程序性能?

在這個問題中,你會讀到那個順序問題……它實際上是最重要的事情。

雖然數據庫中的靜態數據通常以正確的順序插入,
動態數據(用戶在網站生命周期中提供的數據)很少出現這種情況

這就是為什么在查詢中使用ORDER BY通常會加快頁面呈現的速度,
因為學說不會自己做額外的查詢。

例如,我的站點之一在索引上顯示了大約 700 個條目。
首先,這是使用findAll()時的查詢計數:

在此處輸入圖片說明

它在 144 毫秒內顯示 254 個查詢(253 個重復),加上 39 個渲染時間。
接下來,使用findBy()的第二個參數ORDER BY ,我得到以下結果:

在此處輸入圖片說明

您可以在此處查看完整查詢(sreenshot 很大)
更好的是,1 次查詢僅在 8 毫秒內進行,並且渲染時間大致相同。
但是,在這里,我不使用關聯中的任何字段。
從我開始做的那一刻起,doctor qui 會做一些額外的查詢,查詢次數和時間會暴漲。
最后,它會變回findAll()東西

最后,這是自定義查詢:

在此處輸入圖片說明

在這個自定義查詢中,查詢時間從 8 毫秒變為 38 毫秒。
但是,與之前的查詢不同,我在結果中獲得了更多數據,
這將阻止學說進行額外的查詢。
同樣, ORDER BY()在這個查詢中很重要。 沒有它,我會猛增到 84 個查詢。

4. 部分

當您進行自定義查詢時,您可以加載部分對象而不是完整數據。
正如您在問題中所說, description字段似乎減慢了您的加載速度,
使用partials,可以避免從表中加載某些字段,這將加快查詢速度。

首先,這不是您的常規語法,而是您將如何創建查詢構建器:

$em=$this->getEntityManager();
$qb=$em->createQueryBuilder();

以防萬一,我更願意將$em作為一個單獨的變量(例如,如果我想獲取某個類存儲庫)。

然后你可以開始你的部分select 小心,第一次select不能包含任何關聯字段:

$qb->select("partial job.{id, status, title, minimum_experience, min_salary, max_salary, zip_code, vacancies")
   ->from(Job::class, "job");

然后你可以添加你的關聯:

$qb->addSelect("company")
   ->join("job.company", "company");

或者甚至添加部分關聯,以防您不需要關聯的所有數據:

$qb->addSelect("partial activitySector.{id}")
   ->join("job.activitySector", "activitySector");

$qb->addSelect("partial job.{id, company_id, activity_sector_id, status, active, contract_type_id, salary_type_id, workday_id, geoname_id, title, minimum_experience, min_salary, max_salary, zip_code, vacancies, show_salary");

5. 緩存

您還可以使用各種緩存,例如用於 PHP 的 Zend OPCache,您會在以下問題中找到一些建議: Why Symfony3 so慢?

還有 SQL 緩存 Varnish。


這是關於我可以分享的所有內容以減少您的加載時間。

希望它會被證明是有用的,並且您將能夠解決您的問題。

首先,我將通過添加 MySQL 關鍵字“STRAIGHT_JOIN”來開始你的表現,它告訴 MySQL 按照我提供的順序查詢數據,不要試圖為我考慮關系。 但是,在您的數據集如此小且已經 1/2 秒的情況下,不知道這是否會有幫助,但在較大的數據集上,我知道它可以顯着提高性能。

接下來,您似乎正在獲取基於 PK/FK 關系結果的查找描述。 沒有看到這些表上的索引,我建議覆蓋包含鍵和描述的索引,以便連接可以從它用於 JOIN 的索引頁中獲取數據,而不是使用索引頁,找到要獲取的實際數據頁描述並繼續。

最后,如果索引的索引為 ( status , active , created_at ),則索引為 ( created_at , active , status ) 的作業表可能會表現得更好。

使用您現有的索引,這樣想,每天的數據都放在一個盒子里。 在按活動時間戳排序的每一天框中(即使按活動日期簡化),然后是狀態。 因此,對於 CREATED 的每一天,您都會打開一個盒子。 查看輔助框,每個“活動”時間戳對應一個框(例如:按天)。 在每個活動時間戳(天)中,只有現在才能看到“Status = 1”是否記錄。 因此,打開每個活動時間戳日,評估 Status = 1,然后關閉每個創建的日期框並轉到下一個創建的日期框並重復。 所以看看每天打開每個盒子的勞動強度,當天內每個活動的盒子。

現在,在以狀態開頭的建議索引下。 您現在擁有的盒子數量非常有限,每個狀態對應一個盒子。 只打開 1 個狀態框 = 1 這些是您唯一要考慮的...所有其他您不關心的。 在里面,你有基於 ACTIVE Timestamp 的實際記錄,並且是子排序的。 從那里,您可以直接跳轉到當前時間戳的那些。 從第一條記錄和框中的其余記錄開始,您現在擁有所有符合條件的記錄。 完畢。 由於這些記錄(索引)也將 Created_at 作為索引的一部分,因此可以使用降序對其進行優化。

為了確保其他查找表的“覆蓋索引”(如果它們尚不存在),我建議如下。

table              index
company            ( id, name, logo )
activity_sector    (id, name )
geo__name          ( id, name )
job_salary_type    ( id, name )
job_contract_type  ( id, name )
workday            ( id, name )

還有 MySQL 關鍵字...

SELECT STRAIGHT_JOIN   (rest of query...)

這么多鍵,盡量減少鍵的數量。

暫無
暫無

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

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