众所周知,在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. 洛谷P2501 bzoj1049 [HAOI2006]数字序列

    题目链接 bzoj 洛谷 题解 第一问: 假如 \(i < j\) 如果 \(j\)能从\(i\)转移过来 显然中间空隙必须足够 例如:\(50\) \(53\) \(53\) \(52\) 就 ...

  2. 启用和禁用TCPIP上的Netbios

    '设置传输值1是启用,设置2为禁用 On Error Resume Next strComputer = "." Set objWMIService = GetObject(&qu ...

  3. 文件对象FSO应用 文件对象FSO应用

    FileSystemObject对象被用来访问服务器上的文件系统.这个对象能够处理文件.文件夹和目录路径.用它来检索文件系统信息也是可能的. 下面的代码创建了一个文本文件,并写入了一些文本: dim ...

  4. hdu-1022-栈

    Train Problem I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  5. 2018最新mfc作为上位机接收硬件端USB或串口数据显示成图片 解决串口接收数据丢字节丢包问题

    原文作者:aircraft 原文地址:https://www.cnblogs.com/DOMLX/p/9490616.html 本文用的是VS2013MFC写串口数据接收: 第一步:首先建立一个MFC ...

  6. [Matlab] awgn

    Y = awgn(X,SNR,SIGPOWER) when SIGPOWER is numeric, it represents the signal power in dBW. When SIGPO ...

  7. 配置phpstudy+phpstorm+xdebug环境

    phpstorm版本: phpstudy版本: 具体的debug配置流程其实phpstorm里面已经明确的说过了,就是下图所示 所以 第一步: 首先要在phpstudy中开启xdebug扩展,所以要修 ...

  8. 【读书笔记】C#高级编程(一).NET体系结构

    写在前面:从业两年来,一直停留在会用的阶段,而没有去仔细思考过为什么这么用,之前也大致扫过<c#高级编程>一书,这次想借一袭脑海中的冲动,再次好好仔细过过这本书,夯实基础,温故知新. 一. ...

  9. c#学习基础(2)存储、值类型和引用类型、变量

    程序运行时,它的数据必须存储在内存中,数据项需要多大的内存.存储在什么地方以及如何存储都依赖该数据项的类型 运行中的程序使用两个区域来存储数据:栈和堆 栈是一个内存数组,是一个LIFO(last in ...

  10. 2017年11月4日 vs类和结构的区别&哈希表&队列集合&栈集合&函数

    类和结构的区别 类: 类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段实际对象分配的内存 类有构造和析构函数 类可以继承和被继承 结构: 结构是值类型在栈上分配(虽然栈的访问速度比 ...