三列复合索引

Composite index on three columns

我们得到了93M行映射table,里面保存着3个table和对应的3个table的映射信息。我们在从映射 table.

访问数据时遇到性能问题
TableName PK information Number of Rows
Seller SellerId Primary Key 3000 rows
Store StoreId Primary Key 20000 rows
Product ProductId Primary Key 200k rows
SellerStoreProductMapping SellerId,ProductId,StoreId Composite non-clustered index. There is also one surrogate key in this table: SellerStoreProductMappingId which is used as Primary Key clustered 93M rows

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

我的实际查询是这样的:

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 数据类型作为映射中的一种数据类型table。

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

如果您按以下顺序在 SellerStoreProductMapping table 中定义复合索引:SellerId、StoreId、ProductId,那么它只能有效地用于筛选 SellerID 或筛选(SellerId 和 StoreId)或对于(SellerId、StoreId 和 ProductId)

如果您将这三列的所有可能组合(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, Product, Store
FROM   SellerStoreProductMapping
WHERE  Seller = 1 AND Product = 2 AND Store = 1
ORDER  BY anyOtherColumns

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

  • 首先读取(在索引中查找)以找到在谓词下符合条件的候选行
  • second read in the table 找到索引没有的列 有

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

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

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

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

补充说明:

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

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

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