如何解决三列上的复合索引
我们得到了 93M 行的映射表,其中保存了三个表和对应的三个表的映射信息。我们在访问映射表中的数据时面临性能问题。
表名 | PK信息 | 行数 |
---|---|---|
卖家 | SellerId 主键 | 3000 行 |
商店 | StoreId 主键 | 20000 行 |
产品 | ProductId 主键 | 20 万行 |
SellerStoreProductMapping | SellerId,ProductId,StoreId 复合非聚集索引。此表中还有一个代理键:SellerStoreProductMappingId,用作主键集群 | 9300 万行 |
我们的查询可以访问 93M 行表中的以下三种组合中的任何一种:Seller,Product,Store
。
我的实际查询是这样的:
SELECT < many columns from four tables>
FROM SellerStoreProductMapping
INNER JOIN Store
INNER JOIN Seller
INNER JOIN Product ...
WHERE SellerId = 123
但是,发生的是非聚集索引:SellerId,StoreId 在我们的查询中很少使用,即使我们对 SellerId 进行了过滤。它用于 storeId 的索引扫描。
我们的疑问是,对于这三列组合,
- 我们应该使用单独的非聚集索引(3 个索引)吗?
- 我们应该选择两列非聚集复合索引(4 个索引)吗?
- 我们是否应该选择三列复合非聚集索引(9 个索引)。而不是 9,我们将限制在特定的使用场景中。 ?
注意:我们无法创建聚集列存储索引,因为我们将 ROWVERSION 数据类型作为映射表中的数据类型之一。
解决方法
这可能不是答案(但我不允许发表评论),还有一点需要考虑:综合索引中索引的顺序。
如果您在 SellerStoreProductMapping 表中按以下顺序定义复合索引:SellerId,StoreId,ProductId,那么它只能有效地用于过滤 SellerID OR for (SellerId and StoreId) OR for (SellerId and StoreId and产品 ID)
如果您将三列的所有可能组合(7 种可能性)用作查询中的过滤器,那么您可能需要定义至少三个单独的索引。
可以在此处找到对此的一些参考:https://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys
,如果该索引涵盖查询中的所有信息并且谓词是可搜索的,那么您的索引将被系统地使用。
举个例子,让我们看看这些查询:
--1
SELECT *
FROM SellerStoreProductMapping
WHERE Seller = 1 AND Product = 2 AND Store = 1
-- 2
SELECT Seller,Product,Store
FROM SellerStoreProductMapping
WHERE Seller = 1 AND Product = 2 AND Store = 1
-- 3
SELECT anyOtherColumns
FROM SellerStoreProductMapping
WHERE Seller = 1 AND Product = 2 AND Store = 1
-- 4
SELECT Seller,Store
FROM SellerStoreProductMapping
WHERE Seller = 1 AND Product = 2 AND Store = 1
ORDER BY anyOtherColumns
只有查询 2 会系统地使用索引。 所有其他查询(1、3、4)都没有查询中使用的所有列,在索引键中......所以他们必须使用双重读取:
- 首先读取(在索引中查找)以查找符合谓词条件的候选行
- 第二次读取表中查找索引没有的列 有
将两次读取的成本与其他策略(例如扫描表)的成本进行比较。如果扫描成本较低,则不会使用索引...
,关于索引中列顺序的一些经验法则:
- 将
WHERE =
谓词中引用的任何列和唯一联接中引用的任何列放在索引键中(联接返回另一个表中的单行) - 然后是任何范围谓词,例如
> <= < <> BETWEEN
- 然后其他连接列
- 然后是任何排序列
- 不要忘记为您选择的任何其他列添加
INCLUDE
- 如果您可以在同一级别的两列之间进行选择,请先选择选择性最高的列。
因此,如果您的查询针对单个卖家和商店,但针对多个产品,则您需要索引 (Seller,Store,Product)
或 (Store,Seller,Product)
,具体取决于列的选择性。
进一步说明:
-
如果您选择的列未包含在索引中,编译器可能会决定进行聚集索引扫描,因为额外的键查找成本可能不值得。
-
只有当编译器可以计算出连接位于唯一的行上时,它才能检测到唯一的连接表。因此,请始终确保使用
PRIMARY/UNIQUE KEY
或唯一索引声明唯一列。 -
在您的情况下,您的连接表有一个额外的代理主键,我认为这是不必要的,因为其他三列唯一地定义了该行。即使你需要它,你也不必聚集在它上面。您可以使用与 PK 完全分开的聚集索引(充当包含所有列的索引)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。