众所周知,在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. netconf选用秘钥登录

    #! /usr/bin/python2.7import ncclientfrom ncclient import managerwith manager.connect(\ host="19 ...

  2. Unity 使用 陀螺仪 实现 《王者荣耀》 登入界面 背景动态效果

    在 <王者荣耀> 登入界面 左右上下晃动手机(有些手机不支持)可以看到背景在变化 我使用的是iPhone SE 效果如下: 对比两张图片的左下角 可以看到差异 至于为什么要这么做: 1.使 ...

  3. inotify-tools + php脚本实现Linux服务器文件监控并邮件提醒

      需求简介: 由于服务器被挂马,经常被写入涉敏感的html网页,领导时常被网监请去喝茶,呵呵你懂的.所以有两个需求,一是找出服务器的木马后门和修复代码漏洞,二是监控服务器涉及增删改查的文件. 第一个 ...

  4. Java Scanner类

    package io; import java.util.*; public class useScanner { public static void main(String[] args) { S ...

  5. jdbc链接hive报错:java.lang.ClassNotFoundException: org.apache.thrift.transport.TTransport

    写了个jdbc连接hive2的demo,结果报错:java.lang.ClassNotFoundException: org.apache.thrift.transport.TTransport,实际 ...

  6. 在 ASP.NET Core 中使用 SignalR

    https://weblogs.asp.net/ricardoperes/signalr-in-asp-net-core 作者:Ricardo Peres 译者:oopsguy.com 介绍 Sign ...

  7. RestServer 2.0 正式版发布

    RestServer 2.0 正式版发布 使用许可&版权说明 在保持本软件完整的情况下可以将本软件用于任何商业用途. 本软件可以自由传播,但是请保持软件相关文件和说明文档完整. 未经许可不得将 ...

  8. 使用 LitJson 解析Json并读取数据

    开发中经常要获取各种数据,而现今比较常见的数据便是Json数据格式,网上也有很多解析Json数据的方法,但是 作为小白的我,对于那些个高大上的方法理解不够,这不找了许久发了这些一个 LitJson 库 ...

  9. BigDecimal与Long之间的转换

    新建了一个class类 取名叫Firut import java.math.BigDecimal; public class Firut { private String id; private Bi ...

  10. 在找一份相对完整的Webpack项目配置指南么?这里有

    Webpack已经出来很久了,相关的文章也有很多,然而比较完整的例子却不是很多,让很多新手不知如何下脚,下脚了又遍地坑 说实话,官方文档是蛮乱的,而且有些还是错的错的..很多配置问题只有爬过坑才知道 ...