1

我们得到了93M行的映射表,其中保存了三个表和对应的三个表的映射信息。我们在从映射表访问数据时面临性能问题。

表名 PK信息 行数
卖方 SellerId 主键 3000 行
店铺 StoreId 主键 20000 行
产品 ProductId 主键 200k 行
卖家商店产品映射 SellerId,ProductId,StoreId 复合非聚集索引。此表中还有一个代理键:SellerStoreProductMappingId,用作集群的主键 93M 行

我们的查询可以访问以下三种组合中的任何一种:Seller, Product, Store在 93M 行的表中。

我的实际查询是这样的:

SELECT < many columns from four tables>
FROM SellerStoreProductMapping
INNER JOIN Store
INNER JOIN Seller
INNER JOIN Product ...
WHERE SellerId = 123

但是,正在发生的是非聚集索引:SellerId、ProductId、StoreId 在我们的查询中很少使用,即使我们对 SellerId 进行了过滤。它用于 storeId 的索引扫描。

我们的疑问是,对于这三列组合,

  1. 我们应该选择单独的非聚集索引(3 个索引)吗?
  2. 我们应该选择两列非聚集复合索引(4 个索引)吗?
  3. 我们应该选择三列复合非聚集索引(9 个索引)吗?代替 9,我们将限制在特定的使用场景。?

注意:我们无法创建聚集列存储索引,因为我们将 ROWVERSION 数据类型作为映射表中的数据类型之一。

4

3 回答 3

1

这可能不是答案(但我不允许发表评论),还有一点需要考虑:综合索引中的索引顺序。

如果您在 SellerStoreProductMapping 表中按以下顺序定义复合索引:SellerId,StoreId,ProductId,那么它只能有效地用于过滤 SellerID OR for (SellerId and StoreId) OR for (SellerId and StoreId and ProductId) 的查询

如果您在查询中将三列(7 种可能性)的所有可能组合作为过滤器,那么您可能需要定义至少三个单独的索引。

可以在此处找到对此的一些参考:https ://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys

于 2021-02-17T08:02:24.853 回答
1

如果该索引涵盖了查询中的所有信息并且谓词是可搜索的,那么您的索引将被系统地使用。

例如,让我们看看这些查询:

--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, Product, Store
FROM   SellerStoreProductMapping
WHERE  Seller = 1 AND Product = 2 AND Store = 1
ORDER  BY anyOtherColumns

只有查询 2 会系统地使用索引。所有其他查询(1、3、4)没有查询中使用的所有列,在索引键中......所以他们必须使用双重读取:

  • 首先读取(在索引中查找)以查找在谓词下合格的候选行
  • 第二次读表找到索引没有的列

两次读取的成本与扫描表等其他策略的成本相比。如果扫描成本较低,则不会使用索引...

于 2021-02-17T08:20:32.943 回答
1

关于索引中的列顺序要记住的一些经验法则:

  • 将谓词中引用的任何列以及唯一连接中引用的任何列放在索引键的第一个WHERE =位置(连接从另一个表返回单行)
  • 然后是任何范围谓词,例如> <= < <> BETWEEN
  • 然后其他连接列
  • 然后任何排序列
  • 不要忘记为INCLUDE您选择的任何其他列添加 s
  • 如果您可以在同一级别的两列之间进行选择,请首先选择具有最高选择性的列。

因此,如果您的查询是针对单个卖家和商店,但有很多产品,那么您需要一个索引(Seller, Store, Product)(Store, Seller, Product),具体取决于列的选择性。

进一步说明:

  • 如果您选择的列未包含在索引中,编译器可能会决定执行聚集索引扫描,因为额外的键查找成本可能不值得。

  • 只有当编译器可以计算出连接在唯一的行上时,编译器才能检测到唯一的连接表。因此,请始终确保PRIMARY/UNIQUE KEY使用唯一索引或使用唯一索引声明唯一列。

  • 在您的情况下,您的连接表有一个额外的代理主键,我认为这是不必要的,因为其他三个列唯一地定义了该行。即使你需要它,你也不必聚集在它上面。您可以使用与 PK 完全分开的聚集索引(它充当包含所有列的索引)。

于 2021-02-17T11:12:57.883 回答