赞
踩
mysql数据(包括索引)存储在磁盘,而一般查询数据的性能问题主要是IO问题,提高IO效率的关键问题主要是减少磁盘IO次数和IO的量
局部性原理:数据和程序都有聚集成群的倾向,同时之前被访问过的数据很可能再次被查询,空间局部性和时间局部性。
磁盘预读:内存跟磁盘发生交互的时候,一般情况下有一个最小的逻辑单元,称为页(datapage),页一般由操作系统决定是多大,一般是4k或者8k,而我们在进行数据交互的时候,可以取页的整数倍来进行读取,InnoDB存储引擎每次读取数据,读取16k。
索引本质上是一种可以可以加快检索速度的、排好序的数据结构。索引运用在表中某些字段上,存储索引需要单独占据一定的空间。
索引的优点:
索引的缺点:
在哪些列上面创建索引:
不在哪些列建索引?
三种常见的索引底层数据结构:分别是哈希表、有序数组和搜索树。
常见的索引类型有:普通索引、唯一索引、主键索引、全文索引、组合索引。
最基本的索引,没有任何限制;
--直接创建索引:
CREATE INDEX index_name ON table(column(length);
--修改表结构的方式添加索引:
ALTER TABLE table_name ADD INDEX index_name ON (column(length));
--创建表的时候同时创建索引:
CREATE TABLE ‘table’(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
与普通索引类似,但索引列的值必须唯一,允许有空值,可以有多个NULL值。如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似。
--创建唯一性索引:
CREATE UNIQUE INDEX indexName ON mytable(username(length));
--修改表结构:
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length));
--创建表的时候指定:
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
UNIQUE [indexName] (username(length))
);
可以理解为一种特殊的唯一索引,不允许有空值
--创建表的时候创建,当把某个列设为主键的时候,数据库会自动的创建一个以主键作为名称的主键索引。
CREATE TABLE table(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL)
);
--修改表结构:
ALTER TABLE `table_name` ADD PRIMARY KEY ( `col` );
全文索引仅可用于 MyISAM 表,并只支持从CHAR、VARCHAR或TEXT类型,用于替代效率较低的like 模糊匹配操作,而且可以通过多字段组合的全文索引一次性全模糊匹配多个字段。对于大容量的数据表,生成全文索引是一个非常消耗时间和硬盘空间的做法。对于较大的数据集,将你的数据输入一个没有FULLTEXT索引的表中,然后创建索引,其速度比把数据输入现有FULLTEXT索引的速度更为快。
全文索引使用B树存放索引数据,但使用的是特定的算法,将字段数据分割后再进行索引(一般每4个字节一次分割),索引文件存储的是分割前的索引字符串集合,与分割后的索引信息,对应Btree结构的节点存储的是分割后的词信息以及它在分割前的索引字符串集合中的位置。
–创建表的适合添加全文索引
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
FULLTEXT (content)
);
–修改表结构添加全文索引
ALTER TABLE article ADD FULLTEXT index_content(content);
–直接创建索引
CREATE FULLTEXT INDEX index_content ON article(content);
多个字段共同组成的索引,使用时需匹配最左匹配原则。
--创建组合索引:
ALTER TABLE `table_name` ADD INDEX index_name (col1(length), col2(length), col3(length));
索引下推:MySQL 5.6 引入的索引下推优化(Index Condition Pushdown,简称ICP), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。like KK%其实就是用到了索引下推优化。
例如对于辅助的联合索引(name,age,position),正常情况按照最左前缀原则,SELECT * FROM employees WHERE name like ‘LiLei%’ AND age = 22 AND position =‘manager’ 这种情况只会走name字段索引,因为根据name字段过滤完,得到的索引行里的age和position是无序的,无法很好的利用索引。
在MySQL5.6之前的版本,这个查询只能在联合索引里匹配到名字是 ‘LiLei’ 开头的索引,然后拿这些索引对应的主键逐个回表,到主键索引上找出相应的记录,再比对age和position这两个字段的值是否符合。MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。使用了索引下推优化后,上面那个查询在联合索引里匹配到名字是 ‘LiLei’ 开头的索引之后,同时还会在索引里过滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据。索引下推会减少回表次数,对于innodb引擎的表索引下推只能用于二级索引,innodb的主键索引(聚簇索引)树叶子节点上保存的是全行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果。
回表:当查询的数据在索引树中找不到的时候,需要回到主键索引树中去获取,这个过程叫做回表。
为什么范围查找Mysql没有用索引下推优化?
估计应该是Mysql认为范围查找过滤的结果集过大,like KK% 在绝大多数情况来看,过滤后的结果集比较小,所以这里Mysql选择给 like KK% 用了索引下推优化,当然这也不是绝对的,有时like KK% 也不一定就会走索引下推。
覆盖索引是select
的数据列只用从索引中就能够取得,不必回表,换句话说,查询列要被所建的索引覆盖。 覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
在MySQL中,聚簇索引和非聚簇索引的概念与一般的数据库系统相似,但是MySQL中的实现方式有一些特定的特点。
在MySQL中,辅助索引的选择性是一个重要的因素。选择性指的是索引键值的唯一性,即索引键值的不重复程度。具有高选择性的辅助索引可以更有效地缩小搜索范围,从而提高查询性能。
需要注意的是,MySQL InnoDB存储引擎中的表默认使用聚簇索引(主键索引)来组织数据,即数据行按照主键的顺序进行排序。 而其他存储引擎,如MyISAM存储引擎,没有聚簇索引的概念,它使用B树索引来组织数据。在使用MyISAM引擎的表中,可以通过创建辅助索引来加速查询操作。
综上所述,MySQL中的聚簇索引即主键索引,将数据按照主键顺序进行排序和存储;非聚簇索引即辅助索引,基于表的其他列创建的独立索引结构,通过索引键值和行指针来加速查询操作。
聚簇索引一般在下面场景使用:
(1)主键列,InnoDB存储引擎中,默认为表的主键建立一个聚簇索引。
(2)按范围存取的列或者在group by或order by中使用的列。在聚簇索引下,因为表中数据存储的物理顺序与索引的逻辑顺序一致,所以在包含范围检查(between、<、<=、>、>=)或使用group by或order by的查询时,一旦找到具有范围中第一个键值的行,具有后续索引值的行保证物理上毗连在一起而不必进一步搜索,避免了大范围扫描,可以大大提高查询速度。
(3)在连接操作中使用的列。
(4)不经常修改的列。因为码值修改后,数据行必须移动到新的位置。
数据存储文件和索引文件是分开的。物理顺序与索引顺序不相同,可以有多个,小于249个;
常见的索引的数据结构有:B+Tree、Hash索引。
B+Tree是MySQL中使用最频繁的索引数据结构。是Innodb和Myisam存储引擎模式的索引类型。相对Hash索引,B+Tree索引在查找时需要从根节点到叶节点进行多次IO操作,在查询速度比不上Hash索引,但是更适合排序等操作。
为什么选择 B+ 树
二叉树
- 最坏的情况下会成为一个链表,会成为顺序查找。相同的数据量下树比较高。树越高就意味着需要更多次的磁盘访问。
红黑树
- 一个节点只有一个key(关键字),不适合大数据量的存储(2的n次方=N,n为层数),磁盘IO次数还是 较大。
Hash表
- 对索引的key进行一次hash计算就可以定位出数据存储的位置
- 很多时候Hash索引要比B+树索引更高效
- 仅能满足“=”、“IN”,不支持范围查询
- hash冲突问题
B-Tree
- 叶子结点具有相同的深度,叶子结点的指针为空
- 所有索引元素不重复
- 结点中的数据索引从左到右递增排列
B+Tree(B-Tree变种)
- 非叶子结点不存储数据,只存储索引(冗余),可以放更多的索引
- 叶子结点包含所有索引字段
- 叶子结点用指针连接,提高了区间访问的速度
注:存储引擎最小是表级别的,不同的表可以有不同的存储引擎。
MyISAM存储引擎中索引是非聚集索引
InnoDB存储引擎中索引是聚集索引
为什么非主键索引结构叶子结点存储的是主键值?数据冗余、减少数据维护
- 数据冗余:虽然提升了查询性能,但是需要更多的空间来存储冗余的数据。
- 维护麻烦:一个地方修改数据,需要在多棵索引树上修改。
为什么InnoDB表必须有主键??
- 创建主键就会自动创建主键索引,主键索引是聚集索引,聚集索引将索引和完整的记录数据放在同一个B+Tree中文件中,减少了磁盘IO次数。
为什么推荐使用整型的自增主键?
- B+Tree中有一个指向指针指向关键字最小的叶子结点,所有叶子结点链接成一个单链表。所以如果主键为自增主键,插入数据时会直接在链表的尾部插入,效率高并且实现简单。如果是非自增的,B+Tree发生分裂的概率就会增大。
按照联合索引的键值顺序排序的索引。
mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,示例:对列col1、列col2和列col3建一个联合索引
CREATE INDEX test_col1_col2_col3 ON test(col1,col2,col3);
联合索引 test_col1_col2_col3 实际建立了(col1)、(col1,col2)、(col,col2,col3)三个索引。
SELECT * FROM test WHERE col1=“1” AND clo2=“2” AND clo4=“4”
上面这个查询语句执行时会依照最左前缀匹配原则,检索时会使用索引(col1,col2)进行数据匹配。
创建联合索引时列的选择原则(参考博客创建联合索引时列的选择原则):
列的离散性计算:count(distinct col)/ count(col)
例如:
id列一共9列都不重复 9/9 = 1
性别列一共9列只有(男或者女)两列 2/9 约等于0.2
离散性越高选择性越大
对索引使用左或者左右模糊匹配,也就是 like %xx 或者 like %xx% 这两种方式都会造成索引失效。原因在于查询的结果可能是多个,不知道从哪个索引值开始比较,于是就只能通过全表扫描的方式来查询。:
select * from doc where title like ‘%XX’; --不能使用索引
select * from doc where title like ‘XX%’; --非前导模糊查询,可以使用索引
反向条件不走索引:负向条件有:!=、<>、not in、not exists、not like 等
select * from doc where status != 1 and status != 2; --不能使用索引
select * from doc where status in (0,3,4); --优化为 in 查询,可以使用索引
IS NULL、IS NOT NULL 在无法使用索引,不过在mysql的高版本已经做了优化,允许使用索引
在索引列做任何操作(计算、函数、表达式)会导致索引失效而转向全表扫描
select * from doc where YEAR(create_time) <= ‘2016’; – 不能使用索引
select * from doc where create_time<= ‘2016-01-01’; – 可以使用索引
select * from order where date < = CURDATE(); – 不能使用索引
select * from order where date < = ‘2018-01-2412:00:00’; – 可以使用索引
select id from t where substring(name,1,3)=’abc’ – 不能使用索引
select id from t where name like ‘abc%’ – 可以使用索引
select id from t where num/2=100 – 不能使用索引
select id from t where num=100*2 – 可以使用索引
强制类型转换会导致全表扫描 :字符串类型不加单引号会导致索引失效,因为mysql会自己做类型转换,相当于在索引列上进行了操作。
select * from user where phone=13800001234; – 不能使用索引
select * from user where phone=‘13800001234’; – 可以使用索引
使用联合索引时,要符合最左前缀原则,否则会不走索引: :组合索引的字段数不允许超过5个。如果在a,b,c三个字段上建立联合索引 index(a,b,c),那么他会自动建立 a、(a,b)、(a,b,c) 三组索引。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。