众所周知,在mysql里的后通配符可以使用索引查找,前通配查询却无法使用到索引,即使是使用到了索引,也是使用了索引全扫描,效率依然不高,再MySQL5.7之前,一直都没有好的办法解决,但是到了MySQL5.7,自从有了虚拟列,这个问题就好办多了,能够已空间换时间。

  创建测试表

root@localhost [zeno]>show create table test_user\G ;
*************************** 1. row ***************************
Table: test_user
Create Table: CREATE TABLE `test_user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`add_time` datetime DEFAULT NULL,
PRIMARY KEY (`uid`),
KEY `ix_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=6037060 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) ERROR:
No query specified

  使用python插入测试数据

#!/usr/bin/python
import string
import random
import MySQLdb
import time conn = MySQLdb.connect(host='IPAddr',
port=3306,
user='zeno',
passwd='zeno',
db='zeno') def insert(para):
i = 11
while True:
r_name = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(10, 30)))
print r_name cursor = conn.cursor()
cursor.execute("INSERT INTO test_user (name,add_time) VALUES ('%s', now())" % str(r_name))
i = i + 1
conn.commit()
#time.sleep(0.1)
print i insert(conn)

  查看插入的数据量

root@localhost [zeno]>show table status like 'test_user'\G ;
*************************** 1. row ***************************
Name: test_user
Engine: InnoDB
Version: 10
Row_format: Dynamic
Rows: 6002441
Avg_row_length: 51
Data_length: 310165504
Max_data_length: 0
Index_length: 0
Data_free: 5242880
Auto_increment: 6037060
Create_time: 2017-11-23 16:25:15
Update_time: 2017-11-23 16:23:29
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec) ERROR:
No query specified root@localhost [zeno]>select * from test_user limit 10 ;
+-----+-------------------------------+---------------------+
| uid | name | add_time |
+-----+-------------------------------+---------------------+
| 1 | U0WUJ3JJ81IRP27BSA4471 | 2017-11-23 15:37:49 |
| 2 | SOLYNM9Q9A5Y94YG | 2017-11-23 15:37:49 |
| 3 | ONNU5PPKXC3GBR | 2017-11-23 15:37:49 |
| 4 | WVC6GOJ29C | 2017-11-23 15:37:49 |
| 5 | Z653X99ZZI | 2017-11-23 15:37:49 |
| 6 | YP92P02DIKQ8O66K | 2017-11-23 15:37:49 |
| 7 | 2X3G6H8849SDP | 2017-11-23 15:37:49 |
| 8 | 9N9F668XQMTRQSCNE0FWJBMMJEFC0 | 2017-11-23 15:37:50 |
| 9 | 15XAHWZ1IJBP6P4EKCH | 2017-11-23 15:37:50 |
| 10 | VHQJQGQC7U | 2017-11-23 15:37:50 |
+-----+-------------------------------+---------------------+
10 rows in set (0.00 sec)

  开始测试

  一、验证查询条件中使用后通配符的情况

root@localhost [zeno]>select * from test_user where name like '9N9F668XQ%' ;
+-----+-------------------------------+---------------------+
| uid | name | add_time |
+-----+-------------------------------+---------------------+
| 8 | 9N9F668XQMTRQSCNE0FWJBMMJEFC0 | 2017-11-23 15:37:50 |
+-----+-------------------------------+---------------------+
1 row in set (0.00 sec) root@localhost [zeno]>explain select * from test_user where name like '9N9F668XQ%' ;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | test_user | NULL | range | ix_name | ix_name | 99 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

  600W的数据,执行时间0.00sec,已经是毫秒级查询了

  从执行计划中可以看出,type=range, key = 'ix_name',证明是对索引ix_name进行了范围查找,所以,能很快地得到结果

  二、验证查询条件中使用前通配符的情况

root@localhost [zeno]>select * from test_user where name like '%WJBMMJEFC0' ;
+-----+-------------------------------+---------------------+
| uid | name | add_time |
+-----+-------------------------------+---------------------+
| 8 | 9N9F668XQMTRQSCNE0FWJBMMJEFC0 | 2017-11-23 15:37:50 |
+-----+-------------------------------+---------------------+
1 row in set (3.84 sec) root@localhost [zeno]>explain select * from test_user where name like '%WJBMMJEFC0' ;
+----+-------------+-----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test_user | NULL | ALL | NULL | NULL | NULL | NULL | 6002441 | 11.11 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

  600万的数据,运行了3.84sec,速度非常慢

  从执行计划中type=‘ALL’可以看出是进行了全表扫描,扫描完之后,再根据where条件找出合适的数据

  在MySQL5.7之前,对于这种条件中使用了前通配符的查询,几乎就是束手无策,但是,MySQL5.7中增加了一项新功能,可以用较小的代价实现快速查询

  创建虚拟列

root@localhost [zeno]>alter table test_user add r_name varchar(32) generated always as (reverse(`name`));
Query OK, 0 rows affected (0.44 sec)
Records: 0 Duplicates: 0 Warnings: 0

  在虚拟列上创建索引(跟一般创建索引无异)

root@localhost [zeno]>create index ix_r_name on test_user(r_name) ;
Query OK, 0 rows affected (41.90 sec)
Records: 0 Duplicates: 0 Warnings: 0

  问题来了,已经创建了虚拟列,也创建了所以,怎么实现对前通配符的快速查询呢?

  先用一个简短的数字来说明一下思路:假设要查询的列的最终值为‘0123456789’,前通配查询的时候,条件是 name like '%6789',但是已经创建了虚拟列,虚拟列的效果是把原来的数据反转,也就是变成了‘9876543210’,那么,查询的条件变成了name like '9876%',但是,不可能是每次都要自己计算一下,把'6789'换成‘9876’

  因此,在查询的时候,还要取巧的一步,条件中再次把输入的值反转,结果如下

root@localhost [zeno]>select * from test_user where r_name like concat(reverse('WJBMMJEFC0'),'%');
+-----+-------------------------------+---------------------+-------------------------------+
| uid | name | add_time | r_name |
+-----+-------------------------------+---------------------+-------------------------------+
| 8 | 9N9F668XQMTRQSCNE0FWJBMMJEFC0 | 2017-11-23 15:37:50 | 0CFEJMMBJWF0ENCSQRTMQX866F9N9 |
+-----+-------------------------------+---------------------+-------------------------------+
1 row in set (0.00 sec) root@localhost [zeno]>explain select * from test_user where r_name like concat(reverse('WJBMMJEFC0'),'%');
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | test_user | NULL | range | ix_r_name | ix_r_name | 99 | NULL | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

  从执行结果来看,效果已经达到了,600W的数据也只是执行了0.00sec

  三、在条件中同时使用了前通配符和后通配符的情况,暂时没有好的解决办法

参考文档:

  MySQL官方介绍虚拟列:https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html

  以上,如有错谬,请不吝指正。

  原创作品,如需转载,请标明出处,谢谢~

使用mysql5.7新特性解决前通配符查询性能问题的更多相关文章

  1. MySQL5.6 新特性之GTID【转】

    转自 MySQL5.6 新特性之GTID - jyzhou - 博客园http://www.cnblogs.com/zhoujinyi/p/4717951.html 背景: MySQL5.6在5.5的 ...

  2. [MySQL5.6 新特性] 全局事务标示符(GTID)

    GTID的全称为 global transaction identifier  , 可以翻译为全局事务标示符,GTID在原始master上的事务提交时被创建.GTID需要在全局的主-备拓扑结构中保持唯 ...

  3. 使用mysql5.7新特性(虚拟列)解决使用前通配符性能问题

    众所周知,在mysql里的后通配符可以使用索引查找,前通配查询却无法使用到索引,即使是使用到了索引,也是使用了索引全扫描,效率依然不高,再MySQL5.7之前,一直都没有好的办法解决,但是到了MySQ ...

  4. MySQL5.6新特性Index conditontion pushdow

    index condition pushdown是MySQL5.6的新特性,主要是对MySQL索引使用的优化. Index condition push简称ICP,索引条件下推,将索引条件从serve ...

  5. MySQL5.7新特性

    MySQL5.7介绍 身处 MySQL 这个圈子,能够切身地感受到大家对 MySQL 5.7 的期待和热情,似乎每个人都迫不及待的想要了解.学习和使用 MySQL 5.7.那么,我们不禁要问, MyS ...

  6. mysql5.7新特性探究

    一.MySql5.7增加的特性 1.MySql服务方面新特性 1) 初始化方式改变 MySql5.7之前版本初始化方式: scripts/mysql_install_db MySql5.7版本初始化方 ...

  7. 小心SQL SERVER 2014新特性——基数评估引起一些性能问题

    在前阵子写的一篇博文"SQL SERVER 2014 下IF EXITS 居然引起执行计划变更的案例分享"里介绍了数据库从SQL SERVER 2005升级到 SQL SERVER ...

  8. 盘点 Oracle 11g 中新特性带来的10大性能影响

    Oracle的任何一个新版本,总是会带来大量引人瞩目的新特性,但是往往在这些新特性引入之初,首先引起的是一些麻烦,因为对于新技术的不了解.因为对于旧环境的不适应,从Oracle产品到技术服务运维,总是 ...

  9. MySQL5.6 新特性之GTID

    背景: MySQL5.6在5.5的基础上增加了一些改进,本文章先对其中一个一个比较大的改进"GTID"进行说明. 概念: GTID即全局事务ID(global transactio ...

随机推荐

  1. 洛谷 P3267 [JLOI2016/SHOI2016]侦察守卫(树形dp)

    题面 luogu 题解 树形\(dp\) \(f[x][y]表示x的y层以下的所有点都已经覆盖完,还需要覆盖上面的y层的最小代价.\) \(g[x][y]表示x子树中所有点都已经覆盖完,并且x还能向上 ...

  2. bzoj1041 圆上的整点 数学

    题目传送门 题目大意:求一个给定的圆(x^2+y^2=r^2),在圆周上有多少个点的坐标是整数. 思路:没思路,看大佬的博客(转载自https://blog.csdn.net/csyzcyj),转载只 ...

  3. codeforces 703D Mishka and Interesting sum 偶数亦或 离线+前缀树状数组

    题目传送门 题目大意:给出n个数字,m次区间询问,每一次区间询问都是询问 l 到 r 之间出现次数为偶数的数 的亦或和. 思路:偶数个相同数字亦或得到0,奇数个亦或得到本身,那么如果把一段区间暴力亦或 ...

  4. java的Spring学习2- junit和mock

    <!-- 引用Mock --> <dependency> <groupId>org.mockito</groupId> <artifactId&g ...

  5. Djang视图层

    视图 1.什么是视图 视图就是Django项目下的view.py,它的内部是一系列的函数或者是类,用来专门处理客户端访问请求并且返回相应的数据,相当于一个中央处理系统. 2.具体视图实例 3.CBV和 ...

  6. 牌型种数-dfs-蓝桥杯2015

    牌型种数 牌型种数 小明被劫持到 X 赌城,被迫与其他 3 人玩牌. 一副扑克牌(去掉大小王牌,共 52 张),均匀发给 4 个人,每个人 13 张. 这时,小明脑子里突然冒出一个问题: 如果不考虑花 ...

  7. telnet出现Connection closed by foreign host

    2018-10-26 执行命令: telnet smtp.exmail.qq.com 出现信息: [root@pengman Desktop]# telnet Tring Connected to 1 ...

  8. PIE SDK面元素的绘制

    1. 功能简介 在数据的处理中会用到线元素的绘制,目前PIE SDK支持IFillSymbol接口,FillSymbol对象是用于修饰填充面状对象的符号,它包括MarkerFillSymbol(由点状 ...

  9. 利用Flume将本地文件数据中收集到HDFS

    1. 创建文件 放入一个txt文件 然后查看hdfs上的文件夹 不知道为什么并没有出现本地的文件 也不报错 后来发现,没有在logs文件夹下面,在newlogs文件夹下面

  10. Navicat Premium v12.0.23.0 安装,使用激活码激活

    1 下载 可以直接官网下载安装包,也可以直接到我的云盘下载 下载地址:https://pan.baidu.com/s/1apwU9cIKBTr-z0CuJEJ9gg 文件包中包含下面的文件: 2 安装 ...