慢查询Groupby,加入MYSQL laravel

我有4个表格:地点,品牌,类别,位置。

关系是“属于品牌的地方”和“具有类别和位置的很多地方”。

我想获取搜索结果中具有特定类别和位置的地点,但每个品牌仅显示1个地点。

表格信息 places表包含约10万多行

place_category数据透视表包含650k +行,其中place_category.place_idplaces.brand_id列已被索引

place_locations数据透视表包含约550k以上的行,其中place_location.place_idplace_location.location_id列已索引

我到目前为止得到的查询

Place::join('place_location',function ($join) use ($city) {
    $join->on('place_location.place_id','=','places.id')
         ->where('place_location.location_id',$city->id);
})
->join('place_category',function ($join) {
    $join->on('place_category.place_id','places.id')
         ->where('place_category.category_id',$category->id);
})
->groupBy('places.brand_id')
->take(5)
->get();

groupBy导致查询时间变慢,约为2秒。

解释结果看起来像这样

id | select_type | table          | possible_key            | key            | key_len | ref                | rows | Extra

1  | SIMPLE      | places         | PRIMARY                 | brand_id       | 4       | NULL               | 50   | Using where

1  | SIMPLE      | place_location | place_id,place_location | place_location | 4       | const,db.places.id | 1    | Using index

1  | SIMPLE      | place_category | place_category          | place_category | 4       | db.places.id,const | 1    | Using where; Using index

原始MySQL查询如下所示

select 
    `places`.`id`,`places`.`name`,`places`.`display`,`places`.`status_a`,`places`.`status_b`,`places`.`brand_id`,`places`.`address` 
from `places` 
inner join `place_location` 
    on `place_location`.`place_id` = `places`.`id` 
    and `place_location`.`location_id` = 4047 
inner join `place_category` 
    on `place_category`.`place_id` = `places`.`id` 
    and `place_category`.`category_id` = 102 
where 
    `places`.`status_a` != 1 
    and `status_b` = 2 
    and `display` >= 5 
group by `places`.`brand_id` 
limit 4

显示创建表如下所示

CREATE TABLE `places` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,`user_id` int(11) unsigned DEFAULT NULL,`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,`desc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,`city_id` int(11) unsigned NOT NULL DEFAULT '102',`state_id` int(11) unsigned NOT NULL DEFAULT '34',`location_id` int(11) unsigned NOT NULL DEFAULT '15',`landmark_id` int(10) unsigned NOT NULL DEFAULT '1',`postcode` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,`country_id` int(4) unsigned NOT NULL,`lat` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,`long` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,`phone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,`sec_phone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,`third_phone` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,`fourth_phone` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,`brand_id` int(10) NOT NULL DEFAULT '1',`display` int(11) NOT NULL DEFAULT '0',`view` int(10) unsigned NOT NULL DEFAULT '0',`status_b` tinyint(3) unsigned NOT NULL DEFAULT '2',`status_a` tinyint(4) NOT NULL DEFAULT '2',`company_name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,`lock_status_id` tinyint(3) unsigned DEFAULT '1',`created_at` timestamp NULL DEFAULT NULL,`updated_at` timestamp NULL DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `slug` (`slug`),KEY `city_id` (`city_id`),KEY `location_id` (`location_id`),KEY `user_id` (`user_id`),KEY `landmark_id` (`landmark_id`),KEY `name` (`name`),KEY `brand_id` (`brand_id`),KEY `groupby_brandid` (`status_b`,`display`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=116070 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci


CREATE TABLE `place_location` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,`location_id` int(10) NOT NULL,`place_id` int(10) NOT NULL,KEY `place_location` (`place_id`,`location_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=564259 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `place_category` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,`category_id` int(11) unsigned NOT NULL,`place_id` int(11) unsigned NOT NULL,`branch_id` int(11) unsigned NOT NULL,KEY `place_id` (`place_id`),KEY `place_category` (`category_id`,`place_id`)
) ENGINE=InnoDB AUTO_INCREMENT=905384 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

任何想法如何改善查询?错误的索引?或查询错误?

ooxiaoping 回答:慢查询Groupby,加入MYSQL laravel

首先尝试在查询下面执行

explain 
select  `products`.`id`,`products`.`name`,`products`.`display`,`products`.`status_a`,`products`.`status_b`,`products`.`brand_id`,`products`.`address`
    from  `products`
    inner join  `product_location`  ON `product_location`.`product_id` = `products`.`id`
      and  `product_location`.`location_id` = 4047
    inner join  `product_category`  ON `product_category`.`product_id` = `products`.`id`
      and  `product_category`.`category_id` = 102
    where  `products`.`status_a` != 1
      and  `status_b` = 2
      and  `display` >= 5
    group by  `products`.`brand_id`
    limit  4 

EXPLAIN SELECT 语句显示MySQL查询优化器将如何执行查询

  

索引可以提高性能,索引也会产生负面影响   如果有太多的性能。这是因为更多   表具有的索引,MySQL必须做更多的工作来保持它们更新。   诀窍是在足够多的索引之间找到适当的平衡   改善性能,但影响不大   性能。

再次尝试添加索引并执行相同的说明语句

希望这会有所帮助...

,
`products`.`status_a` != 1 
and `status_b` = 2 
and `display` >= 5 

邀请这个

INDEX(status_b,display)

这是一个0/1标志吗?

`products`.`status_a` != 1 

如果是,则更改为

`products`.`status_a` = 0

那你可以做得更好

INDEX(status_b,status_a,display)

product_locationproduct_category听起来像多对多映射表。他们需要综合索引,如下所述:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table确保数据类型匹配。

,

为什么要使用联接查询,您可以为此使用laravel雄辩的关系

Place::whereHas('placeLocation',function ($query) use ($city) {
    $query->where('location_id','=',$city->id);
})
->whereHas('placeCategory',function ($query) {
    $query->where('category_id',$category->id);
})
->groupBy('brand_id')
->take(5)
->get();

placeLocation(HasMany)和placeCategory(BelongsToMany)这两个关系都是必须在Place模型中编写的。

,

通常,数据库将无法在查询执行期间合并多个索引。这意味着,为表中的所有内容创建单个列索引将无济于事。

您似乎经常使用单列索引。尝试以满足您查询的方式组合它们。

多索引如何工作?

创建数据库索引时,我总是尝试根据烹饪书中的索引来解释它们:它们通常是嵌套索引。首先,将他们分类为进餐类型,例如汤,菜,沙拉等。在这些类别中,它们按字母顺序排序。

此索引类似于SQL中的索引:

KEY `recipe` (`meal_type_id`,`name`)

作为一个人,您现在可以通过首先进入菜肴(第一个索引)然后找到字母“ C”(第二个索引)来找到起司蛋糕。

因此在这种情况下,多列索引非常有帮助。

优化餐桌位置:

您在status_a子句中使用status_bdisplayWHERE,在brand_id中也使用GROUP BY。对于多列索引,请尝试找到这些列的有意义的组合。

订单在这里很重要!

例如,如果在status_b = 2上只有10%的数据匹配,那么您应该将该字段用作索引的第一列,因为这样会消除90%的行。然后,第二个索引列要做的事情要少得多。考虑上面的芝士蛋糕示例:我们已经知道芝士蛋糕是一道菜,因此我们直接查看这些菜,就可以消除其余90%的食谱。

这称为“ cardinality”。

优化表格的place_location和place_category:

别忘了还要查看联接的表,并确保它们也被正确索引。

就像前面提到的一样,尝试在这些表上也找到有用的索引。查看查询的要求,并尝试通过有用的索引来满足要求。

一个表应该有几个索引?

要回答这个问题,您只需了解必须在每个INSERT或UPDATE上更新索引。因此,如果它是一个写密集型表,则应使用尽可能少的索引。

如果该表是读取密集型的,但不会被频繁写入,则可以再增加一些索引。请记住,他们将需要记忆。如果您的表有数百万行,那么如果您使用许多索引,则索引可能会变得很大。

本文链接:https://www.f2er.com/3139704.html

大家都在问