使用mysql5.7新特性(虚拟列)解决使用前通配符性能问题
众所周知,在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新特性(虚拟列)解决使用前通配符性能问题的更多相关文章
- Oracle 11g新特性虚拟列分区
如今有个需求:一个单据表要依照月份来分区.假设是在Oracle 10g上,仅仅能再加一个字段. 在Oracle 11g以后就不一样了.能够用虚拟列处理. SQL> select * from v ...
- MySQL5.6 新特性之GTID【转】
转自 MySQL5.6 新特性之GTID - jyzhou - 博客园http://www.cnblogs.com/zhoujinyi/p/4717951.html 背景: MySQL5.6在5.5的 ...
- [MySQL5.6 新特性] 全局事务标示符(GTID)
GTID的全称为 global transaction identifier , 可以翻译为全局事务标示符,GTID在原始master上的事务提交时被创建.GTID需要在全局的主-备拓扑结构中保持唯 ...
- SQL Server 2016新特性:列存储索引新特性
SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...
- 使用mysql5.7新特性解决前通配符查询性能问题
众所周知,在mysql里的后通配符可以使用索引查找,前通配查询却无法使用到索引,即使是使用到了索引,也是使用了索引全扫描,效率依然不高,再MySQL5.7之前,一直都没有好的办法解决,但是到了MySQ ...
- MySQL5.6新特性Index conditontion pushdow
index condition pushdown是MySQL5.6的新特性,主要是对MySQL索引使用的优化. Index condition push简称ICP,索引条件下推,将索引条件从serve ...
- MySQL5.7新特性
MySQL5.7介绍 身处 MySQL 这个圈子,能够切身地感受到大家对 MySQL 5.7 的期待和热情,似乎每个人都迫不及待的想要了解.学习和使用 MySQL 5.7.那么,我们不禁要问, MyS ...
- mysql5.7新特性探究
一.MySql5.7增加的特性 1.MySql服务方面新特性 1) 初始化方式改变 MySql5.7之前版本初始化方式: scripts/mysql_install_db MySql5.7版本初始化方 ...
- MySQL5.6 新特性之GTID
背景: MySQL5.6在5.5的基础上增加了一些改进,本文章先对其中一个一个比较大的改进"GTID"进行说明. 概念: GTID即全局事务ID(global transactio ...
随机推荐
- netconf选用秘钥登录
#! /usr/bin/python2.7import ncclientfrom ncclient import managerwith manager.connect(\ host="19 ...
- Unity 使用 陀螺仪 实现 《王者荣耀》 登入界面 背景动态效果
在 <王者荣耀> 登入界面 左右上下晃动手机(有些手机不支持)可以看到背景在变化 我使用的是iPhone SE 效果如下: 对比两张图片的左下角 可以看到差异 至于为什么要这么做: 1.使 ...
- inotify-tools + php脚本实现Linux服务器文件监控并邮件提醒
需求简介: 由于服务器被挂马,经常被写入涉敏感的html网页,领导时常被网监请去喝茶,呵呵你懂的.所以有两个需求,一是找出服务器的木马后门和修复代码漏洞,二是监控服务器涉及增删改查的文件. 第一个 ...
- Java Scanner类
package io; import java.util.*; public class useScanner { public static void main(String[] args) { S ...
- jdbc链接hive报错:java.lang.ClassNotFoundException: org.apache.thrift.transport.TTransport
写了个jdbc连接hive2的demo,结果报错:java.lang.ClassNotFoundException: org.apache.thrift.transport.TTransport,实际 ...
- 在 ASP.NET Core 中使用 SignalR
https://weblogs.asp.net/ricardoperes/signalr-in-asp-net-core 作者:Ricardo Peres 译者:oopsguy.com 介绍 Sign ...
- RestServer 2.0 正式版发布
RestServer 2.0 正式版发布 使用许可&版权说明 在保持本软件完整的情况下可以将本软件用于任何商业用途. 本软件可以自由传播,但是请保持软件相关文件和说明文档完整. 未经许可不得将 ...
- 使用 LitJson 解析Json并读取数据
开发中经常要获取各种数据,而现今比较常见的数据便是Json数据格式,网上也有很多解析Json数据的方法,但是 作为小白的我,对于那些个高大上的方法理解不够,这不找了许久发了这些一个 LitJson 库 ...
- BigDecimal与Long之间的转换
新建了一个class类 取名叫Firut import java.math.BigDecimal; public class Firut { private String id; private Bi ...
- 在找一份相对完整的Webpack项目配置指南么?这里有
Webpack已经出来很久了,相关的文章也有很多,然而比较完整的例子却不是很多,让很多新手不知如何下脚,下脚了又遍地坑 说实话,官方文档是蛮乱的,而且有些还是错的错的..很多配置问题只有爬过坑才知道 ...