如何实现大量的标签匹配?
环境
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、可能会增加新的分类或删除原有的分类
es