牛!单表千万行数据库: LIKE 搜索优化手记

news2024/10/5 20:26:28

我们可以在 txt 列上建立索引,用于优化该查询:

CREATE INDEX tbl_like_txt_idx ON [tbl_like] ( [txt] )

应用索引后,查询速度大大加快,仅需 5 毫秒:

由此可知:对于左前缀匹配,我们可以通过增加索引的方式来加快查询速度。

右后缀匹配查询优化


在右后缀匹配查询中,上述索引对右后缀匹配并不生效。使用以下 SQL 语句查询所有以“孙悟空”结尾的数据:

SELECT * FROM tbl_like WHERE txt LIKE ‘%孙悟空’

效率十分低下,耗时达到了 2.5秒:

我们可以采用“以空间换时间”的方式来解决右后缀匹配查询时效率低下的问题。

简单来说,我们可以将字符串倒过来,让右后缀匹配变成左前缀匹配。以“防着古海回来再抓孙悟空”为例,将其倒置之后的字符串是“空悟孙抓再来回海古着防”。当需要查找结尾为“孙悟空”的数据时,去查找以“空悟孙”开头的数据即可。

具体做法是:在该表中增加“txt_back”列,将“txt”列的值倒置后,填入“txt_back”列中,最后为 “txt_back”列增加索引。

ALTER TABLE tbl_like ADD txt_back nvarchar(1000);-- 增加数据列

UPDATE tbl_like SET txt_back = reverse(txt); – 填充 txt_back 的值

CREATE INDEX tbl_like_txt_back_idx ON [tbl_like] ( [txt_back] );-- 为 txt_back 列增加索引

数据表调整之后,我们的 SQL 语句也需要调整:

SELECT * FROM tbl_like WHERE txt_back LIKE ‘空悟孙%’

此番操作下来,执行速度就非常迅速了:

由此可知:对于右后缀匹配,我们可以建立倒序字段将右后缀匹配变成左前缀匹配来加快查询速度。

模糊查询优化


在查询所有包含“悟空”的语句时,我们使用以下的 SQL 语句:

SELECT * FROM tbl_like WHERE txt LIKE ‘%悟空%’

该语句无法利用到索引,所以查询非常慢,需要 2.7 秒:

遗憾的是,我们并没有一个简单的办法可以优化这个查询。但没有简单的办法,并不代表没有办法。解决办法之一就是:分词+倒排索引。

分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。我们知道,在英文的行文中,单词之间是以空格作为自然分界符的,而中文只是字、句和段能通过明显的分界符来简单划界,唯独词没有一个形式上的分界符,虽然英文也同样存在短语的划分问题,不过在词这一层上,中文比之英文要复杂得多、困难得多。

倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。

以上两段让人摸不着头脑的文字来自百度百科,你可以和我一样选择忽略他。

我们不需要特别高超的分词技巧,因为汉语的特性,我们只需“二元”分词即可。

所谓二元分词,即将一段话中的文字每两个字符作为一个词来分词。还是以“防着古海回来再抓孙悟空”这句话为例,进行二元分词之后,得到的结果是:防着、着古、古海,海回,回来,来再,再抓,抓孙,孙悟,悟空。使用 C# 简单实现一下:

public static List Cut(String str)

{

var list = new List();

var buffer = new Char[2];

for (int i = 0; i < str.Length - 1; i++)

{

buffer[0] = str[i];

buffer[1] = str[i + 1];

list.Add(new String(buffer));

}

return list;

}

测试一下结果:

我们需要一张数据表,把分词后的词条和原始数据对应起来,为了获得更好的效率,我们还用到了覆盖索引:

CREATE TABLE tbl_like_word (

[id] int identity,

[rid] int NOT NULL,

[word] nchar(2) NOT NULL,

PRIMARY KEY CLUSTERED ([id])

);

CREATE INDEX tbl_like_word_word_idx ON tbl_like_word(word,rid);-- 覆盖索引(Covering index)

以上 SQL 语句创建了一张名为 ”tbl_like_word“的数据表,并为其 ”word“和“rid”列增加了联合索引。这就是我们的倒排表,接下来就是为其填充数据。

我们需要先用 LINQPad 自带的数据库链接功能链接至数据库,之后就可以在 LINQPad 中与数据库交互了。首先按 Id 顺序每 3000 条一批读取 tbl_like 表中的数据,对 txt 字段的值分词后生成 tbl_like_word 所需的数据,之后将数据批量入库。完整的 LINQPad 代码如下:

void Main()

{

var maxId = 0;

const int limit = 3000;

var wordList = new List<Tbl_like_word>();

while (true)

{

$“开始处理:{maxId} 之后 {limit} 条”.Dump(“Log”);

//分批次读取

var items = Tbl_likes

.Where(i => i.Id > maxId)

.OrderBy(i => i.Id)

.Select(i => new { i.Id, i.Txt })

.Take(limit)

.ToList();

if (items.Count == 0)

{

break;

}

//逐条生产

foreach (var item in items)

{

maxId = item.Id;

//单个字的数据跳过

if (item.Txt.Length < 2)

{

continue;

}

var words = Cut(item.Txt);

wordList.AddRange(words.Select(str => new Tbl_like_word { Rid = item.Id, Word = str }));

}

}

“处理完毕,开始入库。”.Dump(“Log”);

this.BulkInsert(wordList);

SaveChanges();

“入库完成”.Dump(“Log”);

}

// Define other methods, classes and namespaces here

public static List Cut(String str)

{

var list = new List();

var buffer = new Char[2];

for (int i = 0; i < str.Length - 1; i++)

{

buffer[0] = str[i];

buffer[1] = str[i + 1];

list.Add(new String(buffer));

}

return list;

}

以上 LINQPad 脚本使用 Entity Framework Core 连接到了数据库,并引用了 NuGet 包“EFCore.BulkExtensions”来做数据批量插入。

之后,就可以把查询安排上,先查询倒排索引,然后关联到主表:

SELECT TOP 10 * FROM tbl_like WHERE id IN (

SELECT rid FROM tbl_like_word WHERE word IN (‘悟空’))

查询速度很快,仅需十几毫秒:

因为我们将所有的语句分成了二字符词组,所以当需要对单个字符模糊查询时,直接使用 LIKE 是一个更加经济的方案。如果需要查询的字

符多于两个时,就需要对查询词进行分词。如需查询“东土大唐”一词,构造出的查询语句可能会是这样:

SELECT TOP 10*FROM tbl_like WHERE id IN (

SELECT rid FROM tbl_like_word WHERE word IN (‘东土’,‘土大’,‘大唐’))

但是,该查询并不符合我们的预期,因为其将只包含“土大”的语句也筛选了出来:

我们可以采取一些技巧来解决这个问题,比如先 GROUP 一下:

SELECT TOP

10 *

FROM

tbl_like

WHERE

id IN (

SELECT

rid

FROM

tbl_like_word

WHERE

word IN ( ‘东土’, ‘土大’, ‘大唐’ )

GROUP BY

rid

HAVING

COUNT ( DISTINCT ( word ) ) = 3

)

在上述 SQL 语句中,我们对 rid 进行了分组,并筛选出了不重复的词组数量是三个(即我们的查询词数量)的。于是,我们可以得到正确的结果:

由此可知:对于模糊查询,我们可以通过分词+倒排索引的方式优化查询速度。

后记


虽然在讲述时使用的是 SQL Server 数据库,但是以上优化经验对大部分关系型数据库来说是通用的,比如 MySQL、Oracle 等。
如果你和笔者一样在实际工作中使用 PostgreSQL 数据库,那么在做倒排索引时可以直接使用数组类型并配置 GiN 索引,以获得更好的开发和使用体验。需要注意的是,虽然 PostgreSQL 支持函数索引,但是如果对函数结果进行 LIKE 筛选时,索引并不会命中。
vYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTU2MTk1OC0wZTZiZTI5ODNhZDgwYTM0LnBuZw?x-oss-process=image/format,png)

由此可知:对于模糊查询,我们可以通过分词+倒排索引的方式优化查询速度。

后记


虽然在讲述时使用的是 SQL Server 数据库,但是以上优化经验对大部分关系型数据库来说是通用的,比如 MySQL、Oracle 等。
如果你和笔者一样在实际工作中使用 PostgreSQL 数据库,那么在做倒排索引时可以直接使用数组类型并配置 GiN 索引,以获得更好的开发和使用体验。需要注意的是,虽然 PostgreSQL 支持函数索引,但是如果对函数结果进行 LIKE 筛选时,索引并不会命中。

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

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

相关文章

市政工控典型安全解决方案

汽车制造业 MES系统 DNC系统 生产 安全域1 管理层 工控安全隔离装置 交换机 安全配置核查系统 HMI 历史数据库 运行监控系统 实时数据库 打印机过程 安全域2 监控层 工控漏洞扫描系统 安全交换机 工控安全审计系统 工控入侵检测系统工程师站 A 操作员站 A 实时数据库A 操作员站…

Linux中的文件传输命令(scp、rsync)、Linux之间通过sshkey公钥免密登录

scp测试环境&#xff1a;centos7.7 最小化安装&#xff0c;默认是有scp命令 -r 递归复制整个目录 -p 保留源文件的修改时间、访问时间和权限 -v 详细方式显示输出 -P&#xff08;大写&#xff09;指定port&#xff08;端口&#xff09;输出scp命令使用格式 scp /filename 目…

Linux 程序开发 之 库打桩机制

目录前言一、库打桩定义二、编译时打桩三、链接时打桩四、运行时打桩五、处理目标文件的工具前言 Linux 链接器支持一个很强大的技术&#xff0c;称为库打桩(library interpositioning)&#xff0c;它允许你截获对共享库函数的调用&#xff0c;取而代之执行自己的代码。使用打桩…

m基于ACO蚁群优化的货车运输路线规划matlab仿真,考虑车辆载重,单位运输成本等因素

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 蚁群算法是通过对自然界中真实蚂蚁的集体行为的观察,模拟而得到一种仿生优化算法,它具有很好的并行性,分布性.根据蚂蚁群体不同的集体行为特征,蚁群算法可分为受蚂蚁觅食行为启发的模型和受孵化分…

渗透学习-靶场篇-WebGoat靶场(JWT攻击)

文章目录前言一、介绍JWTJWT令牌结构获取的令牌的基本过程二、攻击方式以及靶场的搭建1.安装cWebGoat2.空加密验证攻击3.字典爆破三、认证-键值逻辑前言 本次主要学习了javaweb项目方面任意出现的一些安全问题&#xff0c;最主要的是有关于JWT身份认证上的攻击&#xff0c;并利…

es的搜索服务

1、在项目中&#xff0c;创建一个搜索服务的模块&#xff1a;search-service 2、在新创建的搜索模块中添加依赖&#xff1a; <dependencies><!--nacos--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-start…

MyBatis-Plus中的逻辑删除使用

系列文章目录 Mybatis-PlusSpringBoot结合运用_心态还需努力呀的博客-CSDN博客MyBaits-Plus中TableField和TableId用法_心态还需努力呀的博客-CSDN博客 MyBatis-Plus分页查询&#xff08;快速上手运用&#xff09;_心态还需努力呀的博客-CSDN博客_mybatis plus分页查询 MyBa…

【ESXi 7.x 升 8.x】ESXi 升级 —— 使用 ESXCLI 升级 ESXi(Offline Bundle ZIP)

目录3.1 示例 — 使用 ESXCLI 升级 ESXi&#xff08;Offline Bundle ZIP&#xff09;【目标&#xff1a;将 ESXi 6.5 U2 GA 升级为 7.0 U3f】&#xff08;1&#xff09;下载离线升级ZIP包&#xff08;2&#xff09;升级 ESXi① 查看离线包② 升级前确认ESXi版本③ 获取升级用的…

[附源码]Python计算机毕业设计大学生社团管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

ChatGPT教程之 01 什么是ChatGPT革命性的对话生成新工具

今天,我想揭开 ChatGPT 的神秘面纱——GANs*(生成对抗网络)的一个迷人的新应用程序,它在 AI 社区中引起了很大的轰动。 对于那些不熟悉 GAN 的人来说,它们是一种神经网络,它使用两个相互竞争的网络——一个生成器和一个鉴别器——来创建逼真的输出。生成器创建假输出,而…

Paper Note——经典Polyline类型Map论文损失函数

文章目录1. Hierarchical Recurrent Attention Networks for Structured Online Maps2. DAGMapper: Learning to Map by Discovering Lane Topology1. Hierarchical Recurrent Attention Networks for Structured Online Maps https://patrick-llgc.github.io/Learning-Deep-Le…

QT(4)-QAbstractItemView

QAbstractItemView1 说明2 常用函数2.1 交替行颜色2.1.1 alternatingRowColors2.1.2 setAlternatingRowColors2.2 autoScroll2.2.1 hasAutoScroll2.2.2 setAutoScroll2.3 autoScrollMargin2.3.1 autoScrollMargin2.3.2 setAutoScrollMargin2.4 defaultDropAction2.4.1 setDefau…

python--飞机大战

实现功能&#xff1a; 1&#xff1a;飞机的移动&#xff0c;发射子弹&#xff0c;手雷&#xff0c;生命值&#xff0c;生命条 2&#xff1a;敌飞机有3种形态&#xff08;小&#xff0c;中&#xff0c;大&#xff09;不同的飞机大小不一样&#xff0c;生命值不一样&#xff0c…

基于springboot+mybatis+mysql+vue在线订餐系统

基于springbootmybatismysqlvue在线订餐系统一、系统介绍二、功能展示1.主页(用户)2.菜单(用户)3.用户注册(用户)4.用户登陆(用户)5.我的订单(用户)6.我的购物车(用户)7.首页(管理员)8.用户管理(管理员)9.商品管理(管理员)9.订单管理(管理员)10.评论管理(管理员)三、获取源码一…

c++ 类型的转换

文章目录1. C语言中的类型转换1.1 隐式转换1.2 显示转换2. C的类型转换2.1 static_cast2.2 reinterpret_cast2.3 const_cast2.4 dynamic_cast3. 常见面试题前言&#xff1a; C给出了四种类型转换&#xff0c;这是做出的一些规范&#xff0c;为了减少隐式转换。隐式转换的问题&a…

JDK19都出来了~是时候梳理清楚JDK的各个版本的特性了【JDK14特性讲解】

JDK各个版本特性讲解-JDK14特性 一、Java14概述 Oracle在2020年3月17日宣布JAVA14 全面上市,JAVA14通过每六个月发布一次新功能,为企业和开发人员社区提供增强功能,继续了Oracle加快创新的承诺. 最新的JAVA开发工具包提供了新功能,其中包括两项备受期待的新预览功能,实例匹配的…

【Redis】分布式限流与Redis实现限流的四种方式(Redis专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

MHA高可用及故障切换

目录 一、MHA的概述 1、 MHA的概念 2&#xff0e;MHA 的组成 3&#xff0e;MHA 的特点 二、搭建 MySQL MHA的操作步骤 1、实验思路&#xff1a; 1.1、MHA架构 1.2、故障模拟 2、环境准备 3、修改 Master、Slave1、Slave2 节点的主机名 4、修改 Master、Slave1、Slav…

基于java+springboot+mybatis+vue+mysql的农产品销售商城网站

项目介绍 农产品销售成为了很多农产品商家的一项重要的创收项目。传统的销售方式采用了摆摊销售和市场的农产品的批发&#xff0c;在销售农产品方面不是很方便&#xff0c;因此采用了网络的方式进行销售。也是电子商务发展今天的一种趋势&#xff0c;建立了网站&#xff0c;前…

WebSocket——SpringBoot+Vue3+TS+SockJS+STOMP简单连接使用

WebSocket——SpringBootVue3TSSockJSSTOMP简单连接使用本文视频以及相关资源关于WebSocket文档什么时候使用WebSocketWebSocket连接头服务器返回状态码客户端使用技术Github地址npm安装为什么要安装sockjs的.d.ts文件常用方法1.连接2.关闭连接3.发送消息4.设置订阅构建1.创建w…