关于mysql:在SQL中基于聚簇索引和非聚簇索引优化查询?

Optimizing queries based on clustered and non-clustered indexes in SQL?

我最近一直在读关于clustered indexnon-clustered index是如何工作的。我的理解很简单(如果错误,请纠正我):

支持clusterednon-clustered index的数据结构是B-Tree

clustered index:根据索引列(或键)对数据进行物理排序。每个table只能有一个clustered index。如果创建表时没有指定indexSQL服务器将自动在primary key column上创建clustered index

问题1:由于数据是根据索引进行物理排序的,所以这里不需要额外的空间。这是正确的吗?那么当我删除我创建的索引时会发生什么呢?

non-clustered index:在non-clustered indexes中,树的leaf-node包含列值和指向数据库中实际行的指针(行定位器)。这里需要额外的空间来将这个non-clustered index table物理地存储在磁盘上。但是,一个不受non-clustered Indexes.的数量限制。

问题2:这是否意味着对非聚集索引列的查询不会导致排序数据?

问题3:这里有一个额外的关联查找,可以使用叶节点上的指针定位实际的行数据。与聚集索引相比,这会有多大的性能差异?

练习:

考虑雇员表:

1
2
3
4
5
6
7
CREATE TABLE Employee
(
PersonID int PRIMARY KEY,
Name varchar(255),
age int,
salary int
);

现在我创建了一个Employee表(在Employee上创建了一个默认的聚集索引)。

此表上的两个常见查询仅在"年龄"和"薪资"列上发生。为了简单起见,假设表不经常更新

例如:

1
2
3
select * from employee where age > XXX;

select * from employee where salary > XXXX and salary < YYYY;

问题4:构建索引的最佳方法是什么,以便这两个列上的查询具有类似的性能。如果我有关于年龄的聚集索引,年龄列上的查询会更快,但是薪水列上的查询会更慢。

问题5:在一个相关的注释中,我反复看到索引(集群和非集群)应该在具有唯一约束的列上创建。为什么?如果不这样做会发生什么?

非常感谢你我读到的文章如下:

http://javarevisited.blogspot.com/2013/08/difference-between-clustered-index-and-nonclustered-index-sql-server-database.html

http://msdn.microsoft.com/en-us/library/ms190457.aspx

群集与非群集

聚集索引和非聚集索引实际上是什么意思?

聚集索引和非聚集索引有什么区别?

数据库索引如何工作?


我不知道Microsoft SQL Server的内部结构,但我可以回答MySQL,这是您为您的问题标记的。对于其他实现,细节可能有所不同。

Q1。对,聚集索引不需要额外的空间。

如果删除聚集索引会发生什么情况?MySQL的InnoDB引擎总是使用主键(或第一个非空的唯一键)作为聚集索引。如果定义一个没有主键的表,或者删除现有表的主键,InnoDB会为聚集索引生成一个内部人工键。此内部键没有可引用它的逻辑列。

Q2。不保证使用非聚集索引的查询返回的行顺序。实际上,这是访问行的顺序。如果需要按特定顺序返回行,则应在查询中使用ORDER BY。如果优化器可以推断所需的顺序与它访问行的顺序相同(索引顺序,无论是按聚集索引还是非聚集索引),那么它可以跳过排序步骤。

Q3。InnoDB非聚集索引的叶中没有指向相应行的指针,它具有主键的值。因此,在非聚集索引中的查找实际上是两个B树搜索,第一个是查找非聚集索引的叶,然后在聚集索引中进行第二个搜索。

这是单个B树搜索成本的两倍(或多或少),因此InnoDB有一个称为自适应哈希索引的额外功能。频繁搜索的值缓存在ahi中,下次查询搜索缓存值时,它可以执行O(1)查找。在ahi缓存中,它会直接找到指向聚集索引叶的指针,因此它会在一部分时间内消除两个B树搜索。

这可以提高总性能的程度取决于搜索以前搜索过的相同值的频率。根据我的经验,散列搜索与非散列搜索的比率通常为1:2左右。

Q4。构造索引以满足需要优化的查询。通常,聚集索引是主键或唯一键,至少在InnoDB的情况下,这是必需的。agesalary都不可能是唯一的。

你可能真的喜欢我的演示,如何设计索引。

Q5。当您声明一个唯一约束时,InnoDB会自动创建一个索引。如果没有约束的索引,就不能有约束。如果没有索引,插入值时引擎如何确保唯一性?它需要在整个表中搜索该列中的重复值。索引有助于提高唯一性检查的效率。


对于SQL Server

如果聚集索引不是唯一的,则仅需要Q1额外空间。SQL Server将在内部向非唯一聚集索引添加一个4字节的uniquifier。这是因为它在非聚集索引中将集群键用作rowid。

非聚集索引可以按顺序读取。这可能有助于您指定订单的查询。它还可能使合并联接具有吸引力。它还可以帮助进行范围查询(xcol)。

当使用非聚集索引时,q3 sql server会执行额外的"书签查找"。但是,只有当它需要一个不在索引中的列时,才需要这样做。还要注意,您可以在索引的叶级中额外添加cx1(0)列。如果一个索引可以在不进行额外查找的情况下使用,则称为覆盖索引。

如果需要查找书签,只需扫描整个聚集索引,就可以更快地完成行的高百分比查找。级别取决于行大小、键大小等,但5%的行是典型的截止值。

Q4如果应用程序中最重要的事情是尽可能快地进行这两个查询,那么可以在这两个查询上创建覆盖索引:

1
2
create index IX_1 on employee (age) include (name, salary);
create index IX_2 on employee (salary) include (name, age);

注意,您不必特别包括集群键,因为非集群索引将其作为行指针。

Q5这对于集群密钥比非集群密钥更重要,因为它是唯一的。然而,真正的问题是索引对于您的查询是否是选择性的。想象一个bit值的索引。除非数据的分布非常倾斜,否则这样的索引不太可能用于任何事情。

有关Uniquifier的详细信息。想象一下,你和一个非唯一的年龄聚集指数,和一个非聚集的工资指数。假设您有以下行:

1
2
3
age | salary | uniqifier
20  | 1000   | 1
20  | 2000   | 2

然后,工资索引将定位这样的行

1
2
1000 -> 20, 1
2000 -> 20, 2

假设您运行了查询select * from employee where salary = 1000,优化器选择使用薪资索引。然后它将从索引查找中找到对(20,1),然后在主数据中查找该值。