SQL Server ではデータはメモリ上にキャッシュされます。
キャッシュされたデータの読み込みと CPU の関連性について少しまとめてみたいと思います。
SQL Server のデータをすべてメモリ上にキャッシュされれば、ハードウェアリソースの利用には問題ないかというとそういうことはありません。
以下のようなテーブルがあります。
CREATE TABLE [dbo].[Table_1]( [c1] [uniqueidentifier] NOT NULL, [c2] [int] NULL, [c3] [int] NULL, [c4] [int] NULL, CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED ([c1] ASC) ) ON [PRIMARY] GO CREATE NONCLUSTERED INDEX [IX_Table_1] ON [dbo].[Table_1] ( [c2] ASC) GO
このテーブルに以下のクエリでデータを挿入してみます。
SET NOCOUNT ON GO DECLARE @i int , @cnt int = 1 SELECT @i = COUNT(*) + 1 FROM Table_1 WHILE (@cnt <= 100000) BEGIN INSERT INTO Table_1 VALUES(NEWID(), @cnt, @cnt, @cnt) SET @i+=1 SET @cnt+=1 END SELECT COUNT(*) FROM Table_1
このテーブルに対して、以下の 2 種類のクエリを実行してみます。
SET NOCOUNT ON SET STATISTICS TIME ON GO SELECT * FROM Table_1 WITH (INDEX=1) SELECT * FROM Table_1 WITH (INDEX=2) GO DECLARE @start DATETIME = GETDATE() DECLARE @i int SELECT @i = COUNT(*) FROM Table_1 WITH(INDEX=0) WHERE c2 = 5000 SELECT DATEDIFF (ms, @start, GETDATE()) DECLARE @start DATETIME = GETDATE() DECLARE @i int SELECT @i = COUNT(*) FROM Table_1 WHERE c2 = 5000 SELECT DATEDIFF (ms, @start, GETDATE())
一つはインデックスを使用しないクエリ、一つはインデックスを使用するクエリとなっています。
各クエリを実行する前にはインデックスのデータをキャッシュするクエリを実行しています。
全データがキャッシュされている状態でインデックスの使用有無によって CPU 時間の違いを見てみます。
INDEX=0 (テーブルスキャン) を発生させた場合には、6ms ほど実行に時間がかかっています。
Index Seek で検索をした場合には、0ms となっています。
# コンパイルが発生した場合にはこれ以上かかります。
すべてのデータをキャッシュしていてもテーブルのスキャンが発生すると経過時間が長くなり、CPU には負荷がかかります。
以下は、[sys.dm_exec_query_stats] から last_worker_time (マイクロ秒) を取得したものなります。
上が Index_Seek、下が Index_Scan (Table_Scan) が実行された場合の CPU 時間の内容となります。
メモリにデータがキャッシュされていてもスキャンをすると CPU 時間が大きい値となります。
これは大容量のメモリや高速なディスクを搭載していても同じ状況になります。
昨今、高速なディスクや大容量なメモリが低価格で実装できるようになってきていますが、クエリの基本である [必要となるデータだけを読む] というのは CPU の負荷にも影響してきますので、基本的なアプローチは大事ですね。