众所周知,在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. Oracle 11g新特性虚拟列分区

    如今有个需求:一个单据表要依照月份来分区.假设是在Oracle 10g上,仅仅能再加一个字段. 在Oracle 11g以后就不一样了.能够用虚拟列处理. SQL> select * from v ...

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

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

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

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

  4. SQL Server 2016新特性:列存储索引新特性

    SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...

  5. 使用mysql5.7新特性解决前通配符查询性能问题

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

  6. MySQL5.6新特性Index conditontion pushdow

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

  7. MySQL5.7新特性

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

  8. mysql5.7新特性探究

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

  9. MySQL5.6 新特性之GTID

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

随机推荐

  1. iOS10适配相关

    2016年9月7日,苹果发布iOS 10.2016年9月14日,全新的操作系统iOS 10将正式上线. 作为开发者,如何适配iOS10呢? 1.Notification(通知) 自从Notificat ...

  2. 人工智能 tensorflow框架-->简介及安装01

    简介:Tensorflow是google于2015年11月开源的第二代机器学习框架. Tensorflow名字理解:图形边中流动的数据叫张量(Tensor),因此叫Tensorflow 既 张量流动 ...

  3. win10 uwp DataContext

    本文告诉大家DataContext的多种绑法. 适合于WPF的绑定和UWP的绑定. 我告诉大家很多个方法,所有的方法都有自己的优点和缺点,可以依靠自己喜欢的用法使用.当然,可以在新手面前秀下,一个页面 ...

  4. win10 uwp 按下等待按钮

    我们经常需要一个按钮,在按下时,后台执行Task,这时不能再次按下按钮. 我们使用自定义控件,首先新建一个类,我把它命名是ProgressButton 一个进度条按钮,也就是我们按下时发生进度条,完成 ...

  5. Python学习笔记(六)

    Python学习笔记(六) Ubuntu重置root密码 Ubuntu 16.4 目录结构 Ubuntu 命令讲解 1. Ubuntu重置root密码 启动系统,显示GRUB选择菜单(如果默认系统启动 ...

  6. uva242,Stamps and Envelope Size

    这题紫薯上翻译错了 应该是:如果有多个并列,输出邮票种类最少的那个,如果还有并列,输出最大面值最小的那个 坑了我一个下午 dp[p][q]==1表示可以用不超过q张组成面额p 结合记忆化,p从1开始枚 ...

  7. admin的基础配置

    admin自定义配置 一.admin.py 我们知道在models.py文件中创建的数据表,一方面我们可以通过视图函数对其进行增删改查,一方面我们也可以通过admin进行,通常我们是通过admin的前 ...

  8. 假如时光倒流,我会这么学习Java

    回头看看, 我进入Java 领域已经快15个年头了, 虽然学的也一般, 但是分享下我的心得,估计也能帮大家少走点弯路. [入门] 我在2001年之前是C/C++阵营, 有C和面向对象的基础, 后来转到 ...

  9. Java的绝对路径和相对路径

    Java的绝对路径和相对路径 参考:http://blog.csdn.net/u011225629/article/details/46872775 1. 绝对路径 以根部件开始的路径是绝对路径,比如 ...

  10. Django Web框架入门

    一.Django介绍 Django是一个开放源代码的Web应用框架,由Python写成,采用了MVC的框架模式.Django的主要目的是简便.快速的开发数据库驱动的网站.它强调代码复用,多个组件可以很 ...