MySQL的前缀索引及Oracle的类似实现
其实,Oracle也有类似的实现,对于文本,它可以通过substr的函数索引,实现同样甚至更多的功能。另外,经过探索,我们发现,原来数字和时间字段,在Oracle也可以实现类似的功能。
MySQL的前缀索引
MySQL的前缀索引指的是对指定的栏位的前面几位建立的索引。
Altertable Table_Name add key(column_name(prefix_len));
或者
Createindex index_name on Table_Name(column_name(prefix_len));
建立前缀索引后,可以直接当做普通索引进行过滤。
Select ..from table_name where column_name=’…’;
前缀索引的最大的好处是降低索引的大小。另外,由于InnoDB单列索引长度不能超过767bytes,如果是text或者blob字段,直接建立索引可能会报错,而前缀索引可以绕过这一限制。
做个测试看一下。
delimiter;;
dropFUNCTION if exists random_str;;
CREATEFUNCTION random_str(n int) RETURNS varchar(30000)
begin
declarereturn_str varchar(30000) default “”;
declare iint default 0;
whilelength(return_str) < n do
setreturn_str=concat(return_str,md5(rand()));
endwhile;
returnsubstring(return_str,1,n);
end;;
首先,创建一个生成超过1000长度的随机字符串的函数。
创建测试表
CREATETABLE TEST_PREFIX_IND (
ID INT(10) PRIMARY KEY AUTO_INCREMENT,
NORMAL_STR VARCHAR(20) ,
LONG_STR VARCHAR(1000),
TEXT_STR TEXT,
BLOB_STR BLOB
);
插入10000行记录:
drop procedure if exists init_test_prefix_ind;;
createprocedure init_test_prefix_ind(n int)
begin
declare iint default 0;
while i< n do
insertinto test_prefix_ind(NORMAL_STR,long_str, TEXT_STR,BLOB_STR)
values(random_str(20),random_str(rand()*1000+1),random_str(rand()*1000+1),random_str(rand()*300+1));
seti=i+1;
endwhile;
end;;
callinit_test_prefix_ind(10000);;
尝试在类型为varchar(1000)的LONG_STR创建索引
altertable test_prefix_ind add key(LONG_STR);;
成功了,但是Sub_part显示为767,表示系统自动创建了前缀长度为767的前缀索引;
看看大小: 8992k
尝试在TEXT和BLOB的栏位上直接创建索引
mysql> alter table test_prefix_ind add key(text_str);
ERROR 1170 (42000): BLOB/TEXT column ‘text_str’ used in key specification without a key length
mysql> alter table test_prefix_ind add key(blob_str);;
ERROR 1170 (42000): BLOB/TEXT column ‘blob_str’ used in key specification without a key length
在TEXT和BLOB栏位上建立索引,必须指定前缀长度。
alter table test_prefix_ind add key(text_str(30));;
看看大小,528k(9520-8992), 远远小于LONG_STR的8992k.
alter table test_prefix_ind add key(blob_str(30));;
看看大小,544k(10064-9520)。
看看几个表的前缀长度和大小。前缀长度显著降低了索引的大小。
看看查询是否能正常进行:
可以使用上索引。
前缀索引长度的选择
对于一个可能挺长的栏位,怎么判断合适的前缀索引呢?
简单做法:
Select count(distinct substr(long_str,1,5))/count(*) from test_prefix_ind;
炫一点的写法,通过一些小技巧,可以在同一个SQL里遍历多个值,同时查看多个值的选择度。
select R,count(distinct substr(long_str,1,R))/count(*)
from
(SELECT @rownum:=ceil(@rownum*1.4) AS R
FROM (SELECT @rownum:=1) r,test_prefix_ind limit 1,10
) R,test_prefix_ind T
group by R;;
对于这个表,由于数据是随机的,所以,前5位已经足够好。
我们创建一个前缀长度为5的前缀索引。
alter table test_prefix_ind add key(long_str(5));
看看大小,仅仅258k(10320-10064),远低于最早创建的8992k。
测试一下性能,有前缀索引时:
删除索引后,性能差距很明显:
Oracle的类似实现
从前面的做法中,我们可以发现,前缀索引本质上就是把栏位的前N位作为索引,这个看起来,很像Oracle的函数索引。类似于:
Create index index_name on table_name(substr(column_name,1,<length>) );
对于Oracle的函数索引,我们一个比较深的印象就是,where条件必须和函数索引里的表达式一致,才能利用上函数索引。但既然MySQL可以用前缀索引,作为老前辈的Oracle, 似乎应该也能实现才对。
我们来看看,在Oracle里面,是否能够实现同样的功能。
创建表格:
Create table test_substr as
select object_id,object_name||dbms_random.string(‘x’,dbms_random.value(1,1000) as object_name,created from all_objects ,
(select * from dual connect by level < 100)
where rownum < 10000;
创建substr的函数索引:
Create index test_substr_inx on test_substr(substr(object_name,1,5));
看看执行计划:
神奇的事情发生了,的确走了索引,Oracle也支持前缀索引~~
我们可以看到,找谓词中,增加了一个原来语句中没有的东西:
换成绑定变量看看:
可以看到,谓词中变成了:
为什么多了这个东西?因为,从逻辑上来说:
select * from test_
和
select * from test_substr where object_name=:a and substr(object_name,1,5)=substr(:a,1,5);
是百分百等价的。Oracle相当于自动做了语义上的优化。
有兴趣的,可以做个10053。Oracle内部实际进行执行计划解析的,就是这样一个SQL。
SELECT * FROM TEST_SUBSTR WHERE OBJECT_NAME=:A AND SUBSTR(OBJECT_NAME,1,5)=SUBSTR(:A,1,5);
看看如果创建普通索引,空间占用是多少。
Create index test_substr_inx2 on test_substr(object_name);
大小分别是7M和256K.
但Oracle仅止于此吗?我们在来试试看另一个SQL, 这次,我们在条件上也使用substr,但是长度不为5。
果然还是可以的。因为逻辑上来说
select * from test_substr
where substr(object_name,1,<N>)=:a;
在N>=5的时候,
select * from test_substr
where substr(object_name,1,<N>)=:a and substr(object_name,1,5)=substr(:a,1,5);
还是等价的。所以优化器还是可以继续加一个谓词。
当然,如果把WHERE条件中substr换成小于5的值,就不再能用得上索引。因为无法直接换为等价的、又带有substr(object_name,1,5)的语句。
Oracle时间、数字上的前缀索引
仅仅就这样吗?除了字符类型之外,数字类型和时间类型是否也支持?
我们再看看。
在刚才的表的基础上,创建时间类型上的trunc函数索引。
Create index test_trunc_date_inx on test_substr(trunc(created));
看看执行计划:
没问题,还是可以的。
创建数字类型上的trunc函数索引:
create index test_trunc_number on TEST_SUBSTR(trunc(object_id));
实际上,问题的关键在于等价与优化器的内部改写。
我们最后再看另一个例子。
建另一个表,相当栏位长度最长为5。
create table test_scale (object_name varchar2(5));
insert into test_scale select substr(object_name,1,5) from all_objects;
create index test_scale_str_inx in test_scale(object_name);
来看看这个语句的执行效果
select * from test_scale where object_name = ‘DBA_TABLES’;
神奇的事情再次发生,autotrace中db block gets/consistent gets都为0,这代表数据库根本就没去访问表。
原因很简单,‘DBA_TABLES’这个值长度大于5, 超出了表定义中的varchar2(5)了。object_name = ‘DBA_TABLES’就等价于恒否的条件了。这个,在10053里也找不到,但的确存在。
原文来自微信公众号:数据和云
MySQL的前缀索引及Oracle的类似实现的更多相关文章
- mysql索引之一:索引基础(B-Tree索引、哈希索引、聚簇索引、全文(Full-text)索引区别)(唯一索引、最左前缀索引、前缀索引、多列索引)
没有索引时mysql是如何查询到数据的 索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储10 ...
- MySQL建立高性能索引策略
索引永远是最好的查询解决方案嘛? 索引并不总是最好的工具.总的来说,只有当索引帮助存储引擎快速查找到记录带来的好处大于其带来的额外工作(比如插入操作后索引的维护)时,索引才是高效的. 对于非常小的表: ...
- mysql使用联合索引提示字符长度超限制解决办法
mysql在创建数据库的时候,字符集设置的不是utf8而是utf9mb4,在导入sql脚本的时候,发现提示如下错误: 从上图中,我们可以看出,使用的是innodb及字符集.错误提示是长度太长了 ...
- 数据库索引(Oracle和Mysql)学习总结
旭日Follow_24 的CSDN 博客 ,全文地址请点击: https://mp.csdn.net/postedit/80910082 索引概念: 索引是关系数据库中用于存放每一条记录的一种 ...
- Mysql中的联合索引、前缀索引、覆盖索引
索引 索引是一种特殊的文件,它们包含着对数据表里所有记录的引用指针.更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度. 联合索引 又名复合索引,由两个或多个列的索引.它规定了mys ...
- MySQL的索引类型和左前缀索引
1.索引类型: 1.1B-tree索引: 注:名叫btree索引,大的方面看,都用的是平衡树,但具体的实现上,各引擎稍有不同,比如,严格的说,NDB引擎,使用的是T-tree,但是在MyISAM,In ...
- MySQL索引之前缀索引和索引选择性
有时需要索引很长的字符列,它会使索引变大而且变慢.一个策略就是模拟哈希索引.但是有时这也不够好,那? 通常可以索引开始的几个字符,而不是全部值,以节约空间并得到好的性能.这使索引需要的空间变小,但是也 ...
- mysql,sql server,oracle 唯一索引字段是否允许出现多个 null 值?
最近一个项目,涉及到sql server 2008,因为业务需求,希望建立一个唯一索引,但是发现在sql server中,唯一索引字段不能出现多个null值,下面是报错信息: CREATE UNIQU ...
- mysql定时脚本(event),类似oracle的job
mysql定时脚本(event),类似oracle的job 我有2张表:tb_push_data 和 tb_push_data_log 现在需要每隔一段时间将tb_push_data 符合条件的 ...
随机推荐
- Nginx的几个常用配置和技巧
文章列举了几个Nginx常见的,实用的,有趣的配置,希望看过之后能说一句:学到了! 一个站点配置多个域名 server { listen 80; server_name ops-coffee.cn b ...
- Nodejs学习笔记(十一)—数据采集器示例(request和cheerio)
写在之前 很多人都有做数据采集的需求,用不同的语言,不同的方式都能实现,我以前也用C#写过,主要还是发送各类请求和正则解析数据比较繁琐些,总体来说没啥不好的,就是效率要差一些, 用nodejs写采集程 ...
- java将list转为树形结构的方法
目录 1.通过转化成json封装数据 2.通过java8 stream转换 1.通过转化成json封装数据 原始数据如下 [ { "name":"甘肃省", & ...
- canvas文字自动换行、圆角矩形画法、生成图片手机长按保存、方形图片变圆形
canvas的文字自动换行函数封装 // str:要绘制的字符串 // canvas:canvas对象 // initX:绘制字符串起始x坐标 // initY:绘制字符串起始y坐标 // lineH ...
- spring boot 2.0 源码分析(四)
在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...
- Django 多表查询
多表查询是模型层的重要功能之一, Django提供了一套基于关联字段独特的解决方案. ForeignKey 来自Django官方文档的模型示例: from django.db import model ...
- [转]File uploads in ASP.NET Core
本文转自:https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads By Steve Smith ASP.NET MVC ...
- .Net调用Java端带有WS-Security支持的Web Service【亲测通过】
做了几年的开发,今天终于鼓起勇气开通了博客园.平时都是找各种大牛,看他们的分享博客的解决BUG.从今天起,我也开始分享我学习之路.还望大家多多支持! 最近收到一个采用Axis2实现的WebServic ...
- 使用T4Scaffolding 创建自己的代码生成
nuget查找引入T4Scaffolding.Core 这个是最低层的,没有其他依赖,当然也没有现成的模板 这个有对EF的依赖,自带了生成DBContext的模板 这个自带了一些mvc的contr ...
- Hibernate下的增删改查
概述: 关系--对象映射的中间件,属于开源ORM框架,是我们业务逻辑层中的调用数据库的中间件 演变: jdbc---hibernater---mybatis hibernate和mybatis区别? ...