如何在过滤系统中优化多个 "LIKE" 搜索中的慢速 MySQL 查询?
How to optimize the slow MySQL query in multiple "LIKE" search in a filtering system?
我在这里读了很多文章。有很多类似的问题,但我找不到适合我情况的问题。请原谅我,因为我是一名新的网站开发人员,而且我的编码方式很丑陋。
我使用 Laravel 8 和 MySQL InnoDB 来处理我的数据库。我的网站是一个多语言视频信息网站。这是我的 table 结构。
我的表格:
视频
- id(整数)
- 代码(varchar)
- 名称(可变字符)
- 日期(日期)
- 持续时间(整数)
- director_id (varchar)
- genre_id (varchar) [例如68#10#185#237#89#340#156]
videos_genres
- genre_id
- genre_tw
- genre_en
- genre_ja
videos_preview
- 代码
- 拇指
- 封面
- 预览
有10组流派(c1-c10),每组大约有100种流派。每个流派都有一个唯一的 ID,即使它们属于不同的组。在 video table
中,我以 68#10#185#237#89#340#156
的形式存储视频流派 ID。然后我可以使用爆炸“#”将流派 ID 数据返回到数组。然后我可以用这些id加入genre table
。
过滤系统是网站的核心功能。当人们选择多种类型时,他们可以缩小结果范围并准确地得到他们想要的。我使用 GET 方法将 url 请求传递给 Laravel 中的 VideoController,例如 example.com/?c1=68,10&c2=185,237&c7=89,340,156
搜索步骤如下:
- 我将
/?c1=8&c2=33&c7=81
放入数组 $cArr
[68,10,185,237,89,340,156]
- 然后在带有多个LIKE操作的查询中使用
$cArr
$data = cache()->remember($_SERVER['REQUEST_URI'], 60*60*24, function() use($cArr){
return DB::table('videos')
->Where(function ($query) use($cArr) {
for ($i = 0; $i < count($cArr); $i++){
$query->where('genre_id', 'like', $cArr[$i] .'#%');
}})
->orWhere(function ($query) use($cArr) {
for ($i = 0; $i < count($cArr); $i++){
$query->where('genre_id', 'like', '%#' . $cArr[$i]);
}})
->orWhere(function ($query) use($cArr) {
for ($i = 0; $i < count($cArr); $i++){
$query->where('genre_id', 'like', '%#' . $cArr[$i] .'#%');
}})
->leftjoin('videos_preview','videos_preview.code','=','videos.code')
->orderBy('videos.publish_date', 'DESC')
->limit(400)->get();
将生成如下所示的慢速查询。 运行 搜索 300K 行大约用了 10 秒。
select * from `videos` left join `videos_preview` on `videos_preview`.`code` = `videos`.`code`
where (`genre_id` like '68#%' and `genre_id` like '10#%' and `genre_id` like '185#%' and `genre_id` like '237#%' and `genre_id` like '89#%' and `genre_id` like '340#%' and `genre_id` like '156#%')
or (`genre_id` like '%#68' and `genre_id` like '%#10' and `genre_id` like '%#185' and `genre_id` like '%#237' and `genre_id` like '%#89' and `genre_id` like '%#340' and `genre_id` like '%#156')
or (`genre_id` like '%#68#%' and `genre_id` like '%#10#%' and `genre_id` like '%#185#%' and `genre_id` like '%#237#%' and `genre_id` like '%#89#%' and `genre_id` like '%#340#%' and `genre_id` like '%#156#%') order by `videos.publish_date` desc limit 400;
我有一个 6GB 内存和 6CPU 个内核 VPS。但是随着最近流量的增加(同时有大约 500 名访问者)和数据库每天增长 300 多行。我刚刚发现 MySQL 查询将我的 CPU 消耗到了 100%。如您所见,我已经将结果缓存了 24 小时,但是多种类型的组合太多了。大多数组合是在未缓存的 24 小时内首次出现。
请帮帮我。在 Laravel 8 中是否有更好的方法以更好的方式归档相同的过滤器功能?预先感谢您让每一个生命更安全。抱歉我的英语不好。
- AND 和 OR 搞得一团糟。重新思考。
LIKE '%...'
必须检查每一行
OR
必须检查每一行
这将不得不检查每一行,但它会更快:
WHERE FIND_IN_SET(genre, '68,10,185,237,89,340,156')
请注意,此处需要逗号。这将检查 genre 是这些数字之一。你想将用户提供的类型测试与在一起吗?或者他们?
-- Both 185 and 10:
WHERE FIND_IN_SET(185, '68,10,185,237,89,340,156')
AND FIND_IN_SET( 10, '68,10,185,237,89,340,156')
-- Both 185 or 10:
WHERE FIND_IN_SET(185, '68,10,185,237,89,340,156')
OR FIND_IN_SET( 10, '68,10,185,237,89,340,156')
另一种方法是使用全文索引:
-- The column `genre` might be "mystery drama documentary comedy"
WHERE MATCH(`genre`) AGAINST ("+comedy +musical" IN BOOLEAN MODE)
那会 运行 快很多,因为它创建了单词的反向索引。 The Against 说它必须既是喜剧又是音乐剧。 (因此样本 genre
不匹配。)
我在这里读了很多文章。有很多类似的问题,但我找不到适合我情况的问题。请原谅我,因为我是一名新的网站开发人员,而且我的编码方式很丑陋。
我使用 Laravel 8 和 MySQL InnoDB 来处理我的数据库。我的网站是一个多语言视频信息网站。这是我的 table 结构。
我的表格:
视频
- id(整数)
- 代码(varchar)
- 名称(可变字符)
- 日期(日期)
- 持续时间(整数)
- director_id (varchar)
- genre_id (varchar) [例如68#10#185#237#89#340#156]
videos_genres
- genre_id
- genre_tw
- genre_en
- genre_ja
videos_preview
- 代码
- 拇指
- 封面
- 预览
有10组流派(c1-c10),每组大约有100种流派。每个流派都有一个唯一的 ID,即使它们属于不同的组。在 video table
中,我以 68#10#185#237#89#340#156
的形式存储视频流派 ID。然后我可以使用爆炸“#”将流派 ID 数据返回到数组。然后我可以用这些id加入genre table
。
过滤系统是网站的核心功能。当人们选择多种类型时,他们可以缩小结果范围并准确地得到他们想要的。我使用 GET 方法将 url 请求传递给 Laravel 中的 VideoController,例如 example.com/?c1=68,10&c2=185,237&c7=89,340,156
搜索步骤如下:
- 我将
/?c1=8&c2=33&c7=81
放入数组$cArr
[68,10,185,237,89,340,156] - 然后在带有多个LIKE操作的查询中使用
$cArr
$data = cache()->remember($_SERVER['REQUEST_URI'], 60*60*24, function() use($cArr){
return DB::table('videos')
->Where(function ($query) use($cArr) {
for ($i = 0; $i < count($cArr); $i++){
$query->where('genre_id', 'like', $cArr[$i] .'#%');
}})
->orWhere(function ($query) use($cArr) {
for ($i = 0; $i < count($cArr); $i++){
$query->where('genre_id', 'like', '%#' . $cArr[$i]);
}})
->orWhere(function ($query) use($cArr) {
for ($i = 0; $i < count($cArr); $i++){
$query->where('genre_id', 'like', '%#' . $cArr[$i] .'#%');
}})
->leftjoin('videos_preview','videos_preview.code','=','videos.code')
->orderBy('videos.publish_date', 'DESC')
->limit(400)->get();
将生成如下所示的慢速查询。 运行 搜索 300K 行大约用了 10 秒。
select * from `videos` left join `videos_preview` on `videos_preview`.`code` = `videos`.`code`
where (`genre_id` like '68#%' and `genre_id` like '10#%' and `genre_id` like '185#%' and `genre_id` like '237#%' and `genre_id` like '89#%' and `genre_id` like '340#%' and `genre_id` like '156#%')
or (`genre_id` like '%#68' and `genre_id` like '%#10' and `genre_id` like '%#185' and `genre_id` like '%#237' and `genre_id` like '%#89' and `genre_id` like '%#340' and `genre_id` like '%#156')
or (`genre_id` like '%#68#%' and `genre_id` like '%#10#%' and `genre_id` like '%#185#%' and `genre_id` like '%#237#%' and `genre_id` like '%#89#%' and `genre_id` like '%#340#%' and `genre_id` like '%#156#%') order by `videos.publish_date` desc limit 400;
我有一个 6GB 内存和 6CPU 个内核 VPS。但是随着最近流量的增加(同时有大约 500 名访问者)和数据库每天增长 300 多行。我刚刚发现 MySQL 查询将我的 CPU 消耗到了 100%。如您所见,我已经将结果缓存了 24 小时,但是多种类型的组合太多了。大多数组合是在未缓存的 24 小时内首次出现。
请帮帮我。在 Laravel 8 中是否有更好的方法以更好的方式归档相同的过滤器功能?预先感谢您让每一个生命更安全。抱歉我的英语不好。
- AND 和 OR 搞得一团糟。重新思考。
LIKE '%...'
必须检查每一行OR
必须检查每一行
这将不得不检查每一行,但它会更快:
WHERE FIND_IN_SET(genre, '68,10,185,237,89,340,156')
请注意,此处需要逗号。这将检查 genre 是这些数字之一。你想将用户提供的类型测试与在一起吗?或者他们?
-- Both 185 and 10:
WHERE FIND_IN_SET(185, '68,10,185,237,89,340,156')
AND FIND_IN_SET( 10, '68,10,185,237,89,340,156')
-- Both 185 or 10:
WHERE FIND_IN_SET(185, '68,10,185,237,89,340,156')
OR FIND_IN_SET( 10, '68,10,185,237,89,340,156')
另一种方法是使用全文索引:
-- The column `genre` might be "mystery drama documentary comedy"
WHERE MATCH(`genre`) AGAINST ("+comedy +musical" IN BOOLEAN MODE)
那会 运行 快很多,因为它创建了单词的反向索引。 The Against 说它必须既是喜剧又是音乐剧。 (因此样本 genre
不匹配。)