第八章,索引的创建与设计原则(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%就算是比较高效的索引了。
拓展:联合索引把区分度高(散列性高)的列放在前面。
- 使用最频繁的列放到联合索引的左侧
这样也可以较少的建立一些索引。同时,由于"最左前缀原则",可以增加联合索引的使用率。 - 在多个字段都要创建索引的情况下,联合索引优于单值索引
3.3 限制索引的数目
在实际工作中,我们也需要注意平衡,索引的数目不是越多越好。我们需要限制每张表上的索引数量,建议单张表索引数量不超过6个。原因:
①每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。
②索引会影响工INSERT、DELETE、UPDATE等语句的性能,因为表中的数据更改的同时,索引也会进行调整和更新,会造成负担。|
③优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个索引都可以用于查询,会增加MySQL优化器生成执行计划时间,降低查询性能。
3.4 哪些情况不适合创建索引
- 在where中使用不到的字段,不要设置索引
WHERE条件(包括GROUP BY、ORDER BY)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的 - 数据量小的表最好不要使用索引比如少于1000条
- 有大量重复数据的列上不要建立索引
举例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% 的时候,也不需要对这个字段使用索引
索引如果查询到大量重复数据还要一个一个去回表,非常影响性能
- 避免对经常更新的表创建过多的索引
- 不建议用无序的值作为索引
例如身份证、UUID(在索引比较时需要转为ASCII,并且插入时可能造成页分裂)、MD5、HASH、无序长字
符串等。
因为经常要进行页分裂 - 删除不再使用或者很少使用的索引
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不在需要,数据库管理员应当定期找出这些索引,将他们删除,从而减少索引对于更新操作的影响
- 不要定义冗余或重复的索引
① 冗余索引
举例:建表语句如下
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. 删除不再使用或者很少使用的索引
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不在需要,数据库管理员应当定期找出这些索引,将他们删除,从而减少索引对于更新操作的影响
- 不要定义冗余或重复的索引
① 冗余索引
举例:建表语句如下
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 既是主键、又给它定义为一个唯一索引,还给它定义了一个普通索引,可是主键本身就
会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况要避免。