elasticsearch 解决全模糊匹配最佳实践

事件背景:

某 CRM 系统,定义了如下两个表:

客户表 t_custom

字段名

类型

描述

idlong自增主键
phonestring客户手机
.........

客户产品关系表 t_custom_product

字段名

类型

描述

idlong自增主键
custom_idlong客户id
product_idlong产品id
.........

有个页面查询的需求,需要根据手机号模糊匹配,查询出所有匹配上的产品信息。

想要快速实现,可以写出如下 sql:

select from t_custom_product as ta left join t_custom as tb on ta.custom_id = tb.id where tb.phone like %#{phone}%

过了3年,其中 t_custom 已经有了100w 数据、t_custom_product 有了 1000w 数据,这时候,这条 sql 理所当然成了头号慢 sql。

新来的开发 @纪潘霞,受命解决这个问题。

改造 sql

一开始,纪先生想快速解决,就将 sql 改造成如下模式,然后给 phone 字段添加了正向以及反向索引 index(phone,id)   和 index (reverse(phone),id)

SELECT *  FROM (

    (select from t_custom_product as ta left join t_custom as tb on ta.custom_id = tb.id where tb.phone like #{phone}% order by id desc limit 10)

        UNION ALL 

    (select from t_custom_product as ta left join t_custom as tb on ta.custom_id = tb.id where tb.phone like %#{phone} order by id desc limit 10)

AS combined 

ORDER BY id DESC  

LIMIT 10;

改造之后,查询速度飞起,慢 sql 没有了。但是线上用户开始抱怨了,只输中间号码的场景,无法查询了。例如某客户手机号为:13098830998,查询 988 就无法查询出来。

纪先生说:

es 保存宽表

因为不符合需求,方案打回重做。

这时候,纪先生申请了一套 ES 集群。使用黄工提供的 canal 、datax 技术,将这个表的数据打成大宽表,写入 ES,字段大致如下:

客户关系宽表 index_custom_product (1000w数据)

字段名

类型

描述

idlongt_custom_product 的主键
custom_idlongt_custom 的主键
phonestringt_custom 的手机号
product_idlong产品id
.........

先采用 wildcard 的语句进行查询,查询语句如下 (不加 keyword 什么都查不出来):

GET index_custom_product/_search

{

    "query": {

        "wildcard" : {

            "phone.keyword" "*#{phone}*"

        }

    }

}

结果发现性能超差,查询资料得知,这种也是走的全表扫描。

es 匹配搜索

考虑 es 本身支持搜索,所以将查询改用搜索的方式:

GET index_custom_product/_search

{

    "query": {

        "match" : {

            "phone" "#{phone}"

        }

    }

}

结果在搜索号码片段的时候,什么都查不出来。查阅资料得知,默认分词器,不会对数字进行分词。

考虑切换成 N-gram 分词器,这个分词器特性如下:

POST _analyze

{

  "tokenizer""ngram",

  "text""Quick Fox"

}

将会返回(其中 min length = 1 ,max length = 2 )

[ Q, Qu, u, ui, i, ic, c, ck, k, "k "" "" F", F, Fo, o, ox, x ]

原理讲解部分(略,即兴演讲)

纪先生切换之后,这时候对手机号 13098830995 分词,会返回如下结果:

1133300099988...... ]

然后如果用户查询 988,这个查询会被解析为如下词组的查询

9,98,8,88 ]

显然,可以匹配到手机号 13098830995 的分词,从而查询出结果。
 

仍然有问题

改造之后,纪先生高兴的发布了,结果毫无疑问的,被测试打回来了。

因为测试拿 998 查询,结果只要手机号有 9 的数据都查询出来了。

因为 match  只要有一个词匹配,即匹配成功。

将 match 改成 match_phrase 即可。

GET index_custom_product/_search

{

    "query": {

        "match_phrase" : {

            "phone" "#{phone}"

        }

    }

}

还有问题?

改造之后,纪先生高兴的发布了,结果又被测试打回来了。

测试拿出 9888 进行查询,这个查询的分词组为:

9,98,8,88 ]

显然,可以匹配到手机号 13098830995。

纪先生接着又改了一版,考虑到查询出的结果集已经比较固定了,所以加了 filter 作为后置过滤,通过正则过滤出正确的手机号。

GET index_custom_product/_search

{

    "query": {

      "must": [

        "match_phrase": { "phone":   "#{phone}"        }}

      ],

      "filter": [

        "regexp":  { "phone""#{phone_regexp}" }}

      ]

    }

}

性能问题?调优

发布之后,某天,用户输入了 1111111111111(11个1) 进行查询,因为查询很慢,用户等不及狂点起来了。

毫无疑问的,ES 集群挂了。

这个是个开放性的结果,有后面几个调优方向:

  1. 经过纪先生和产品的激烈沟通,业务同意这个查询框加入一个判断条件:至少输入 4 位数字。然后 ngram length 调整到 4、4。(需要业务妥协,资源消耗少)
  2. ngram 调整 length 到 2、11,但是这样会让 es 内存占用加倍,需要扩容一下 ES。(业务体验最好,最占资源)
  3. ngram 调整 length 到 2、2,然后改用 term 查询,但是会有一定的幻觉。例如 8398 可以查询出 13398830995 => 高亮词汇为 83、39、98。(资源消耗少,极端情况会有异常数据)

这里只是给一个思路,调优本质上还是根据具体业务场景进行定制,技术和业务的互相妥协

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/49762.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

笔记本检测工具 | 爱回收笔记本质检系统 v1.9.6

软件简介 爱回收笔记本质检系统是一款专为笔记本电脑硬件检测而设计的软件。它以其快速的检测速度、简便的操作流程和直观的检测结果,为用户提供了一种高效、易懂的硬件检测解决方案。 这款软件不仅适用于对电脑硬件有一定了解的用户,也特别适合对硬件…

C#使用csvhelper实现csv的操作

新建控制台项目 安装csvhelper 33.0.1 写入csv 新建Foo.cs namespace CsvSut02;public class Foo {public int Id { get; set; }public string Name { get; set; } }批量写入 using System.Globalization; using CsvHelper; using CsvHelper.Configuration;namespace Csv…

如何为WordPress网站设置多语言站点

随着全球化的发展,拥有一个支持多语言的站点已成为提升用户体验、扩大受众范围的重要手段。本文将详细介绍如何为WordPress网站设置多语言站点,提供两种最佳方案详解,帮助您轻松实现多语言站点的搭建与管理。无论您是选择在同一站点内发布多语…

FastGPT 知识库搜索测试功能解析(一)

本文以 FastGPT 知识库的搜索测试功能为入口,分析 FastGPT 的知识检索流程。 一、搜索功能介绍 1.1 整体介绍 搜索测试功能包含三种类型:语义检索、全文检索、混合检索。 语义检索:使用向量进行文本相关性查询,即调用向量数据库根据向量的相似性检索; 全文检索:使用…

我在Vscode学Java集合类

Java集合类 一、集合1.1 集合和数组之间的对比1.2 集合框架的核心接口1.3 集合框架中的实现类单列集合双列集合 1.4 集合框架的特点 二、 Collection集合与Iterator迭代器2.1 Collection的概述2.1.1 常用方法增加元素的方法修改元素的方法删除元素的方法查询元素的方法遍历集合…

LLMs之Agent:Agentscope的简介、安装和使用方法、案例应用之详细攻略

LLMs之Agent:Agentscope的简介、安装和使用方法、案例应用之详细攻略 目录 Agentscope的简介 1、更新的日志 2、支持的模型API 3、支持的服务 Agentscope的安装和使用方法 1、安装 支持的本地模型部署 从源码安装 使用pip 配置 创建Agent 构造对话 Age…

Go语言编程 学习笔记整理 第2章 顺序编程 后半部分

1.流程控制 1.1 条件语句 if a < 5 { return 0 } else { return 1 } 注意&#xff1a;在有返回值的函数中&#xff0c;不允许将“最终的”return语句包含在if...else...结构中&#xff0c; 否则会编译失败&#xff01;&#xff01;&#xff01; func example(x int) i…

基于 HTML+ECharts 实现智慧景区数据可视化大屏(含源码)

构建智慧景区数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 随着旅游业的蓬勃发展&#xff0c;智慧景区的概念逐渐深入人心。通过数据可视化&#xff0c;景区管理者可以实时监控游客流量、设施使用情况以及环境状况&#xff0c;从而提升游客体验和管理效率。本文将详…

线性代数:向量组与向量组等价

向量组概述 向量组是由若干个同维数的行向量或列向量组成的集合。这些向量可以是二维的、三维的&#xff0c;甚至是更高维度的。在数学和物理学的许多领域中&#xff0c;向量组扮演着至关重要的角色&#xff0c;特别是在线性代数、矩阵论、以及工程学中。 例如&#xff0c;一…

《梦醒蝶飞:释放Excel函数与公式的力量》18.2 数据可视化技术

第18章&#xff1a;创建图表和数据可视化 18.2 数据可视化技术 数据可视化是将数据转化为图形和图表的过程&#xff0c;以便更好地理解和分析数据。有效的数据可视化可以揭示数据的模式、趋势和异常&#xff0c;从而帮助做出更明智的决策。以下将介绍几种常用的数据可视化技术…

3dsMax 设置近平面削减,靠近模型之后看不到模型,看很小的模型放大看不到

3dsMax 设置近平面削减&#xff0c;靠近模型之后看不到模型&#xff0c;看很小的模型放大看不 问题展示 解决办法_1 把这两个东西最上面的拖拽到最上面&#xff0c;最下面的拖拽到最下面。 解决办法_2 勾选视口裁剪 把这两个东西最上面的拖拽到最上面&#xff0c;最下面的…

服务器部署环境(docker安装Mysql + Redis + MongoDB)

1. 安装Docker 1、选择要安装的平台 Docker要求CentOS系统的内核版本高于3.10 uname -r #通过 uname -r 命令查看你当前的内核版本官网地址 2. 卸载已安装的Docker, 使用Root权限登录 Centos。确保yum包更新到最新。 sudo yum update如果操作系统没有安装过Docker , 就不需要…

使用Gradle构建编译Spring boot 2.7.x

一、环境准备 JDK 1.8Spring boot 2.7.xGradle 7.5.1 (安装参考&#xff1a;win11安装Gradle)Idea 2023.1 二、源码导入gitee(可选) 按需导入。如果能科学上网&#xff0c;可跳过这一步。 为避免github访问不稳定问题&#xff0c;建议将对应的代码导入到gitee。然后通过git管…

Redis的缓存问题:缓存穿透、缓存击穿和缓存雪崩

目录 一、缓存穿透 1、问题描述 2、解决方案 二、缓存击穿 1、问题描述 2、解决方案 三、缓存雪崩 1、问题描述 2、解决方案 3、雪崩案例 一、缓存穿透 1、问题描述 缓存穿透指的是⼤量请求的 key根本不存在于缓存中&#xff0c;每次针对此key的请求从缓存获取不到…

【element ui】input输入控件绑定粘贴事件,从 Excel 复制的数据粘贴到输入框(el-input)时自动转换为逗号分隔的数据

目录 1、需求2、实现思路:3、控件绑定粘贴事件事件修饰符说明: 4、代码实现&#x1f680;写在最后 1、需求 在 Vue 2 和 Element UI 中&#xff0c;要实现从 Excel 复制空格分隔的数据&#xff0c;并在粘贴到输入框&#xff08;el-input&#xff09;时自动转换为逗号分隔的数据…

推荐系统三十六式学习笔记:工程篇.常见架构24|典型的信息流架构是什么样的

目录 整体框架数据模型1.内容即Activity2.关系即连接 动态发布信息流排序数据管道总结 从今天起&#xff0c;我们不再单独介绍推荐算法的原理&#xff0c;而是开始进入一个新的模块-工程篇。 在工程实践的部分中&#xff0c;我首先介绍的内容是当今最热门的信息流架构。 信息…

ElasticSearch(四)— 数据检索与查询

一、基本查询语法 所有的 REST 搜索请求使用_search 接口&#xff0c;既可以是 GET 请求&#xff0c;也可以是 POST请求&#xff0c;也可以通过在搜索 URL 中指定索引来限制范围。 _search 接口有两种请求方法&#xff0c;一种是基于 URI 的请求方式&#xff0c;另一种是基于…

C#初级——基础语法

前言 学习Unity游戏编程开发会使用到两种语言&#xff0c;一种是C#&#xff0c;另一种是Javascript。有学习C语言基础和想学unity游戏开发的萌新一般都推荐学习C#基础编程&#xff0c;以此来快速上手unity的学习。 本次学习使用到的工具为&#xff1a;VS2022 环境安装 首先&a…

IDEA搭建Vue开发环境(安装Node.js、安装vue-cli、创建项目、编译项目、启动项目、yarn启动项目、npm和yarn命令行命令简单使用)

目录 1. 安装Node.js2. 安装vue-cli构建工具3. 使用vue-cli创建项目4. 启动项目5. IDEA启动vue6. 在IDEA编译vue项目7. 用yarn启动vue项目8. npm和yarn命令行命令简单使用8.1 npm8.2 yarn 1. 安装Node.js Node.js基于Google的V8引擎&#xff0c;形成了一个Javascript的运行环境…

CFPRF:一种用于音频时间伪造检测和定位的框架

关键词&#xff1a;音频伪造检测、时间伪造定位、差异特征感知学习 最近&#xff0c;一种新型的音频部分伪造形式对音频取证提出了挑战&#xff0c;这要求采取先进的对策来检测长时间音频中的微妙伪造操作。然而&#xff0c;现有的对策仍然服务于分类目的&#xff0c;未能对部分…