質問: 1つの大きなデータセットを別の大きなデータセットに対して検索するために使用できる、より優れたインデックス作成戦略またはクエリSELECTはありますか?または、ルックアップディメンションテーブルをメモリに配置することを検討する必要がありますか(すべて125 GB)。
サーバー構成:
IIS Exchange Serverログテーブルスキーマ:
CREATE TABLE [FWY].[ExchangeServerLogTest](
[RowKey] [int] IDENTITY(1,1) NOT NULL,
[SourceFileName] [varchar](50) NOT NULL,
[SourceServer] [varchar](9) NOT NULL,
[SourceService] [varchar](6) NOT NULL,
[EventOccuranceTs] [datetime] NOT NULL,
[ServiceType] [varchar](50) NOT NULL,
[UserNameType] [varchar](25) NOT NULL,
[DomainId] [varchar](50) NULL,
[DomainName] [varchar](255) NULL,
[UserNameToLookup] [varchar](255) NOT NULL,
[UserAgent] [varchar](255) NULL,
[OutsideProtocolId] [varchar](10) NOT NULL,
[OutsideIp] [varchar](39) NULL,
[OutsideIpHex] [varbinary](16) NULL,
[InsideProtocolId] [varchar](10) NOT NULL,
[InsideIp] [varchar](39) NULL,
[InsideIpHex] [varbinary](16) NULL,
[DeviceId] [varchar](32) NULL,
[DeviceType] [varchar](25) NULL,
[DeviceModel] [varchar](75) NULL,
[AsOfDt] [date] NULL,
[OutsideProtocolKey] [int] NULL,
[InsideProtocolKey] [int] NULL,
CONSTRAINT [PK_ExchangeServerLogTest] PRIMARY KEY CLUSTERED
(
[RowKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [SECONDARY]
) ON [SECONDARY]
非クラスタ化インデックス:
CREATE NONCLUSTERED INDEX [NCIDX_ExchangeServerLogTest_InsideOutsideProtocolKeyIpHexInclRowKey] ON [FWY].[ExchangeServerLogTest]
(
[InsideProtocolKey] ASC,
[OutsideProtocolKey] ASC,
[InsideIpHex] ASC,
[OutsideIpHex] ASC
)
INCLUDE ( [RowKey]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
IP GeoLocationデータベンダーテーブルスキーマ
CREATE TABLE [DE].[IpGeoLocation](
[CreateTs] [datetime] NOT NULL,
[CreateBy] [varchar](50) NOT NULL,
[CreateSequenceKey] [int] NULL,
[UpdateTs] [datetime] NULL,
[UpdateBy] [varchar](50) NULL,
[UpdateSequenceKey] [int] NULL,
[ActiveInd] [int] NOT NULL,
[RowKey] [int] IDENTITY(1,1) NOT NULL,
[VendorKey] [int] NULL,
[VendorTypeKey] [int] NULL,
[DimensionTypeKey] [int] NULL,
[ProtocolKey] [int] NULL,
[ProtocolId] [varchar](10) NOT NULL,
[EffectiveStartDate] [date] NULL,
[EffectiveEndDate] [date] NULL,
[NetworkStartIp] [varchar](39) NOT NULL,
[NetworkStartIpHex] [varbinary](16) NULL,
[NetworkEndIp] [varchar](39) NOT NULL,
[NetworkEndIpHex] [varbinary](16) NULL,
[Country] [varchar](255) NOT NULL,
[Region] [varchar](255) NOT NULL,
[City] [varchar](255) NOT NULL,
[ConnectionSpeed] [varchar](255) NOT NULL,
[ConnectionType] [varchar](255) NOT NULL,
[MetroCode] [int] NOT NULL,
[Latitude] [numeric](6, 3) NULL,
[Longitude] [numeric](6, 3) NULL,
[PostalCode] [varchar](255) NOT NULL,
[PostalExtension] [varchar](255) NOT NULL,
[CountryCode] [int] NOT NULL,
[RegionCode] [int] NOT NULL,
[CityCode] [int] NOT NULL,
[ContinentCode] [int] NOT NULL,
[TwoLetterCountry] [varchar](2) NOT NULL,
[InternalCode] [int] NOT NULL,
[AreaCodes] [varchar](255) NOT NULL,
[CountryConfidenceCode] [int] NOT NULL,
[RegionConfidenceCode] [int] NOT NULL,
[CityConfidenceCode] [int] NOT NULL,
[PostalConfidenceCode] [int] NOT NULL,
[GmtOffset] [varchar](255) NOT NULL,
[InDistance] [varchar](255) NOT NULL,
[TimeZoneName] [varchar](255) NOT NULL,
CONSTRAINT [PK_IpGeoLocation] PRIMARY KEY CLUSTERED
(
[RowKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [SECONDARY]
) ON [SECONDARY]
非クラスタ化インデックス:
CREATE NONCLUSTERED INDEX [NCIDX_IpGeoLocation_ProtocolKeyNetworkStartEndIpHexIncRowKey] ON [DE].[IpGeoLocation]
(
[ProtocolKey] ASC,
[NetworkStartIpHex] ASC,
[NetworkEndIpHex] ASC
)
INCLUDE ( [RowKey]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
IPアドレスは、.NETのSystem.Netクラスを使用して16進値に変換されます:Ipaddress.Parse(IpAddress)。GetAddressBytes()。 SSISを使用してデータファイルを読み込み、ProtocolIdおよびIPアドレスをバイト配列として返すスクリプトコンポーネントがあります。これは、DT_BYTEとしてSSISに入り、SQL ServerのVARBINARY(16)フィールドにマッピングされます(バイト配列は暗黙的に16進値に変換されます)。
2つのデータセットがあります。IIS Exchange Server IPログレコードとサードパーティベンダーから提供されたIP GeoLocationデータです。Geolocationは一連のIPアドレスをカバーしています。IPアドレスをログファイルを取得してそのGeoLocationを取得します。両方のデータセットがIPv4とIPv6に対応しており、IPアドレスは文字列形式で受信されます。データを読み込むときに、IPアドレスを16進値[VARBINARY(16)]に変換して、 IPアドレスGeoLocationを検索できます。
ここでの問題は、大量のレコードをロードしていることです。現在、ベンダーは2億近くのIPアドレスの地理位置情報(つまり、ディメンションルックアップテーブル)を提供しています。最初から、パフォーマンスの最適化がすべての段階で必要になることを知っていました(つまり、ハードウェア構成、テーブルのパーティション分割、およびインデックス作成戦略)。 1週間分のサンプルログデータをロードしました。これは約1億5000万のレコードです。
注:ログファイルは解析され、レコードの約90%が無視されます-レコードの10%のみをロードしているため、ここで行うことのできるパフォーマンスの向上はありません。
ExchangeLogsテーブルに次のインデックスを作成しました。
IPGeoLocationテーブルに次のインデックスを作成しました。
IPジオロケーションを検索するとき、次のように2つのデータセットを結合します。
SELECT COUNT(DISTINCT DE.RowKey)
FROM DE.IpGeoLocation DE
INNER JOIN FWY.ExchangeServerLogTest T
ON T.InsideProtocolKey = DE.ProtocolKey
AND T.InsideIpHex BETWEEN DE.NetworkStartIpHex AND DE.NetworkEndIpHex
推定クエリ実行プラン: 推定InsideIpクエリ実行プラン
実際のクエリ実行プラン:クエリが完了するまで待機しています
SELECT COUNT(DISTINCT DE.RowKey)
FROM DE.IpGeoLocation DE
INNER JOIN FWY.ExchangeServerLogTest T
ON T.OutsideProtocolKey = DE.ProtocolKey
AND T.OutsideIpHex BETWEEN DE.NetworkStartIpHex AND DE.NetworkEndIpHex
推定実行プラン: 推定OutsideIpクエリ実行プラン
実際のクエリ実行プラン:DOES NOT FINISH
注2: ProtocolIdを含める必要があります。そうでない場合、各IPルックアップに対して2つの結果があります。1つはIPv4用で、もう1つはIPv6用です。
これは、コストの95%がインデックスseekにあり、インデックスの別の2%scan-97%がインデックス作業に起因することを考えると、非常に効率的な実行計画のようです。
ログファイルの各行には、内部IPアドレスと外部IPアドレスの両方が含まれています。ロードされたサンプルデータの場合:
結果:
ログテーブルもIP GeoLocationテーブルもパーティション分割していません。これにより、2つの個別のLUNを介してデータをストリーミングすることでパフォーマンスが向上する可能性がありますが、私はまだIT Opsグループからハードウェア構成仕様を取得しようとしています(新しいサーバーをプロビジョニングしただけなので、その情報はまだありません)。
まず、2つの個別のインデックスを追加することをお勧めします。
(InsideProtocolKey, InsideIpHex) INCLUDE (RowKey)
(OutsideProtocolKey, OutsideIpHex) INCLUDE (RowKey)
クエリを再試行してください。 4列のインデックスは、列が2番目と4番目の位置にあり、「内側」のクエリ(1番目と3番目)にはわずかに適しているため、「外側」クエリには適していません。さらに、これらの2つのインデックスのサイズは半分になります(20バイト対1行あたり40バイト)。
第二に、マイナーな改善。 ProtocolKey
列(およびそのバリエーションであるInside/Outside)には2つのオプションしかないため、(すべての)int
(4バイト)からtinyint
(1バイト)またはbit
(1ビット)に変換して保存できます。行ごとに3バイト(または3 + 7/8)。
これは大きな節約にはなりませんが、大きなテーブルの場合は役立ちます。それほど大きくない場合、200M行x 3バイト= 600MBの節約、すべてのインデックスに対して列が表示される場所。インデックスbit
列のスペースの使用については完全にはわかりませんが、同じテーブルサイズの場合、保存はtinyint
(600MB)以上の場合と同じです(最大775MB)。それでも、これについて再度言及しますが、列を使用するすべてのインデックスに対してです。
インデックスが小さいほど、ディスク上のサイズが小さくなり、より重要ですメモリが少ない、そしてメモリにとどまるの可能性が高くなります(特に、RAMあなたが持っている。
第3に、8 GBは非常に少量のように聞こえますRAM最近このサイズのテーブルがある場合は特にそうです。RAMは安いです(少なくとも合格するまで128GB Standard/Enterpriseのしきい値で、ライセンス料金が高くなります)。