Sql-Server

在具有許多非常相似的起始值的 VARCHAR 列上建立索引是否性能不佳

  • August 7, 2012

在使用索引的查詢上,我們似乎有非常不尋常的糟糕表現。例如表格看起來像

  • PK BIGINT
  • ID VARCHAR(50)
  • Col1
  • Col2
  • 等等

所以我們需要在數據庫中插入一行,然後在ID上查找。但是第三方的ID,我們有PK。我們需要找回PK。但是這些 ID 中有很大一部分具有非常相似的起始值。例如

  • “//45-423484834893457”
  • “//45-573459834589345”
  • “//45-345345345345345

我不確定 SQL Server 是如何遍歷 BTree 的,如果它對值進行雜湊處理或從最左邊的位置開始進行字元串比較。

在查詢這些值時,具有非常大範圍的非常相似的值(至少前 4 個字元是相同的)是否會導致索引性能不佳?

更新:

抱歉,查找查詢是

SELECT PK_Column FROM table WHERE ID = @ID

標記要求:

SQL Server parse and compile time: 
  CPU time = 0 ms, elapsed time = 0 ms.

SQL Server Execution Times:
  CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
  CPU time = 0 ms, elapsed time = 22 ms.
SQL Server parse and compile time: 
  CPU time = 0 ms, elapsed time = 30 ms.

(1 row(s) affected)
SQL Server parse and compile time: 
  CPU time = 0 ms, elapsed time = 0 ms.

(1 row(s) affected)
SQL Server parse and compile time: 
  CPU time = 0 ms, elapsed time = 0 ms.

<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.1" Build="10.0.4000.0" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
 <BatchSequence>
   <Batch>
     <Statements>
       <StmtSimple StatementCompId="1" StatementEstRows="1" StatementId="1" StatementOptmLevel="TRIVIAL" StatementSubTreeCost="0.0032831" StatementText="SELECT
      LocalMsgId
    FROM
      Pdu (nolock)
  WHERE
     RemoteMsgId = '41/00/2789aeb8/1127796335811'
      
" StatementType="SELECT" ParameterizedText="(@1 varchar(8000))SELECT [LocalMsgId] FROM [Pdu](nolock) WHERE [RemoteMsgId]=@1" QueryHash="0x677C78E75E33C4C7" QueryPlanHash="0xB358D862A43E4853">
         <StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
         <QueryPlan CachedPlanSize="16" CompileTime="7406" CompileCPU="1970" CompileMemory="120">
           <RelOp AvgRowSize="23" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Index Seek" NodeId="0" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032831" TableCardinality="5074270">
             <OutputList>
               <ColumnReference Database="[smpp]" Schema="[dbo]" Table="[Pdu]" Column="LocalMsgId" />
             </OutputList>
             <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" NoExpandHint="false">
               <DefinedValues>
                 <DefinedValue>
                   <ColumnReference Database="[smpp]" Schema="[dbo]" Table="[Pdu]" Column="LocalMsgId" />
                 </DefinedValue>
               </DefinedValues>
               <Object Database="[smpp]" Schema="[dbo]" Table="[Pdu]" Index="[IX_Pdu_RemoteMsgId]" IndexKind="NonClustered" />
               <SeekPredicates>
                 <SeekPredicateNew>
                   <SeekKeys>
                     <Prefix ScanType="EQ">
                       <RangeColumns>
                         <ColumnReference Database="[smpp]" Schema="[dbo]" Table="[Pdu]" Column="RemoteMsgId" />
                       </RangeColumns>
                       <RangeExpressions>
                         <ScalarOperator ScalarString="[@1]">
                           <Identifier>
                             <ColumnReference Column="@1" />
                           </Identifier>
                         </ScalarOperator>
                       </RangeExpressions>
                     </Prefix>
                   </SeekKeys>
                 </SeekPredicateNew>
               </SeekPredicates>
             </IndexScan>
           </RelOp>
           <ParameterList>
             <ColumnReference Column="@1" ParameterCompiledValue="'41/00/2789aeb8/1127796335811'" />
           </ParameterList>
         </QueryPlan>
       </StmtSimple>
     </Statements>
     <Statements>
       <StmtSimple StatementCompId="2" StatementId="2" StatementText="
SET STATISTICS IO OFF
" StatementType="SET STATS" />
     </Statements>
   </Batch>
   <Batch>
     <Statements>
       <StmtSimple StatementCompId="1" StatementId="1" StatementText="SET STATISTICS TIME OFF
" StatementType="SET STATS" />
     </Statements>
   </Batch>
 </BatchSequence>
</ShowPlanXML>

這取決於您使用的查詢。MS SQL Server 使用始終平衡的 BTree 索引,但如果您使用這樣的查詢:

select * from table where field like 'some%'

並且您的大部分記錄都符合這種情況,MS SQL Server 可以決定使用表掃描而不是索引掃描或索引查找會更便宜。

另外: 無論如何,您可以使用計算列來反轉您的欄位值並在其上創建索引

從執行計劃來看,你對那個查詢真的沒什麼可做的——它已經是最優的了。

我真正的問題是:性能問題是什麼?

如果即使是單個語句也很慢,那麼系統是否足夠繁忙以至於索引頁面會超出記憶體?最大記憶體設置是否正確配置?即使有這麼寬的索引欄位,在記憶體中遍歷索引頁仍然會比較快。

如果您只是查看 Profiler 並看到很多很多這樣的小查詢,那麼我們是否在談論某種批處理過程,它恰好SELECT在循環中執行單行?這是一個應用程序問題——進行單例查找而不是基於集合的操作可能會使任何系統陷入癱瘓。

引用自:https://dba.stackexchange.com/questions/22072