第八章,索引的创建与设计原则(4)

简介: 第八章,索引的创建与设计原则

第八章,索引的创建与设计原则(3)https://developer.aliyun.com/article/1530679

我们可以通过截取字段的前面一部分内容建立索引,这个就叫前缀索引。这样在查找记录时虽然不能精确的定位到记录的位置,但是能定位到相应前缀所在的位置,然后根据前缀相同的记录的主键值回表查询完整的字符串值。既节约空间,又减少了字符串的比较时间,还大体能解决排序的问题。

例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间,如果只检索字段前面的若干字符,这样可以提高检索速度。

创建一张商户表,因为地址字段比较长,在地址字段上建立前缀索引

创建一张商户表,因为地址字段比较长,在地址字段上建立前缀索引

create table shop(address varchar(120) not null);
alter table shop add index(address(12));

但是问题是截取多少?截得多占用高存储空间,截得少,重复内容过多,字段的散列度(选择性)会降低怎么计算不同长度的选择性呢?

先看一下字段在全部数据中的选择度:

select count(distinct address) / count(*) from shop;

通过不同长度去计算,与全表的选择性对比:

count(distinct left(列名, 索引长度))/count(*)

例如:

select count(distinct left(address,10)) / count(*) as sub10, -- 截取前10个字符的选择度
count(distinct left(address,15)) / count(*) as sub11, -- 截取前15个字符的选择度
count(distinct left(address,20)) / count(*) as sub12, -- 截取前20个字符的选择度
count(distinct left(address,25)) / count(*) as sub13 -- 截取前25个字符的选择度
from shop;

引申另一个问题:索引列前缀对排序的影响

如果使用了索引列前缀,比方说前边只把address 列的前12个字符放到了二级索引中,下面的查询就会很尴尬

SELECT * FROM shop
ORDER BY address 
LIMIT 12; 

因为二级索引中不包含完整的address列信息,所以无法对12个字符相同,后面的字符不同的记录进行排序,也就是使用索引列前缀的方式无法支持索引排序,只能使用文件排序.

引申另一个问题:索引列前缀对排序的影响

拓展:Alibaba《Java开发手册》

【 强制 】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本

区分度决定索引长度。

说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会 高达

90% 以上 ,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。

9. 区分度高(散列性高)的列适合作为索引

列的基数指的是某一列中不重复数据的个数,比方说某个列包含值2,5,8,2,5,8,2,5,8,虽然有9条记录,但该列的基数却是3。也就是说,在记录行数一定的情况下,列的基数越大,该列中的值越分散;列的基数越小,该列中的值越集中。*这个列的基数指标非常重要,直接影响我们是否能有效的利用索引。最好为列的基数大的列建立索引,为基数太小列的建立索引效果可能不好。

可以使用公式 select count(distinct a)/count(*) from t1计算区分度,越接近1越好,一般超过33%就算是比较高效的索引了。

拓展:联合索引把区分度高(散列性高)的列放在前面。

  1. 使用最频繁的列放到联合索引的左侧
    这样也可以较少的建立一些索引。同时,由于"最左前缀原则",可以增加联合索引的使用率。
  2. 在多个字段都要创建索引的情况下,联合索引优于单值索引

3.3 限制索引的数目

在实际工作中,我们也需要注意平衡,索引的数目不是越多越好。我们需要限制每张表上的索引数量,建议单张表索引数量不超过6个。原因:

①每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。

②索引会影响工INSERT、DELETE、UPDATE等语句的性能,因为表中的数据更改的同时,索引也会进行调整和更新,会造成负担。|

③优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个索引都可以用于查询,会增加MySQL优化器生成执行计划时间,降低查询性能。

3.4 哪些情况不适合创建索引

  1. 在where中使用不到的字段,不要设置索引
    WHERE条件(包括GROUP BY、ORDER BY)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的
  2. 数据量小的表最好不要使用索引比如少于1000条
  3. 有大量重复数据的列上不要建立索引
    举例1:要在 100 万行数据中查找其中的 50 万行(比如性别为男的数据),一旦创建了索引,你需要先
    访问 50 万次索引,然后再访问 50 万次数据表,这样加起来的开销比不使用索引可能还要大。
    举例2:假设有一个学生表,学生总数为 100 万人,男性只有 10 个人,也就是占总人口的 10 万分之 1。

学生表 student_gender 结构如下。其中数据表中的 student_gender 字段取值为 0 或 1,0 代表女性,1 代

表男性。

CREATE TABLE student_gender(
student_id INT(11) NOT NULL,
student_name VARCHAR(50) NOT NULL,
student_gender TINYINT(1) NOT NULL,
PRIMARY KEY(student_id)
)ENGINE = INNODB;

如果我们要筛选出这个学生表中的男性,可以使用:

SELECT * FROM student_gender WHERE student_gender = 1

运行结果(10 条数据,运行时间 0.696s ):

结论:当数据重复度大,比如 高于 10% 的时候,也不需要对这个字段使用索引

索引如果查询到大量重复数据还要一个一个去回表,非常影响性能

  1. 避免对经常更新的表创建过多的索引
  2. 不建议用无序的值作为索引
    例如身份证、UUID(在索引比较时需要转为ASCII,并且插入时可能造成页分裂)、MD5、HASH、无序长字
    符串等。
    因为经常要进行页分裂
  3. 删除不再使用或者很少使用的索引

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不在需要,数据库管理员应当定期找出这些索引,将他们删除,从而减少索引对于更新操作的影响

  1. 不要定义冗余或重复的索引
    ① 冗余索引
    举例:建表语句如下
CREATE TABLE person_info(
 id INT UNSIGNED NOT NULL AUTO_INCREMENT,
 name VARCHAR(100) NOT NULL,
 birthday DATE NOT NULL,
 phone_number CHAR(11) NOT NULL,
 country varchar(100) NOT NULL,
  PRIMARY KEY (id),
  KEY idx_name_birthday_phone_number (name(10), birthday, phone_number),
  KEY idx_name (name(10))
);

我们知道,通过 idx_name_birthday_phone_number 索引就可以对 name 列进行快速搜索,再创建一个专门针对 name 列的索引就算是一个冗余索引,维护这个索引只会增加维护的成本,并不会对搜索有

什么好处。

② 重复索引

另一种情况,我们可能会对某个列 重复建立索引 ,比方说这样

CREATE TABLE repeat_index_demo (
 col1 INT PRIMARY KEY,
 col2 INT,
  UNIQUE uk_idx_c1 (col1),
  INDEX idx_c1 (col1)
);

我们看到,col1 既是主键、又给它定义为一个唯一索引,还给它定义了一个普通索引,可是主键本身就

会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况要避免。

符串等。

因为经常要进行页分裂

6. 删除不再使用或者很少使用的索引

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不在需要,数据库管理员应当定期找出这些索引,将他们删除,从而减少索引对于更新操作的影响

  1. 不要定义冗余或重复的索引
    ① 冗余索引
    举例:建表语句如下
CREATE TABLE person_info(
 id INT UNSIGNED NOT NULL AUTO_INCREMENT,
 name VARCHAR(100) NOT NULL,
 birthday DATE NOT NULL,
 phone_number CHAR(11) NOT NULL,
 country varchar(100) NOT NULL,
  PRIMARY KEY (id),
  KEY idx_name_birthday_phone_number (name(10), birthday, phone_number),
  KEY idx_name (name(10))
);

我们知道,通过 idx_name_birthday_phone_number 索引就可以对 name 列进行快速搜索,再创建一个专门针对 name 列的索引就算是一个冗余索引,维护这个索引只会增加维护的成本,并不会对搜索有

什么好处。

② 重复索引

另一种情况,我们可能会对某个列 重复建立索引 ,比方说这样

CREATE TABLE repeat_index_demo (
 col1 INT PRIMARY KEY,
 col2 INT,
  UNIQUE uk_idx_c1 (col1),
  INDEX idx_c1 (col1)
);

我们看到,col1 既是主键、又给它定义为一个唯一索引,还给它定义了一个普通索引,可是主键本身就

会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况要避免。

相关文章
|
6月前
|
存储 关系型数据库 MySQL
第八章,索引的创建与设计原则(2)
第八章,索引的创建与设计原则
25 0
|
6月前
|
存储 自然语言处理 关系型数据库
第八章,索引的创建与设计原则(1)
第八章,索引的创建与设计原则
39 0
|
6月前
|
存储 SQL 缓存
第八章,索引的创建与设计原则(3)
第八章,索引的创建与设计原则
24 0
|
7月前
|
Java C++ Python
面向对象特性分析大全集
面向对象特性分析大全集
|
SQL 存储 关系型数据库
MySQL基础下篇[表的创建/约束的使用/事务和范式以及索引的使用]~1
MySQL基础下篇[表的创建/约束的使用/事务和范式以及索引的使用]~
|
存储 SQL 关系型数据库
MySQL基础下篇[表的创建/约束的使用/事务和范式以及索引的使用]~2
MySQL基础下篇[表的创建/约束的使用/事务和范式以及索引的使用]~2
|
SQL 存储 关系型数据库
MySQL基础下篇[表的创建/约束的使用/事务和范式以及索引的使用]~3
MySQL基础下篇[表的创建/约束的使用/事务和范式以及索引的使用]~
|
存储 SQL 关系型数据库
第8章_索引的创建与设计原则(下)
第8章_索引的创建与设计原则
150 0
|
SQL 存储 关系型数据库
第8章_索引的创建与设计原则(上)
第8章_索引的创建与设计原则
73 0
|
SQL 存储 关系型数据库
索引的创建与设计原则(1)
索引的创建与设计原则(1)
索引的创建与设计原则(1)