findinset mysql 性能_FindInSet性能优化

2023-05-16

背景:最近在mysql性能优化中遇到一个findinset()函数的性能问题:坦然说在数据量低的情况下,find_in_set的性能还是不错的,但是在30w左右开始严重劣化,到那时数据库结构已经定下来了,拆表的代价太大,而且带来很多冗余数据,找了很多方法还是不行,甚至我都开始考虑用ES来做了,但是自己搭一套ES集群实在麻烦,最终跑到隔壁组找了个专业dba,很快就解决了我的问题,看来专业的事情还是得交给专业的人来做哦!

findinset()

先介绍一下findinset吧!

不少数据表设计的时候使用一个字段来存储多对多关系,比如:

表 user中有一个字段叫 category, category存储的是 "1,3,9" 这样的类型的数据,实际上是category的id 用逗号分隔开来的

要查询一个用户属于id为2分类的用户可以这么写:

select * from `user` where find_in_set('2',`user`.`category`);

具体find_in_set 的使用请参照手册:

http://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_find-in-set

虽然这样很好用,但问题是如果数据量大,又无法走索引,是很慢的。

取网友的一个例子:

user 表录入 100万的数据,同时建立 user_category 表,每个user有 3 个分类,那么category表里有300万条记录:

CREATE TABLE `user_category` (     `id` int(11) NOT NULL AUTO_INCREMENT,     `user_id` int(11) DEFAULT NULL,     `category_id` int(11) DEFAULT NULL,     PRIMARY KEY (`id`),     KEY `category_id` (`category_id`),     KEY `user_id` (`tax_id`)   ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT

现在比较一下在百万级的数据量上使用 join 链接外键查询和find_in_set查询的性能

1. 使用 find_in_set 查询,平均时间在2.2秒左右

SELECT SQL_NO_CACHE COUNT(*) FROM `user` WHERE FIND_IN_SET(65,category)

2. 使用left join , 使用了右表中的索引,平均时间在0.2秒左右

SELECT SQL_NO_CACHE COUNT(DISTINCT(`user`.id)) FROM `user`    LEFT JOIN `user_category` ON `user`.`id`= `user_category`.`user_id`   WHERE `user_category`.`category_id`=75

这是采用一种空间换时间的办法。但是如果实在项目后期,又无法改变表结构又该怎么办呢?

既然问题的核心在于:findinset函数无法走索引,那给他加上索引不久好了

那么,全文索引登场了。

全文索引

通过数值比较、范围过滤等就可以完成绝大多数我们需要的查询,但是,如果希望通过关键字的匹配来进行查询过滤,那么就需要基于相似度的查询,而不是原来的精确数值比较。全文索引就是为这种场景设计的。你可能会说,用 like + % 就可以实现模糊匹配了,为什么还要全文索引?like + % 在文本比较少时是合适的,但是对于大量的文本数据检索,是不可想象的。全文索引在大量的数据面前,能比 like + % 快 N 倍,速度不是一个数量级,但是全文索引可能存在精度问题。你可能没有注意过全文索引,不过至少应该对一种全文索引技术比较熟悉:各种的搜索引擎。虽然搜索引擎的索引对象是超大量的数据,并且通常其背后都不是关系型数据库,不过全文索引的基本原理是一样的.

关于全文索引的详情请参考这个:https://blog.csdn.net/mrzhouxiaofei/article/details/79940958

使用全文索引

和常用的模糊匹配使用 like + % 不同,全文索引有自己的语法格式,使用 match 和 against 关键字,比如:

select * from fulltext_test where match(content,tag) against('xxx xxx');

注意: match() 函数中指定的列必须和全文索引中指定的列完全相同,否则就会报错,无法使用全文索引,这是因为全文索引不会记录关键字来自哪一列。如果想要对某一列使用全文索引,请单独为该列创建全文索引。

如:首先创建测试表,插入测试数据

create table test (

id int(11) unsigned not null auto_increment,

content text not null,

primary key(id),

fulltext key content_index(content)

) engine=MyISAM default charset=utf8;

insert into test (content) values ('aaaa'),('bbbb'),('cccc');

按照全文索引的使用语法执行下面查询:

select * from test where match(content) against('aaa');

注意:

MySQL 中的全文索引,有两个变量,最小搜索长度和最大搜索长度,对于长度小于最小搜索长度和大于最大搜索长度的词语,都不会被索引。通俗点就是说,想对一个词语使用全文索引搜索,那么这个词语的长度必须在以上两个变量的区间内。

对比结果:

使用findinset需要10s左右,使用全文索引只需要0.3s左右

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

findinset mysql 性能_FindInSet性能优化 的相关文章

  • MySQL 如何使用返回多行的 SELECT 子查询插入表?

    MySQL 如何使用返回多行的 SELECT 子查询插入表 INSERT INTO Results People names VALUES SELECT d id FROM Names f JOIN People d ON d id f i
  • 自定义 php 论坛 - 显示新的/未读的帖子

    我自己使用 php 编写了一个自定义论坛脚本 我决定不使用 phpbb 和其他工具 因为我希望我所做的事情具有 100 的灵活性 不过我遇到了一个问题 如何向用户显示帖子是否是新的 未读的 我想到了两种解决方案 1 饼干 2 数据库 我不想
  • 使用 Python 开发时保护 MySQL 密码?

    我正在编写一个使用本地托管的 MySQL 数据库的 Python 脚本 该程序将以源代码形式提供 这样 MySQL 密码就肉眼可见 有没有好的办法来保护这个呢 这个想法是为了防止一些顽皮的人查看源代码 直接访问 MySQL 并做一些事情 好
  • 从本地 html/javascript 网站插入 mySQL 数据库

    我正在尝试做什么 我的程序的目的是插入数据local HTML JS网站变成online 非本地 mySQL数据库 到目前为止我尝试过的 我试图用来实现此目的的原始方法是让我的本地网站使用 javascript 通过在线发布数据PHP文件
  • 如何使用Peewee查询多个相似的数据库?

    我遇到了使用 Peewee 查询多个数据库的问题 我有 2 个现有的 mysql 数据库 让我们将它们命名为 A 和 B 结构相似 因为它是两个 Bugzilla 数据库 我使用 Pwiz 生成模型 modelsA py 和 modelsB
  • MySQL ALTER TABLE 挂起

    我知道这个问题已经被问过好几次了 但我的问题发生在我刚刚创建的表上 它只有 10 列和 1 行 因此 与通常的挂起问题不同 这不是具有大量数据的大表的情况 但它仍然挂着 这是我正在运行的 SQL ALTER TABLE db Search
  • Mysql:计算访问频率

    我有这张桌子 CREATE OR REPLACE TABLE hits ip bigint page VARCHAR 256 agent VARCHAR 1000 date datetime 我想计算每个页面的 googlebot 访问频率
  • 当我耗尽 bigint 生成的密钥时会发生什么?怎么处理呢?

    我自己无法想象一个好的答案 所以我想在这里问 在我心里 我总是想知道 如果AUTO INCREMENT PRIMARY ID我的专栏MySQL表用完了吗 举例来说 我有一个有两列的表 一个ID auto increment primary
  • 解析错误:语法错误,意外的“”(T_ENCAPSED_AND_WHITESPACE)[重复]

    这个问题在这里已经有答案了 完整错误 解析错误 语法错误 意外的 T ENCAPSED AND WHITESPACE 需要标识符 T STRING 或 变量 T VARIABLE 或数字 T NUM STRING 它说错误出现在第 12 行
  • 错误代码:1822(当数据类型匹配且具有复合键时)

    得到一个 错误代码 1822 添加外键约束失败 丢失的 引用表中约束 subject ibfk 1 的索引 注册 当尝试创建subject桌子 问题是 错误并没有出现在上一张表上student 数据类型相同 并且定义了主键 两者都会出现此错
  • Navicat utf8 不适用于 mysql 数据库

    我目前正在尝试合并两个不同步的表达式引擎数据库之间的数据更改 为此我选择使用 navicat 该网站在模板中大量使用希腊字符集 当我在 phpmyadmin 中查看希腊语表字段数据时 我按预期看到了希腊语字符 当我将它们加载到 Navica
  • MySQL - 替换列中的字符

    作为一个自学成才的新手 我给自己制造了一个大问题 在将数据插入数据库之前 我将字符串中的撇号 转换为双引号 而不是 MySQL 实际需要的反斜杠和撇号 在我的表增长到超过 200 000 行之前 我认为最好立即纠正此问题 所以我做了一些研究
  • PHP 数据库显示在具有不同锚标记的相同字段中

    我四处寻找 看看这是否可行 但却空手而归 首先 这是我的代码 div style display none div ul li li li li li li ul
  • 捕获动态表中 HTML 元素的值

    我有从数据库生成的以下动态表
  • ORDER BY id 或 date_created 显示最新结果?

    我有一个表 实际上有几个 我想首先从中获取最新条目的结果 这是我的ORDER BY条款选项 date created INT 从不改变值 id 当然是INT AUTO INCRMENT 两列应同等地代表记录插入的顺序 我自然会使用date
  • 从 datagridview 选定的行更新 mysql 数据库

    我有一个 datagridview 它在表单加载事件上加载 mysql 数据库表 t pi clients 并且我有另一个选项卡 其中包含 t pi client 相应列的文本框 它能够从 fullrowselect 模式获取数据到这些文本
  • 子查询与连接

    我重构了从另一家公司继承的应用程序的一个缓慢部分 以使用内部联接而不是子查询 例如 WHERE id IN SELECT id FROM 重构后的查询运行速度提高了约 100 倍 50 秒到 0 3 我预计会有改进 但谁能解释为什么它如此剧
  • mysql计算唯一行值

    TABLE quotation id clientid 1 25 2 25 3 25 4 25 5 26 如何查询有多少个不同的客户端TABLE quotation 我不希望重复的条目被计算多次 我需要的答案是2 在 1 行中 因为唯一的非
  • MySQL:查询中周数的周日期范围

    我有一个看起来像这样的数据库表 id clock info 1 1262556754 some info 2 1262556230 some other info 3 1262556988 and another 4 1262555678
  • 优化mysql中日期类型字段的查询

    我目前准备了以下查询 select sum amount as total from incomes where YEAR date 2019 and MONTH date 07 and incomes deleted at is null

随机推荐