MYSQL//我是否正确执行了SQL优化?

cyvaqqii  于 2023-08-02  发布在  Mysql
关注(0)|答案(3)|浏览(98)
# index: `review_count`,  `store_id`, `status`, `name`, `address`
EXPLAIN ANALYZE  SELECT 
            store0_.name,
        store0_.status,
        store0_.name,
        store0_.address,
        category1_.name

    from
        store store0_ 
    inner join
        category category1_ 
            on store0_.category_id=category1_.category_id 
    left outer join
        store_keyword storekeywo2_ 
            on store0_.store_id=storekeywo2_.store_id 
    left outer join
        keyword keyword3_ 
            on storekeywo2_.keyword_id=keyword3_.keyword_id 
    where
     store0_.review_count  >= 0 AND ####
        store0_.status='APPROVED'
        AND  store0_.name LIKE '가게%'
        AND keyword3_.name = '분위기 좋은'
      
ORDER BY store0_.review_count
   LIMIT 100
   OFFSET 8000

字符串
如果您将条件“store0_.review_count >= 0 AND”,则性能将提高约10倍,从0.14ms提高到0.014ms。此时,实际时间是使用mysql(heidisql)的执行计划测量的。
(我不知道为什么在性能上会有这样的差异。)因为条件“store0_.review_count >= 0 AND”对所有行都是true。为什么性能提高了10倍?

jvidinwx

jvidinwx1#

这与性能优化无关,而是与查询的可读性/功能性有关:

  • 你把商店的关键字加入外部,以便显示没有关键字的商店吗?但你有AND keyword3_.name = '분위기 좋은'。外部联接行没有关键字。它为null,因此您会忽略所有外部链接的数据列。看起来您确实需要一个内部连接,因此请使用内部连接而不是外部连接。
  • 您的查询结果会显示商店及其类别。所以关键词只是个条件。通过将条件放在WHERE子句中而不是连接表来明确这一点。这也不会让读者知道您的查询是否会产生重复项。(不应该,因为安装了正确的密钥后,不应该将同一个关键字多次链接到一个商店。)
  • review_count在您的表中是唯一的吗?如果不是,则查询结果不具有确定性。可能是行7999、8000和8001具有相同的审阅计数。每次运行查询时,您可能会在结果中得到一行或另一行。如果您之前甚至运行过查询来获取前8000行,则在新选择的1000行中可能会有重复的行,而遗漏了其他行。使ORDER BY子句具有确定性,例如ORDER BY store0_.review_count, store0_.store_id的值。

这是关于性能的:

  • 使用偏移的速度很慢。DBMS必须对所有可能的结果行进行排序,以便找到位置8000并获取下一个1000。因此,如果您的想法是获取第一个1000,然后是下一个1000,然后是下一个1000,依此类推,DBMS将不得不一次又一次地对表进行排序。相反,记住你已经走了多远,然后只选择从那里开始的行。因此,必须进行排序的中间结果将随着每个新调用而变得越来越小。

下面是我可能编写此查询的方式:

select 
  s.name,
  s.status,
  s.address,
  c.name
from store s
inner join category c on c.category_id = s.category_id 
where s.review_count >= 0
and s.status = 'APPROVED'
and s.name like '가게%'
and s.store_id in
(
  select sk.store_id
  from store_keyword sk 
  where sk.keyword_id = 
  (
    select k.keyword_id 
    from keyword k
    where k.name = '분위기 좋은'
  )
)
and (s.review_count, s.store_id) > (@last_review_count, @last_store_id)
order by s.review_count, s.store_id
limit 1000;

字符串
MySQL 8提供了将元组与>进行比较的功能。如果您使用的是尚不支持此功能的旧版本,请将最后一个条件替换为:

and 
(
  s.review_count > @last_review_count
  or 
  (s.review_count = @last_review_count and s.store_id > @last_store_id)
)


关于指数:当然,你必须能够快速找到一个关键字

create unique index idx1 on keyword (name, keyword_id);
create unique index idx2 on keyword (keyword_id, store_id);


这些是您可能已经拥有的索引。
然后,您希望对存储表进行索引,但有四列限制了行的访问:

  • 查看计数>= 0
  • 状态=“已批准”
  • 名称LIKE ' %'
  • store_id在那些具有关键字''的集合中

现在,您需要一个索引,以便快速找到核心。如果90%的行为“已批准”,则该状态将没有太大帮助。如果只有0.9%的行为“已批准”,则将为“已批准”。如果大多数评论数是负面的,那么这是非常有选择性的,如果大多数是零或正面的,它不是。你希望有很多商店使用这个关键字还是很少?构建索引时,首先选择最具选择性的列,然后选择第二具选择性的列,依此类推。例如:

create index idx3 on store (status, store_id, name, review_count);


如果不安全,请使用不同的组合创建多个索引:

create index idx4 on store (review_count, status, name, store_id);
...


然后检查哪些正在使用,并删除其他的。

klh5stk1

klh5stk12#

更好的索引可能是:

CREATE INDEX X001 ON store (status, category_id, name, review_count, address)

字符串
还有更多:

CREATE INDEX X002 ON keyword (name, keyword_id)
CREATE INDEX X003 ON store_keyword (store_id, keywrd_id)

kuarbcqp

kuarbcqp3#

这些可能会有帮助。查看每个表的SHOW CREATE TABLE可能会有所帮助。

store0_:  INDEX(status, name, category_id, review_count)
storekeywo2_:  INDEX(store_id,  keyword_id)
keyword3_:  INDEX(name, keyword_id)

字符串
WHERE (a,b) > (c,d)的使用可能会优化,也可能不会优化; MySQL是什么版本?
如果store_keyword是多对多Map表,请遵循 Many-to-many 中的索引建议
LIMIT 100 OFFSET 8000是一个性能杀手--它必须找到8100行,然后一次遍历8000行。如果你在“分页”,这是第81页--谁有耐心在列表中走那么远??想想另一种方式,给予用户访问如此庞大的列表。

相关问题