数据库的优化,最大的一点:索引的建立,那么你写过这么多的sql语句,真的会用索引么,或者说会用索引么?体验过百万级别的查询速度么?
为什么要使用索引?
索引能够加速表中数据的查找和排序,索引是一个单独建立 的物理数据库,并且包含
一个指向数据表中指定列的数据值指针(指向对应数据的指针)。
作用:
1. 快速查询数据
2.保证数据的唯一性
3. 实现表与表之间的参照完整性
4.在使用order by、group by子句进行数据检索时,利用索引 可以减少排序和分组的时间。
优点:
1. 大大加快数据检索速度
2. 创建唯一索引,保证数据库表中每行数据的唯一性
3. 加速表之间的连接
4. 减少查询中分组和排序的时间
缺点:
1.需要占用物理空间
2. 对表中数据进行增加、删除和修改时索引也要更新,降低了数据维护速度
一.索引的分类(聚集索引,非聚合索引,复合索引)
1.聚集(clustered)索引,也叫聚簇索引。
定义:表中行的物理顺序与键值的逻辑(索引)顺序相同。因为数据的物理顺序只能有一种,所以一张表只能有一个聚集索引。如果一张表没有聚集索引,那么这张表就没有顺序的概念,所有的新行都会插入到表的末尾。对于聚集索引,叶节点即存储了数据行,不再有单独的数据页。
定义比较抽象,举个实际的例子:
一个表就像是我们以前用的新华字典,聚集索引就像是拼音目录,而每个字存放的页码就是我们的数据物理地址,我们如果要查询一个“哇”字,我们只需要查询“哇”字对应在新华字典拼音目录对应的页码,就可以查询到对应的“哇”字所在的位置,而拼音目录对应的A-Z的字顺序,和新华字典实际存储的字的顺序A-Z也是一样的,如果我们中文新出了一个字,拼音开头第一个是B,那么他插入的时候也要按照拼音目录顺序插入到A字的后面
下面看个数据,解释下数据物理地址:
地址 id username age
0x01 1 小张 11
0x02 2 小红 12
0x03 3 小华 11
.. .. .. ..
0xff 256 小英 12
第一列的地址表示该行数据在磁盘中的物理地址,后面三列才 是我们SQL里面用的表里的列,
其中id是主键,建立了聚集索引.结合上面的表格就可以理解这句话了吧:数据行的物理顺序与列值的顺序相同,
如果我们查询id比较靠后的数据,那么这行数据的地址在磁盘中的物理地址也会比较后,而且由于物理排列方式与聚集索引的顺序相同,所以也就只能建立一个聚集索引了.
不理解?咱们再用图来说明
树节点1下面有2和2的节点,也可以看作数据节点,是不是可以直接获取到对应的全部列的数据?这个就是聚集索引所有的优势
2.创建聚集索引(注:Sqlite不支持聚集索引)
2.1. 如果不创建索引,系统会自动创建一个隐含列作为表的聚集索引。
create table t1(
id int primary key,
name nvarchar(255)
)
2.2.创建表后添加聚集索引
SQL Server
create clustered index clustered_index on table_name(colum_name)
MySQL
alter table table_name add primary key(colum_name)
值得注意的是,最好还是在创建表的时候添加聚集索引,由于聚集索引的物理顺序上的特殊性,
因此如果再在上面创建索引的时候会根据索引列的排序移动全部数据行上面的顺序,会非常地耗费时间以及性能。
二.非聚集索引
定义:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非
聚集索引。
可以这样理解:除了聚集索引以外的索引都是非聚集索引,只是人们想细分一下非聚集索引,
分成普通索引,唯一索引,全文索引。如果非要把非聚集索引类比成现实生活中的东西,那
么非聚集索引就像新华字典的偏旁字典,他结构顺序与实际存放顺序不一定一致。
通俗点的理解就是:索引节点跟聚集节点是一样的,只是查询的时候,定义的索引在其他列上并没有覆盖,这时候还得去这个没有覆盖的列上再查询,这就需要二次查询了
标准的说法是:非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此如
果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么他还要进行第二
次的查询,查询节点上对应的数据行的数据。
举个例子:
id username age
1 小张 11
2 小红 12
3 小华 11
.. .. ..
256 小英 12
以及聚集索引clustered index(id), 非聚集索引index(username)。
使用以下语句进行查询,不需要进行二次查询,直接就可以从非聚集索引的节点里面就可以获取
到查询列的数据。
select id, username from t1 where username = '小张'
select username from t1 where username = '小张'
但是使用以下语句进行查询,就需要二次的查询去获取原数据行的score:
select username, age from t1 where username = '小张'
如何解决非聚集索引的二次查询问题?使用复合索引
三:复合索引(索引生效讲解)
复合索引(覆盖索引)顾名思义就是建立两列以上的索引,即可查询复合索引里的列的数据而不需要进行回表二次查询,如index(col1, col2),执行下面的语句:
select col1, col2 from t1 where col1 = 'xxx';
注意:在查询使用时,最好将条件顺序按找索引的顺序,这样效率最高
举些例子:
CREATE INDEX index_table ON table(x, y, z)创建索引
那么x,xy,xyz都是前导列,而yz,y,z这样的就不是
索引生效的条件:
前序列必须使用等于或者in操作最右边的列可以使用不等式,这样索引才可以完全生效。
同时,where子句中的列不需要全建立了索引,但是必须保证建立索引的列之间没有间隙
CREATE INDEX idx_ex1 ON ex1(a,b,c,d,e,...,y,z);
WHERE a=5 AND b IN (1,2,3) AND c IS NULL AND d='hello'
这显然对于abcd四列都是有效的,因为只有等于和in操作,并且是前导列。
再看三个查询语句:
WHERE a=5 AND b IN (1,2,3) AND c>12 AND d='hello'
那这里只有a,b和c的索引会是有效的,d列的索引会失效,因为它在c列的右边,而c列使用
了不等式,根据使用不等式的限制,c列已经属于最右边。
WHERE b=5 AND c=3 AND d = 'hello'
不生效,没有按照索引顺序
WHERE d = 'hello'
不生效,没有按照索引顺序
最后再看一条:
WHERE b IN (1,2,3) AND c NOT NULL AND d='hello'
索引将不会被生效,因为没有使用前导列,a不在,这个查询会是一个全表查询。
对于between,or,like,都无法使用索引。必须要用对应的等式来生效索引
between:
WHERE scope BETWEEN 100 and 202;
这时就应该将其转换成:
WHERE scope >= 10 AND scope <= 20;
like:
index_table WHERE scope LIKE 'sql%';;
此时应该将它转换成:
WHERE scope >= 'sql' AND scope < 'xxx';
or(用in):
WHERE scope = 'abc' OR scope = 'xyz';
此时应该将它转换成:
WHERE scope IN ('abc', 'xyz');
总结:
1.使用聚集索引的查询效率要比非聚集索引的效率要高,但是如果需要频繁去改变聚集索引的值,写入性能并不高,因为需要移动对应数据的物理位置。
2.非聚集索引在查询的时候可以的话就避免二次查询,这样性能会大幅提升。
3.索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片
联想问题?:
复合索引会替代单一索引么?