B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树 它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。
B树有以下特点:
B+树和B-树之间的区别是,B+树所有的关键字都存储在叶子节点中,所有的叶子节点之间增加了一个双向指针。这样做可以更加方便的范围查找。
问题一:为什么在b-树或b+树中选择?
问题二:为什么选择B+树?
我们发现当使用id去查询数据时,效率很高,因为使用id可以利用B+树的特性,加速查询。相同的数据,使用id列查询和其他列查询,时间消耗差距很多。
那么我们是否可以创建一个和B+树相同的结构,但是使用别的列查询,答案是可以的但是如果我们创建这样的结构我们的数据库体积就会膨胀很多倍,十分影响性能。因此我们可以对这样的结构进行优化,优化之后我们只在叶子节点里id,当我们查到id之后再进行回表(回到原来的结构中根据id进行查询),查询整条记录,这就是我们日常工作中经常创建的【索引】。
主键和数据共存的索引叫聚簇索引,其他的叫做非聚簇索引,或辅助索引,或二级索引,例如我们上面使用姓名列+主键建立的索引。InnoDB使用的是聚簇索引,MyIsam使用的是非聚簇索引。
小问题:主键为什么建议使用自增id?
对于【二级索引】而言,根据其不同的特性,我们又可以分为普通索引、唯一索引、复合索引等。
就是普通的索引,没有任何要求,理论上任何列都可以当作普通索引,创建方式如下:
create index idx_user_name on user(user_name);
删除索引 :
drop index idx_user_name on user;
其中idx_user_name为索引名,user为数据库名,user_name为当作索引的列。(创建索引是一个很费时间的操作)
当我们创建一个索引时,如果这个列的数据很长,我们可以截取这个列的前几个字符当作索引,语法如下:
create index idx_email on user(email(5)); --使用email列的前五位作为索引
使用修改表的方式创建索引:
alter table user add index idx_user_name (user_name);
建表的同时创建索引:
create table tbl_name(
tid int,
tname varchar(20),
gender varchar(1),
index [indexName] (fieldName(length))
)
对列有要求,要求列的值不能重复。
建表时创建唯一索引:
create table tbl_name(
tid int,
tname varchar(20),
gender varchar(1),
unique index unique_index_tname (tname)
)
独立的sql语句创建索引,邮箱,用户名就应该创建唯一索引,姓名就应该是普通索引:
create unique index idx_email on user(email);
通过alter语句添加索引:
ALTER table tbl_name ADD UNIQUE [ux_indexName] (username(length))
唯一索引和主键的区别:
唯一约束和唯一索引的区别:
MySQL在执行查询语句时一般只会使用一个索引,除非是使用or连接了两个索引列会产生索引合并。(如果or连接了一个无索引列时索引会直接失效)。
当【查询语句】中包含【多个查询条件,且查询的顺序基本保持一致】时,我们推荐使用复合索引,索引的【组合使用】效率是低于【复合索引】的。
比如:我们经常按照A列、B列、C列进行查询时,通常的做法是建立一个由三个列共同组成的【复合索引】而不是对每一个列建立【普通索引】。
创建联合索引的方式如下:(复合索引会优先按照第一列排序,第一列相同的情况下会按照第二列排序)
alert table test add idx_a1_a2_a3 table (a1,a2,a3) --使用alert创建
create index idx_user_nick_name on ydl_user(user_name,nick_name,email(7)); --直接创建
最左前缀原则(重点):
(1)最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 ,如果建立(a,b,c,d)顺序的联合索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
(2)=和in可以乱序,比如a = 1 and b < 2 and c = 3 ,咱们建立的索引就可以是(a,c,b)或者(c,a,b)。
(因此在编写sql语句的时候尽量把范围查询的条件往后放)
小问题:什么联合索引的性能会比索引的组合使用效率高?
这是因为最左前缀原则引起的,假如我们查询两个
做全文检索(不如百度的搜索功能)使用的索引,但是这种场景,我们有更好的替代品,如:ElacticSearch,所以实际使用不多,只当了解。
使用 like + % 实现的模糊匹配有点类似全文索引。但是对于大量的文本数据检索,全文索引比 like + % 快 N 倍,速度不是一个数量级,但是全文索引可能存在【精度问题】。同时普通索引在使用like时如果%放在首位,索引会失效。
全文索引的版本支持
使用全文索引的注意
创建全文索引的方式:
(1).创建表时创建全文索引:
create table user (
..
FULLTEXT KEY fulltext_text(text)
)
(2).在已存在的表上创建全文索引:
create fulltext index fulltext_text on user(text);
(3).通过 SQL 语句 ALTER TABLE 创建全文索引:
alter table user add fulltext index fulltext_text(text);
(4).直接使用 DROP INDEX 删除全文索引:
drop index fulltext index on user;
(5).全文检索的语法:
select * from user where match(text) against('text');
hash索引是Memory存储引擎的默认方式,而且只有memory引擎支持hash索引,memory的数据是放在内存中的,一旦服务关闭,表中的数据就会丢失。
合理的使用memory引擎可以极大的提升性能,针对memory引擎的特点重启丢失),我们最好在其中存储一些公共的、常用的、不经常发生改变的数据,比如一些字典数据、配置数据等。同时,这些数据最好持久化在一些其他的地方,比如配置文件、其他的表,在程序启动的时候,主动的进行加载,我们可以使用如下sql,将一张表的数据加载到内存中:
insert into hash_user select * from user where id < 2000000;
当我们执行过程中可能会出现错误,提醒我们hash_user已经满了,我们只需要调节下边两个参数,修改配置文件重启MySQL即可:
tmp_table_size = 4096M
max_heap_table_size = 4096M
关于hash索引需要了解的几点:
MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。这是在地理位置领域使用的一种索引,其他场景用的很少,所以不需要深入学习。
explain关键字可以模拟MySQL优化器执行SQL语句,可以很好的分析SQL语句或表结构的性能瓶颈。用法如下图:
执行explain会产生以下11列内容,如下:
列号 | 列 | 说明 |
1 | id | select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序 |
2 | select_type | 查询类型 |
3 | table | 正在访问哪个表 |
4 | partitions | 匹配的分区 |
5 | type | 访问的类型 |
6 | possible_keys | 显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到 |
7 | key | 实际使用到的索引,如果为NULL,则没有使用索引 |
8 | key_len | 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度 |
9 | ref | 显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值 |
10 | rows | 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数 filtered //查询的表行占表的百分比 |
11 | filtered | 查询的表行占表的百分比 |
12 | Extra | 包含不适合在其它列中显示但十分重要的额外信息 |
select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序。
(1) id相同
id如果相同,可以认为是一组,执行顺序从上至下,如下查询语句:
(2) id不同
如果是子查询,id的序号会递增,id的值越大优先级越高,越先被执行例子。
(3)id部分相同部分不同
id如果相同,可以认为是一组,从上往下顺序执行在所有组中,id值越大,优先级越高,越先执行例子:
(1)SIMPLE
简单查询,不包含子查询或Union查询的sql语句。
(2)PRIMARY
查询中若包含任何复杂的子部分,最外层查询则被标记为主查询。
(3) SUBQUERY
在select或where中包含子查询。
(4)UNION
若第二个select出现在union之后,则被标记为UNION。
(6)UNION RESULT
从UNION表获取结果的合并操作。
最好到最差备注:掌握以下10种常见的即可NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL
表示数据来自哪张表
显示可能应用在这张表中的索引,一个或多个查询涉及到的字段若存在索引,则该索引将被列出,但不一定被实际使用
实际使用到的索引,如果为NULL,则没有使用索引查询中若使用了覆盖索引(查询的列刚好是索引),则该索引仅出现在key列表
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度在不损失精确度的情况下,长度越短越好key_len显示的值为索引字段最大的可能长度,并非实际使用长度即key_len是根据定义算而得,不是通过表内检索出的
哪些列或常量被用于查找索引列上的值
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数
匹配的分区
它指返回结果的行占需要读到的行(rows列的值)的百分比
频繁更新的字段不适合建立索引
where条件中用不到的字段不适合建立索引
表数据可以确定比较少的不需要建索引
数据重复且发布比较均匀的的字段不适合建索引(唯一性太差的字段不适合建立索引),例如性别,真假值
参与列计算的列不适合建索引,索引会失效
对字符串的列创建索引,如果可能,应该指定一个前缀长度。例如,如果有一个CHAR(255)的 列,如果在前10 个或20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要,最好给这些列创建复合索引。
关于类型转化导致索引失效的问题也不是所有的类型转化都会导致索引失效,比如以下例子中,=用int类型的age与字符串'18'比较,以及把日期类型与字符串'2008-05-31 17:20:54'比较,索引就没有失效,但是当用字符串gander与数字作比较时类型就失效了。
-- 索引不失效
explain select * from student where age = '18'
explain select * from ydl_user where login_date = '2008-05-31 17:20:54'
-- 索引失效 本来是字符串,你使用数字和他比较
explain select * from student where gander = 1