加速/优化 MySQL 语句 - 查找之前未选择的新行

Posted

技术标签:

【中文标题】加速/优化 MySQL 语句 - 查找之前未选择的新行【英文标题】:Speed-up/Optimise MySQL statement - finding a new row that hasn't been selected before 【发布时间】:2020-10-09 08:15:46 【问题描述】:

首先介绍一下表和数据库的背景。

我有一个 mysql 数据库,其中有几个表:

电影: 包含所有以 netflixid 作为唯一主键的电影/系列信息。

用户: 包含用户信息“ratingid”是唯一的主键

评分: 包含所有用户评分信息、netflixid 和复合“netflixid-userid”的唯一主键

此语句有效:

SELECT * 
FROM films 
WHERE 
    INSTR(countrylist, 'GB') 
    AND films.netflixid NOT IN (SELECT netflixid FROM rating WHERE rating.userid = 1) 
LIMIT 1

但是检索您尚未评级的新电影记录需要的时间越来越长。 (目前为 6.8 秒,在 8000 行电影台上大约 2400 个用户评分)

首先我认为它是 INSTR(countrylist, 'GB'),所以我将它们分成了自己的 tinyint 列 - 没有区别。 我也试过NOT EXISTS,但时间差不多。

关于如何快速从电影中选择新的“未分级”行有什么想法/想法?

谢谢!

【问题讨论】:

我会考虑单独的电影/国家/地区表(并避免列表和 INSTR。) 【参考方案1】:

尝试加入?

SELECT * 
FROM films
LEFT JOIN rating on rating.ratingid=CONCAT(films.netflixid,'-',1)
WHERE 
    INSTR(countrylist, 'GB')
    AND rating.pk IS NULL
LIMIT 1

或者做等效的NOT EXISTS。

【讨论】:

这是一个好的开始! 5.8s 运行这个! rating.pk 应该是我的场景的 rating.ratingid 对于没有评分的用户有什么作用?除非您的服务器功率不足,否则它真的不应该这么慢。 对于低评级用户来说大约需要 2.3 秒 - 我的主机目前有点动力不足,我可能会升级它,让它有更多的“咕噜声” 多少比例的电影有国标?这6000部电影是总数还是GB电影的数量? “正确”的做法是有一个 film_country 表,每部电影每个国家都有一行【参考方案2】:

我会推荐not exists

select *
from films f
where 
    instr(countrylist, 'GB')
    and not exists (
        select 1 from rating r where r.userid = 1 and f.netflixid  = r.netflixid 
    )
    

这应该利用rating表的主键索引,所以子查询执行得很快。

也就是说,外部查询中的instr() 函数也代表了一个瓶颈。由于函数调用,数据库无法在此处利用索引:基本上它需要将计算应用于整个表,然后才能进行过滤。为避免这种情况,您可能需要检查您的设计:即,有一个单独的表来表示电影和国家/地区之间的关系,每个元组在单独的行中;然后,您可以使用另一个 exists 子查询来过滤国家/地区。

【讨论】:

试过这个 - 7.5s 目前完成。我将 countrycode 字段拆分为自己的 tinyint 字段,因此将 INSTR 替换为 "GB=1" 对速度没有任何影响! 试过这个:` select * from movies f where instr(countrylist, 'GB') and not exists ( select 1 from rating r where r.userid = 1 and f.netflixid = r.netflixid ) LIMIT 1 ` 结果:(共 1 个,查询耗时 7.0784 秒。) @PeterTurner 。 . .这应该有最好的性能。我认为您没有正确描述 ratings 上的主键。【参考方案3】:

如果国家/地区列表包含多个国家/地区,则 INSTR(countrylist, 'GB') 可以更改为 countrylist = 'GB'countrylist LIKE '%GB%'

如果您只需要某些列的详细信息,请不要选择所有“*”。取决于列数,查询可能很慢

【讨论】:

尝试将 countrylist 拆分为单独的 tinyint 字段 - 查询时我使用了 "GB=1" 但仍然需要很长时间! 或者加入看起来不错的解决方案:SELECT * FROM rating left join films on films.netflixid = rating.netflixid WHERE films.countrylist = 'GB' AND AND films.netflixid IS NULL LIMIT 1

以上是关于加速/优化 MySQL 语句 - 查找之前未选择的新行的主要内容,如果未能解决你的问题,请参考以下文章

Mysql优化查询

如何用一款小工具大大加速MySQL SQL语句优化

索引原理与慢查询优化

MySQL查询性能调优化

简单的方式优化mysql

MySQL优化