前置知识
数据库索引
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。
索引键值的逻辑顺序与索引所服务的表中相应行的物理顺序相同的索引,被称为聚集索引,反之为非聚集索引,索引一般使用二叉树排序索引键值的,聚集索引的索引值是直接指向数据表对应元组的,而非聚集索引的索引值仍会指向下一个索引数据块,并不直接指向元组,因为还有一层索引进行重定向,所以非聚集索引可以拥有不同的键值排序而拥有多个不同的索引。而聚集索引因为与表的元组物理顺序一一对应,所以只有一种排序,即一个数据表只有一个聚集索引。
以上引用自维基百科。
聚集索引(Clustered Index)类似门牌,门牌的物理地址和房子的物理地址是一一对应的。非聚集索引类似地址簿,地址簿除了可以按照门牌号顺序之外,还可以按其他项排序,如家庭人数,户主姓名等。通过户主找到的门牌号,再去找门牌就找到了这家人。
根据门牌查地址:门牌——》家庭
根据户主查地址:户主——》门牌号——》家庭
有聚集索引:聚集索引——》数据
有聚集索引where条件没用该列:非聚集索引——》聚集索引——》数据
无聚集索引:非聚集索引——》行指针——》数据
索引一般使用树结构,查找时间复杂度为O(logn)
执行计划简析(以SQLserver为例)
查看执行计划
SQLserver可以用SQLserver studio查看执行计划。我这里用的Navicat,代码如下:
SET STATISTICS PROFILE ON 你的SQL语句 --SELECT * from [dbo].[no_primary_key_jimmy] where bb = 'ede'SET STATISTICS PROFILE OFF |
SET STATISTICS PROFILE ON 你的SQL语句 --SELECT * from [dbo].[no_primary_key_jimmy] where bb = 'ede'SET STATISTICS PROFILE OFF |
执行计划简析
- Rows 表示该步骤实际产生的记录数;
- Executes 表示该步骤实际被执行的次数;
- StmtText 包含了每个步骤的具体描述,也就是如何访问和过滤表的数据、如何实现表的连接、如何进行排序操作等;
- StmtId,该语句的编号;
- NodeId,当前操作步骤的节点号,不代表执行顺序;
- Parent,当前操作步骤的父节点,先执行子节点,再执行父节点;
- PhysicalOp,物理操作,例如连接操作的嵌套循环实现;
- LogicalOp,逻辑操作,例如内连接操作;
- Argument,操作使用的参数;
- DefinedValues,定义的变量值;
- EstimateRows,估计返回的行数;
- EstimateIO,估计的 IO 成本;
- EstimateCPU,估计的 CPU 成本;
- AvgRowSize,平均返回的行大小;
- TotalSubtreeCost,当前节点累计的成本;
- OutputList,当前节点输出的字段列表;
- Warnings,预估得到的警告信息;
- Type,当前操作步骤的类型;
- Parallel,是否并行执行;
- EstimateExecutions,该步骤预计被执行的次数;
摘自 https://database.51cto.com/art/201912/608403.htm
表结构
如上图所示,tid为[dbo].[jimmy_50W]表的主键,会隐式建立聚集索引。该表数据量为70W(命名后又加了20W)
查询语句的执行计划
如图所示,存在聚集索引且where条件使用主键列,就会通过聚集索引搜索(Clustered Index Seek)方式定位数据行。
|--Clustered Index Seek(OBJECT:([sap].[dbo].[jimmy_50W].[PK__jimmy_50W__2B3F6F97]), SEEK:([sap].[dbo].[jimmy_50W].[tid]=CONVERT_IMPLICIT(int,[@1],0)) ORDERED FORWARD)
清空缓存,看一下执行时间,0.431秒,还比较快
查询执行时间
如果使用没有索引的列作为查询条件会怎么样呢
无索引列作为查询条件
可以看到这次变成了Clustered Index Scan(聚集索引扫描)。聚集索引扫描本质上就是表扫描,回到上面的比喻,一个一个看门牌其实也就是一家一家去找了。
清除缓存看一下查询时间
聚集索引扫描查询时间
15.302秒,15.302/0.431 = 35.5,快了三十多倍(个例,实际情况中,查询速度还受IO性能,并发数等多种因素影响)
需要注意的是,更新和删除也是需要根据where条件先找到该条数据的,当然如果你不想加where,直接删表跑路,那当我没说。
下面是删除,更新的执行计划
no_primary_key_jimmy表示没有主键没有索引的
可以看到现在变成了全表扫描( |--Table Scan(OBJECT:([sap].[dbo].[no_primary_key_jimmy]), WHERE:([sap].[dbo].[no_primary_key_jimmy].[bb]=[@2]) ORDERED))
全表扫描你懂的