Mysql全文索引性能测试对比
在关系型数据库中,使用like模糊查询,如果是 'xxx%'头匹配方式,字段索引不会失效,性能能接受;但如果是 '%xxx%'全文匹配方式,索引会失效,在数据量大的情况下,通过此种方式查询的效率极低。此时可通过全文索引(Full-Text Search)来进行。
Mysql在5.6之后提供全文索引,本文主要记录测试全文索引效率。
Mysql全文索引基本情况
1、Mysql在5.6之后基于MyISAM引擎提供全文索引,在MySQL 5.7.6之前,全文索引只支持英文全文索引,不支持中文全文索引
2、Mysql5.7开始 innoDb开始支持全文索引,且从MySQL 5.7.6开始,MySQL内置了ngram全文解析器,用来支持中文、日文、韩文分词。
测试全文索引效率
测试前说明
1、为了对比,本次测试用了10万、100万、1000万数量级做对比测试。
2、测试数据库在服务器(ctrix虚拟机2vcpu,8G内存),本次使用版本5.7.13;客户端在PC上用navicat工具;网络千M网。
3、测试是基于我使用的硬件情况,因此反映性能的具体指标供参考。
4、预先准备好数据,我是采用了《简爱》全本中英文,采用随机截断不同长度文本方式分别从里面收取10万、100万、1000万数据。
5、my.cnf配置(配置完了别忘了重启mysql)如下:
[mysqld]
#英文分词长度默认为4,<4通常不建索引;英文单词长度<4的通常查询不到
ft_min_word_len = 4 #中文分词长度位2,每个字都可以查出
ngram_token_size=2
测试步骤
建表(见如下语句)
CREATE TABLE ft_bigdata10 (id int(11) NOT NULL AUTO_INCREMENT,tenant_id char(6) NOT NULL ,code_list Text DEFAULT NULL, -- 非英文单词的字符串,用','隔开,每个串为单词+','隔开,每个单词16个字符随机组成en_cnword TEXT DEFAULT NULL, -- 中英文文段落(从简爱 中英文对照版随机抽取)PRIMARY KEY (id) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ;
CREATE TABLE ft_bigdata100 (id int(11) NOT NULL AUTO_INCREMENT,tenant_id char(6) NOT NULL ,code_list Text DEFAULT NULL, -- 非英文单词的字符串,用','隔开,每个串为单词+','隔开,每个单词16个字符随机组成en_cnword TEXT DEFAULT NULL, -- 中英文文段落(从简爱 中英文对照版随机抽取)PRIMARY KEY (id) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ;
CREATE TABLE ft_bigdata1000 (id int(11) NOT NULL AUTO_INCREMENT,tenant_id char(6) NOT NULL ,code_list Text DEFAULT NULL, -- 非英文单词的字符串,用','隔开,每个串为单词+','隔开,每个单词16个字符随机组成en_cnword TEXT DEFAULT NULL, -- 中英文文段落(从简爱 中英文对照版随机抽取)PRIMARY KEY (id) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ;
导入数据
由于数据量大,将数据加载到没有FULLTEXT索引的表中然后创建索引要比将数据加载到具有现有FULLTEXT索引的表中快得多。本次均是在数据导入后再建索引。
需要自己写程序导入。本人用python完成,见如下代码。
主程序:
# -*- coding:utf-8 -*-import random
import stringfrom dbsql import DbSql
cn_en_src=[]def getTenantId():'随机生成6位数字的字符串'return "%06d" % random.randint(100000, 119999)def getCodeStr():# 从a-zA-Z0-9生成指定数量的随机字符:# ran_str = ''.join(random.sample(string.ascii_letters + string.digits, 16))ran_str=''for i in range(1,random.randint(1, 50)):ran_str+=','+''.join(random.sample(string.ascii_letters + string.digits, 16))# print(ran_str)return ran_strdef getEnCnStr():global cn_en_srcif len(cn_en_src)<1:#读取英文或中文源with open('jianai.txt', 'r', encoding='utf-8') as f:flines=f.readlines()for line in flines: #去掉短的行line=str(line)if(len(line)>=5):cn_en_src.append(line.replace('\'','')) #去掉 '# print(len(cn_en_src))currLine=random.randint(0,len(cn_en_src)-1)colBegin=random.randint(1,int(len(cn_en_src[currLine])/2)-1)colEnd=random.randint(1,len(cn_en_src[currLine])-colBegin)enCnStr=str(cn_en_src[currLine][colBegin:(colBegin+colEnd)])return enCnStrdef makeRow(tableName,rows):rowData=[]dbSql=DbSql()insertHead='insert into '+tableName+' (tenant_id,code_list,en_cnword) values'for row in range(1,rows+1):print('生成行:'+str(row))rd="('"+getTenantId()+"','"+getCodeStr()+"','"+getEnCnStr()+"')"rowData.append(rd)if(len(rowData)>=10000):dbSql.batchInsert(insertHead,rowData)rowData.clear()#最后的数据插入if(len(rowData)>=1):dbSql.batchInsert(insertHead,rowData)def makeLoginData(rows):'''生成10000条三张表的数据INSERT INTO gxycsy_resource.multi_tenant_account (account_code, status, tenant_id) VALUES ('r_accountno%', '1', 'r_tenantid%');INSERT INTO gxycsy.mtsm_user (id, email, name, phone, sex, status, user_code, username, tenant_id, source, paper_no, photo, agent_id, create_user_id) VALUES ('r_userid%', 'r_accountno%@qq.com', '用户r_accountno%', NULL, '1', '1', NULL, 'r_accountno%', 'r_tenantid%', '0', NULL, NULL, NULL, NULL);INSERT INTO gxycsy.mtsm_account (id, account_code, code_account_type, credentials_non_expired, enabled, non_expired, non_locked, password, user_id, tenant_id, del_status, change_time) VALUES ('r_accountId', 'r_accountno%', '1', '', '', '', '', '$2a$04$bIRQFnqrz9wL19NfrtP9j.oao380cpxGIguN2zgQtIzLz6Uow5e.6', 'r_userid%', 'r_tenantid%', '\0', NULL);'''sqlMulti="INSERT INTO gxycsy_resource.multi_tenant_account (account_code, status, tenant_id) VALUES "sqlMultiPattern=" ('r_accountno%', '1', 'r_tenantid%')"sqlUser="INSERT INTO gxycsy.mtsm_user (id, email, name, phone, sex, status, user_code, username, tenant_id, source, paper_no, photo, agent_id, create_user_id) VALUES "sqlUsePattern=" ('r_userid%', 'r_accountno%@qq.com', '用户r_accountno%', NULL, '1', '1', NULL, 'r_accountno%', 'r_tenantid%', '0', NULL, NULL, NULL, NULL)"sqlAccount="INSERT INTO gxycsy.mtsm_account (id, account_code, code_account_type, credentials_non_expired, enabled, non_expired, non_locked, password, user_id, tenant_id, del_status, change_time) VALUES "sqlAccountPattern=" ('r_accountId%', 'r_accountno%', '1', 1, 1, 1, 1, '$2a$04$bIRQFnqrz9wL19NfrtP9j.oao380cpxGIguN2zgQtIzLz6Uow5e.6', 'r_userid%', 'r_tenantid%', 0, NULL)"gapChar=""testCsv=[] #测试数据文件for i in range(1,rows+1):tenantId="%06d" % random.randint(100000, 106666)accountno="138%06d" % random.randint(10000000, 99999999)userId="Testuse%06d" % iaccountId="testaccount%06d" % isqlMulti+=gapChar+sqlMultiPattern.replace("r_accountno%",accountno).replace("r_tenantid%",tenantId)sqlUser+=gapChar+sqlUsePattern.replace("r_accountno%",accountno).replace("r_tenantid%",tenantId).replace("r_userid%",userId)sqlAccount+=gapChar+sqlAccountPattern.replace("r_accountno%",accountno).replace("r_tenantid%",tenantId).replace("r_userid%",userId).replace("r_accountId%",accountId)testCsv.append(accountno+","+str(i))if(gapChar==""):gapChar = ","# print(sqlMulti+";")# print(sqlUser+";")# print(sqlAccount+";")with open('testDataInsert.sql','w') as f:f.write(sqlMulti+";\n")f.write(sqlUser + ";\n")f.write(sqlAccount + ";\n")with open('testDataOfJmeter.csv','w') as f:f.write("account,rowno\n")for tc in testCsv:f.write(tc+"\n")if __name__ == '__main__':# getCodeStr()# print(getTenantId())# for i in range(1,10):ss# print(getEnCnStr())# makeRow('ft_bigdata1000',10000000)makeLoginData(10000)
sqldb.py文件代码:
'''数据库执行'''import pymysqlclass DbSql:def __init__(self):# 打开数据库连接self.con = pymysql.connect(host="192.168.1.231",port=3306,user="root",password="pwd",database="test_bg",cursorclass=pymysql.cursors.DictCursor)# 使用 cursor() 方法创建一个游标对象 cursorself.cursor = self.con.cursor()def __del__(self):# 关闭数据库连接self.con.close()def startTransaction(self):self.con.begin()def commit(self):self.con.commit()def rollback(self):self.con.commit()def query(self,sql):'''sql查询,以json形式返回数据'''# 使用 execute() 方法执行 SQL 查询self.cursor.execute(sql)datas = self.cursor.fetchall()return datasdef execSql(self,sql,autoTransaction=True):'''执行sql语句'''self.con.autocommit(autoTransaction)self.cursor.execute(sql)def batchInsert(self,insertSqlHead,valueList):'''执行sql语句参数:insertSqlHead格式为'insert into table_name(col1,col2,...) values “valueList:每条数据一行数据(同insertSqlHead格式为对应),每行格式为”(co1value,col2value,...)'''if(len(valueList)<1):returnsql=insertSqlHead+' 'gapSignal=''# print(len(valueList))for value in valueList:sql += gapSignal+valueif(gapSignal == ''): #第一次后,用','隔开gapSignal=','sql+=';'# print(sql)self.con.autocommit(True)self.cursor.execute(sql)if __name__ == '__main__':dbSql=DbSql()data=dbSql.query("select * from member where id=-1")print("datalen="+str(len(data)))print(data)
测试
1、建立全文索引
1.1 单字段索引(中英文字段分开):
建索引语句:
alter table ft_bigdata10 add fulltext index idx_ft10_cl(code_list);
alter table ft_bigdata10 add fulltext index idx_ft10_en(en_cnword);
alter table ft_bigdata10 add fulltext index idx_ft10_cn(en_cnword) with parser ngram;
10万:116.96s
100万:5966.29s
1000万:-
1.2 双字段索引:
建索引语句:alter table ft_bigdata10 add fulltext index idx_ft10(code_list,en_cnword)
10万: 53.94s
100万:752s
1000万:6960s
注:建索引快慢同CPU和磁盘速度又很大关系
2、表空间大小:
10万:约70M
100万:约600M
1000万:约5.7G
2、单字段索引
3、多字段索引
结论
1、 全文索引相比于like非头匹配模式,性能是指数级的提升。
2、 单字段全文索引比多字段全文索引效率更高,当然空间也占用更大。
本次测试中,还发现了Mysql全文索引的很多注意事项,见:Mysql全文索引使用注意事项
Mysql全文索引性能测试对比相关推荐
- FreeSql与SqlSugar性能测试对比
这篇文章主要是对SqlSugar 做一次简单的性能测试对比.主要针对插入.批量插入.批量更新.读取性能的测试: 测试环境 1..net core 2.2 2.FreeSql 0.3.17 3.sqlS ...
- 三个流行MySQL分支的对比
简介 MySQL是历史上最受欢迎的免费开源程序之一.它是成千上万个网站的数据库骨干,并且可以将它(和Linux)作为过去10年里Internet呈指数级增长的一个有力证明. 那么,如果MySQL真的这 ...
- mysql数据库性能测试实例_数据库性能测试方案示例
究竟怎样进行数据库性能测试,数据库性能测试需要做些什么?大多数产品线的RD和QA也比较迷茫,经常过来咨询. 一般说来,做数据库性能测试需要如下几个步骤: 1:明确测试目的 2:设计测试模型 (即压力模 ...
- swoole mysql 协程_Swoole 协程 MySQL 客户端与异步回调 MySQL 客户端的对比
Swoole 协程 MySql 客户端与 异步回调 MySql 客户端的对比 为什么要对比这两种不同模式的客户端? 异步 MySQL 回调客户端是虽然在 Swoole 1.8.6 版本就已经发布了, ...
- InnoDB MySQL 全文索引 学习笔记
最近在学习MySQL全文索引的知识,基本搞清了功能以及使用方式,整理了相关资料分享出来一起学习进步哈 目录 1. 倒排索引 2. InnoDB全文检索 3. MySQL全文检索 3.1 Natural ...
- 多个mysql 环境_关于几个MySQL环境问题的对比
有时候出现了环境问题,对比是一种很好的方式,如果对比得当,可以避免反复的出现问题,可以根据对比的情况推理出一些可能出现的情况或者问题. 如果对比不当,很可能得出错误的结论.今天就简单举几个例子来说明一 ...
- MySQL全文索引应用简明教程
MySQL全文索引应用简明教程 本文从以下几个方面介绍下MySQL全文索引的基础知识: MySQL全文索引的几个注意事项 全文索引的语法 几种搜索类型的简介 几种搜索类型的实例 全文索引的几个注意事项 ...
- mysql全文索引含义_【分针网】MySQL全文索引应用简明教程
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 本文从以下几个方面介绍下MySQL全文索引的基础知识:1.MySQL全文索引的几个注意事项2.全文索引的语法3.几种搜索类型的简介4.几种搜索类型的实例 ...
- mysql全文索引中文问题的解决(转)
MySQL从3.23.23开始就逐渐支持全文索引和搜寻. 全文索引就是建index,全文搜寻就是去查index. LIKE是用Regular Expression去做查询. MySQL全文索引是 ...
最新文章
- 「SAP技术」SAP MM ME2N报表能按‘order acknowledgement‘查询采购订单
- 《Java编程思想》第四版读书笔记 第十四章 类型信息
- CRNN竞赛程序实现过程
- JAVA中String字符串比较equals()和equalsIgnoreCase()的区别
- 前端学习(2709):重读vue电商网站29之左侧菜单栏
- #import和@class 的区别
- zookeeper原理,与集群部署
- unity3d Crease效果分析
- 转载十年 - 武汉公交杂记
- 记一款价廉物美的小型DAC+耳放----Dr.DAC
- Excel如何批量将图片插入到批注中
- 高中数学竞赛与自主招生内部练习试题
- wordpress模板WP主题安装失败的解决办法
- 《富爸爸穷爸爸》读书摘录脑图
- HTTP中Origin和Referer的区别?
- ctfshow SQL注入Web171-174
- matlab做频域,如何使用matlab进行频域分析
- 遨博机器人aubo_robot 包编译问题及解决方法
- 基于野火M3开发板(STM32F103VET6)的迷宫小球(重力感应控制)游戏开发
- python range的逆序输出
热门文章
- 项目一 家庭记账软件
- [zz] 腾讯云 AV1落地
- 抖音成牛年春晚独家合作伙伴;微信8.0安卓/IOS版正式更新;孙正义辞去软银董事长…
- Innocence --Avril Lavigne (艾薇儿#183;拉维尼)
- visio中公式太小_板式换热器选型计算方法及公式
- python+opencv 打开网络摄像头
- 服务器千兆网络显示10,win10系统如何查看网卡是千兆还是百兆
- android后缀.uc 文件,UC头条:华为手机里的英文文件, 到底该不该清理掉? 今天终于能给出答案了...
- SPOT-6卫星数据介绍
- 如沐春光?汽车供应链市场即将迎来曙光