如何实现大量的标签匹配?

环境

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的目标数量达到几十甚至上百个,查询效率低下。

目前考虑过:

  1. 因为分类可以动态变动,如果存储分类id,一旦分类发生变动会有极大的更新历史数据的负担。
  2. 将数据和tag及分类的关系展开存关系表然后join。需求层面的查询条件导致无论左右表谁做驱动表,结果都会出现几十万的结果条数,join之后nested loop行数太多还是很慢。
  3. 有人提议用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、可能会增加新的分类或删除原有的分类

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 16
巴啦啦

你说的如果存储类型id,动态变更,会引起更新历史数据的负担,这一点不是很明白,能说下场景吗? 历史数据是已经形成的,不应该去更改,应该要冗余标签名称。修改分类名称,不应该去改变原有涵义,只能修改为换一个说法,比如显示屏改为显示器。不应该显示屏改为枕头。如果需要枕头,那应该新增,删除分类,也应该只是隐藏。你分了表,最好冗余一张主表,方便查询。
你看我理解得对不对,你的结构是4级分类,每个商品,可以拥有任意一级的多个标签,即你存储标签的字段,一条记录可能存储着多个一级分类标签~多个四级分类标签。这些都在同一个数据库字段内。是这样吗?

5个月前 评论
anjing 5个月前
穿过你的黑发的我的手 (楼主) 5个月前
穿过你的黑发的我的手 (楼主) 5个月前
穿过你的黑发的我的手 (楼主) 5个月前
巴啦啦 (作者) 5个月前
穿过你的黑发的我的手 (楼主) 5个月前
巴啦啦 (作者) 5个月前
巴啦啦

你把标签,全部丢到redis位图里面,标签id为键,商品id为位数(假设你们的是商品),每形成一个商品或记录,就用商品或记录id,设置到位图对应的位置。也就是一个商品或记录的新增,将可能会对多个位图进行操作。需要多标签查询的时候,使用bittop命令把相关标签的位图做与操作,这样可以把拥有这些商品标签的商品或记录查询出来。你看这样行不

5个月前 评论
穿过你的黑发的我的手 (楼主) 5个月前
Complicated

没说的,你这量,最好还是es

5个月前 评论

es 使用 keyword 把, 匹配效率很高

5个月前 评论

用clickhouse吧。我就有你这种需求,直接算完存clickhouse了

3个月前 评论
穿过你的黑发的我的手 (楼主) 3个月前
哪吒的狗腿子 (作者) 3个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!