MySQL前缀索引

MySQL前缀索引,第1张

前缀索引顾名思义,定义字符串的一部分当做索引,而不是把整个字符串当做索引。默认地,如果你创建索引的语句不指定前缀长度,那么索引就会包含整个字符串。

假设一张表有 id,name,email 2个字段

1.创建email列的普通索引应该是: alter table T add index idx_email1( email )

2.前缀索引的创建规则为: alter table table T add index idx_email2( email(6) )

当然第一索引包含是的整个字符串,第二个是该字段前6个字节(注意是字节)

对于这2中索引,B+树怎么存储呢?

INSERT INTO T (email) VALUES ('瞎子','zhangsh1234@163.com'), ('剑圣','lisi1998883@163.com'), ('露娜','zhangssxyz@163.com'), ('李白','zhangsy1998@163.com'), ('韩信','zhaq5481993@163.com'), ('百里玄策','hhaq5481993@163.com')

【谁还不是个野王啊】

普通索引存储为:

是的你没看错,前缀索引那颗树上的存储的是email的前6位字节,也就是你创建前缀索引时指定的前缀字节长度。2种树相比,前缀索引存储了更少的数据,那么他所耗费的空间也就相比较少,这正是他的一个优点。同样的也就相对的增加了扫描行数。

什么增加了扫描行数???? 这是为什么呢?

那么小朋友咱们一起来看下吧。

假设SQL如此这般: select id,name,email from T where email = 'zhangsh1234@163.com'

那么这2个SQL,应该怎么 *** 作呢。

idx_email1:

2.到主键上查到主键为ID1的,判断email值是否正确【为什么判断呢,其实我理解是为了二次判断保证数据一致性吧,比较官方的解释尚未找到】,正确放入结果集

3.取 idx_email1 索引树上刚刚查到的位置的下一条记录,如此往复。

循环过程中,需要回主键取1次数据,所以系统可以认为只扫描了一行【1次是数第一棵树数出来的】

idx_email2:

1.从 索引数上找到满足索引值为 'zhangs'的该记录,取得 ID1的值

2.到主键上查到主键值是 ID1 的行,判断出 email 的值是’ zhangsh1234@xxx.com ’,这行记录放入结果集【不是要的值,丢弃,进行下一步】

3.取 idx_email2 上刚刚查到的位置的下一条记录,重复以上步骤

在这个过程中,要回主键索引取 3 次数据,也就是扫描了 3 行。通过这个对比,你很容易就可以发现,使用前缀索引后,可能会导致查询语句读数据的次数变多。

但是,对于这个查询语句来说,如果你定义的 idx_email2 不是 email(6) 而是 email(8),也就是说取 email 字段的前 8 个字节来构建索引的话,即满足前缀’zhangsh’的记录只有一个,也能够直接查到 ID1,只扫描一行就结束了。也就是说使用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询成本。

那么问题来了,到底定义多长才算是合理呢?

一般的定义原则是 count(distinct(columnName))/count(*) ,当前缀索引【count(distinct(columnName(length))),length是你想要创建列的前缀字节长度】越接近此值越好,当有多个前缀字节都一样且都等于这个值时怎么选择呢,当然是 字节越少越好了哈,字节越少越省空间。索引选取的越长,占用的磁盘空间就越大,相同的数据页能放下的索引值就越少,搜索的效率也就会越低。

count(distinct(columnName(length))) 翻译到SQL 为: count(dictinct(left(colunmName, length)))

前面我们说了使用前缀索引可能会增加扫描行数,这会影响到性能。其实,前缀索引的影响不止如此,我们再看一下另外一个场景。

来呀,上SQL: select id,email from T where email='zhangsh1234@163.com'

如果按照email全字段索引,那么此SQL 是不需要回表的【为什么不需要回表?兄嘚,这个相当于覆盖索引了哈】

那么如果按照前缀索引是否需要回表呢?答案是的。

因为当判断前6个字节相等后,需要拿到id 回表拿到email的全部内容进行比较,如果不相同,丢弃这行,否则加入结果集。

那么有人会问了,我把长度放大点,包含所有字节不就好了吗?

那么此时会有如下问题。

1.当你此时的长度是囊括了全字段,但是系统是不知道的,他还是需要回表再次判断的,去确定前缀索引的定义是否截断了完整信息。

2.此时长度是够了,那么能肯定因为业务日后不会增加长度吗?

3.尽可能的加长长度,还不如直接建立全字段索引呢

综上,使用前缀索引就用不上覆盖索引对查询性能的优化了,这也是你在选择是否使用前缀索引时需要考虑的一个因素。

前面说到的是,可以根据字段前面几个字节进行查询的,那么对于身份z这种,一共 18 位,其中前 6 位是地址码,所以同一个县的人的身份z号前 6 位一般会是相同的。

或许你会说,多弄几个字节不就好吗?那么请问下自己为什么使用前缀索引呢,不就是为了节省空间吗?

那么这么做合适吗? 不合适对吗? 乖~,快去反省下吧

那么采用前缀索引显示是不行的,那么如果用前缀索引怎么办呢,聪明的你应该已经猜到了,采用倒叙存储,然后建立前缀索引。

放到SQL 中就应该是这样的: select field_list from t where id_card = reverse('id_card_string')

当然了,这种逻辑建议放到业务逻辑中实现,而不是放到SQL 中。

按照上述第4节的内容,有人或许会有另一个想法,还倒叙建立前缀索引复杂不,hash索引或者hash字段不香吗?

有人会问了,为什么要在创建一个值来存储hash值呢,如果不存储你知道原值是什么吗? 同时hash算法是有一定重复可能的(hash值碰撞)

【可以了解下partition算法哦:[ https://selfboot.cn/2016/09/01/lost_partition 】。如果重复了,不存储原值,你是无法判断出正确数据的。

注:【hash字段不代表hash索引,hash索引原理正在快马加鞭】,简单说下hash索引,hash索引不需要创建一个值来存储hash值,而是有hasn表来存储【hash值碰撞时,由一个链表来搞定了】,存储的内容为 hash值和每行的行指针

说回来啊,跑题了

查询时: select field_list from t where id_card_crc=crc32('id_card_string') and id_card='id_card_string'

不过有个问题相信你也想到了,不管是hash存储值还是hash索引都是不支持范围查询的。

来总结下这2个优缺点吧

1.从占用空间来看呢,倒叙索引不需要额外开辟存储空间,而hash字段需要额外的一个字段,所以从这点上看倒叙索引更胜一筹,NO!并不准确,如果前缀长度过长,那么这2个情况额外的空间也就相差无几了

3.从查询效率上看,使用 hash 字段方式的查询性能相对更稳定一些。因为 crc32 算出来的值虽然有冲突的概率,但是概率非常小,可以认为每次查询的平均扫描行数接近 1。而倒序存储方式毕竟还是用的前缀索引的方式,也就是说还是会增加扫描行数

1.全字段完整索引比较占空间,但是而走覆盖索引

2.前缀索引,节省空间,但会增加扫描 次数 并且不能使用覆盖索引【每次都需回表校验】

3.倒序存储,再创建前缀索引,用于绕过字符串本身前缀的区分度不够的问题。【倒叙方法建立放到业务逻辑中】

4.hash字段索引,相比前缀索引性能较为稳定,但是有额外的存储空间和计算消耗,同时也 支持范围查询

添加语法: ALTER TABLE table_name ADD KEY(column_name(prefix_length))

在MySQL中,前缀长度最大值为255字节。对于存储引擎为MyISAM或InnoDB的数据表,前缀最长为1000字节。 必须注意的是,在MySQL中,对于TEXT和BLOB这种大数据类型的字段,必须给出前缀长度(length)才能成功创建索引。

如何确定前缀索引长度?

可以通过计算选择性来确定前缀索引的选择性,计算方法如下

全列选择性:

SELECT COUNT(DISTINCT column_name) / COUNT(*) FROM table_name

某一长度前缀的选择性

SELECT COUNT(DISTINCT LEFT(column_name, prefix_length)) / COUNT(*) FROM table_name

当前缀的选择性越接近全列选择性的时候,索引效果越好。

参考文章:

MySQL索引 *** 作命令详解

MySQL 前缀索引

一 ROWID的概念

存储了row在数据文件中的具 *** 置 位编码的数据 A Z a z + 和 /

row在数据块中的存储方式

SELECT ROWID last_name FROM hr employees WHERE department_id =

比如 OOOOOOFFFBBBBBBRRR

OOOOOO data object number 对应dba_objects data_object_id

FFF file# 对应v$datafile file#

BBBBBB block#

RRR row#

Dbms_rowid包

SELECT dbms_rowid rowid_block_number( AAAGFqAABAAAIWEAAA ) from dual

具体到特定的物理文件

二 索引的概念

类似书的目录结构

Oracle 的 索引 对象 与表关联的可选对象 提高SQL查询语句的速度

索引直接指向包含所查询值的行的位置 减少磁盘I/O

与所索引的表是相互独立的物理结构

Oracle 自动使用并维护索引 插入 删除 更新表后 自动更新索引

语法 CREATE INDEX index ON table (column[ column] )

B tree结构(非bitmap)

[一]了解索引的工作原理

表 emp

目标 查询Frank的工资salary

建立索引 create index emp_name_idx on emp(name)

 [试验]测试索引的作用

运行/rdbms/admin/utlxplan 脚本

建立测试表

create table t as select * from dba_objects

insert into t select * from t

create table indextable

as select rownum id owner object_name subobject_name

object_id data_object_id object_type created

from t

set autotrace trace explain

set timing on

分析表 可以得到cost

查询 object_name= DBA_INDEXES

在object_name列上建立索引

再查询

[思考]索引的代价

插入 更新

 三 唯一索引

何时创建 当某列任意两行的值都不相同

当建立Primary Key(主键)或者Unique constraint(唯一约束)时 唯一索引将被自动建立

语法 CREATE UNIQUE INDEX index ON table (column)

演示

四 组合索引

何时创建 当两个或多个列经常一起出现在where条件中时 则在这些列上同时创建组合索引

组合索引中列的顺序是任意的 也无需相邻 但是建议将最频繁访问的列放在列表的最前面

演示(组合列 单独列)

 五 位图索引

何时创建

列中有非常多的重复的值时候 例如某列保存了 性别 信息

Where 条件中包含了很多OR *** 作符

较少的update *** 作 因为要相应的跟新所有的bitmap

结构 位图索引使用位图作为键值 对于表中的每一数据行位图包含了TRUE( ) FALSE( ) 或NULL值

优点 位图以一种压缩格式存放 因此占用的磁盘空间比标准索引要小得多

语法 CREATE BITMAP INDEX index ON table (column[ column] )

掩饰

create table bitmaptable as select * from indextable where owner in( SYS PUBLIC )

分析 查找 建立索引 查找

 六 基于函数的索引

何时创建 在WHERE条件语句中包含函数或者表达式时

函数包括 算数表达式 PL/SQL函数 程序包函数 SQL函数 用户自定义函数

语法 CREATE INDEX index ON table (FUNCTION(column))

演示

必须要分析表 并且query_rewrite_enabled=TRUE

或者使用提示/*+ INDEX(ic_index)*/

七 反向键索引

目的 比如索引值是一个自动增长的列

多个用户对集中在少数块上的索引行进行修改 容易引起资源的争用 比如对数据块的等待 此时建立反向索引

性能问题

语法

重建为标准索引 反之不行

 八 键压缩索引

比如表landscp的数据如下

site feature job

Britten Park Rose Bed Prune

Britten Park Rose Bed Mulch

Britten Park Rose Bed Spray

Britten Park Shrub Bed Mulch

Britten Park Shrub Bed Weed

Britten Park Shrub Bed Hoe

……

查询时 以上 列均在where条件中同时出现 所以建立基于以上 列的组合索引 但是发现重复值很多 所以考虑压缩特性

Create index zip_idx

on landscp(site feature job)

press

将索引项分成前缀(prefix)和后缀(postfix)两部分 前两项被放置到前缀部分

Prefix : Britten Park Rose Bed

Prefix : Britten Park Shrub Bed

实际所以的结构为

Prune

Mulch

Spray

Mulch

Weed

Hoe

特点 组合索引的前缀部分具有非选择性时 考虑使用压缩 减少I/O 增加性能

九 索引组织表(IOT)

将表中的数据按照索引的结构存储在索引中 提高查询速度

牺牲插入更新的性能 换取查询性能 通常用于数据仓库 提供大量的查询 极少的插入修改工作

必须指定主键 插入数据时 会根据主键列进行B树索引排序 写入磁盘

 十 分区索引

簇:

A cluster is a group of tables that share the same data blocks because they share mon columns and are often used together

lishixinzhi/Article/program/Oracle/201311/17769


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/6652347.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-03-26
下一篇 2023-03-26

发表评论

登录后才能评论

评论列表(0条)

保存