最近利用H5原生框架写了一款类似于抖音的网页版短视频系统, 这是一款基于 MySQL数据库的视频资源分享平台中,其中搜索功能经常需要针对每条视频标签(如 #生活、#时尚)进行搜索,另外我需要在用户点击某视频当中的标签后,搜索并展现打着该标签的类似视频。
目前是将标签存储在了短视频信息表中的某个字段
可是,在雏形基本完成的时候我忽然想到,如果后期数据量逐渐增大,标签搜索系统的模糊查询对服务器的资源占用也不可忽视,如果使用不当,即使是 60 万条数据也可能让数据库压力山大,查询速度慢得令人崩溃。
常见写法:LIKE 查询
以平台视频列表 playerlist
表为例,常见查询语句如下:
SELECT * FROM playerlist
WHERE videoLabel LIKE '%生活%'
ORDER BY like_count DESC;
初看没问题,但 LIKE '%关键词%'
这样的写法其实非常影响性能,尤其是在数据量上万甚至几十万时。
原因分析:为什么 LIKE 会慢?
- 无法使用索引: 前缀有
%
,MySQL 无法使用索引加速查找。 - 全表扫描: 只能逐条扫描所有数据进行字符串匹配。
- 排序增加负担: 再加上
ORDER BY
排序,MySQL 更吃力。
优化方案
1. 使用 FULLTEXT 全文索引
MySQL 提供了 FULLTEXT
索引,特别适合对文本字段的模糊匹配需求:
ALTER TABLE playerlist ADD FULLTEXT(videoLabel);
SELECT * FROM playerlist
WHERE MATCH(videoLabel) AGAINST('生活' IN NATURAL LANGUAGE MODE)
ORDER BY like_count DESC;
优点:
- 性能优于
LIKE
- 支持分词搜索(英文很好,中文需配合 ngram)
2. 建立标签表,实现多对多结构
如果标签是以 #生活,#潮流
形式存在,建议进行结构优化,将标签拆分为独立表:
tags
:存储所有标签playerlist_tags
:视频与标签的关联关系
SELECT p.*
FROM playerlist p
JOIN playerlist_tags pt ON p.id = pt.player_id
JOIN tags t ON pt.tag_id = t.id
WHERE t.name = '生活'
ORDER BY p.like_count DESC;
优势:
- 结构清晰,便于维护
- 性能好,可灵活支持多标签搜索
- 易扩展:标签权重、热度、推荐等功能
3. 利用缓存系统
热门关键词如“生活”、“时尚”等,可以用 Redis 缓存查询结果,减少数据库压力。
4. 最低限度优化:建立普通索引
虽然 LIKE '%关键词%'
用不上索引,但仍可以为 videoLabel
建一个普通索引备用:
ALTER TABLE playerlist ADD INDEX idx_videoLabel (videoLabel);
实践方案
- 60 万数据在短视频系统当中,已经算是非常保守的量级.不是小数据量,
LIKE '%关键词%'
查询在这类场景下性能堪忧。根据项目阶段与结构,目前是在建表优化期,当前我准备使用多对多,可扩展的设计结构,也就是上面我说的第二个方案.查询时从tags
表找到目标标签ID,拿ID去playerlist_tags
表找到标签绑定的所有相关视频ID,然后去playerlist拿出目标videoID并展示.(直接避免了模糊搜索,并且最大程度依赖表的索引机制加快了查询) - 由于短视频系统属于高并发高查询场景,应该将每次查询结果存入Redis 中,当下次查询已经缓存过的数据则直接从缓存中取出数据返回给用户
- 限制游客禁止搜索,已登录用户限制使用搜索功能的周期频率,甚至限制低等级用户禁止搜索功能.
- 巧用CDN缓存功能,分库分表查询
合理设计结构和查询,是系统高性能的基础。希望这篇实战记录能帮到你,也欢迎收藏与分享。