如何实现大量的标签匹配?
环境#
mysql: 8.0.32
php: 8.1
laravel: 10.x
问题#
按月分表单月 200W 的数据量,标签 tags 字段需要做标签的精确匹配运算,标签有 4 级的分类,且分类可以动态变动。
目前用 mysql 的 json 字段存储了 tags,建立多值索引,利用 json_overlaps 函数进行查询,在目标匹配数量较小的情况下(如不超过 10 个),查询速度还在可以忍受的范围内。
目前的问题是,tags 存在分类,标签在第 4 级。需求层面可以多选 1,2,3 级分类查询,且分类能够动态变动,所以数据库没有存分类 id,多选上级分类时,统一转化为 4 级标签进行匹配。
造成 json_overlaps 的目标数量达到几十甚至上百个,查询效率低下。
目前考虑过:
- 因为分类可以动态变动,如果存储分类 id,一旦分类发生变动会有极大的更新历史数据的负担。
- 将数据和 tag 及分类的关系展开存关系表然后 join。需求层面的查询条件导致无论左右表谁做驱动表,结果都会出现几十万的结果条数,join 之后 nested loop 行数太多还是很慢。
- 有人提议用 elasticsearch 来做,一方面项目需要采购和额外开发,成本略高;另一方面缺乏 elasticsearch 使用经验也不确定是否可以实现,还有一定的学习成本。目前还是考虑优先基于 mysql 实现,实在不行的话再做其它考虑。
补充下大致的表结构:
CREATE TABLE `t_tags` (
`id` int NOT NULL AUTO_INCREMENT,
`tag_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '标签名称',
`parent_id` int NOT NULL DEFAULT '0' COMMENT '父级id',
`tag_level` tinyint DEFAULT '1' COMMENT '层级',
`id_path` varchar(64) DEFAULT NULL COMMENT 'id层级路径',
`name_path` varchar(64) DEFAULT NULL COMMENT '名称层级路径',
`tag_type` tinyint DEFAULT '1' COMMENT '类型:1分类,2标签',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='标签表'
CREATE TABLE `t_records` (
`id` int NOT NULL AUTO_INCREMENT,
`tag_list` json DEFAULT null COMMENT '命中标签集合',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='记录表'
所谓动态变动是指:
1、标签会增加新的,原有的标签也可能不再使用;
2、原有的标签和分类的从属关系可能会变更;
3、可能会增加新的分类或删除原有的分类
推荐文章: