2014-05-22 17 views
21

Chúng tôi có một trang web khách hàng với cơ sở dữ liệu SQL 2012 50Gb trên máy chủ có RAM trên 100 GB.Bắt buộc SQL Server nạp trước toàn bộ cơ sở dữ liệu vào bộ nhớ

Khi ứng dụng được sử dụng, máy chủ SQL thực hiện công việc tuyệt vời trong việc lưu bộ nhớ đệm vào bộ nhớ nhưng hiệu suất tăng từ bộ nhớ đệm xảy ra thời gian SECOND một truy vấn được chạy chứ không phải là truy vấn đầu tiên.

Để cố gắng tối đa hóa bộ nhớ cache lượt truy cập lần đầu tiên truy vấn được chạy, chúng tôi đã viết một proc đó lặp qua từng chỉ số của tất cả các bảng trong toàn bộ DB, chạy này:

SELECT * INTO #Cache 
FROM ' + @tablename + ' WITH (INDEX (' + @indexname + '))' 

Trong một nỗ lực để buộc một to, xấu, được đọc nhiều dữ liệu nhất có thể. Chúng tôi đã lên kế hoạch chạy 15 phút một lần và công việc tuyệt vời nói chung.

Nếu không tranh luận về các tắc nghẽn khác, thông số phần cứng, kế hoạch truy vấn hoặc tối ưu hóa truy vấn, có ai có ý tưởng nào tốt hơn về cách thực hiện tác vụ tương tự này không?

CẬP NHẬT
Cảm ơn bạn đã đề xuất. Đã xóa "INTO #Cache". Thử nghiệm & nó đã không làm cho một sự khác biệt trên điền vào bộ đệm.
Đã thêm: Thay vì Chọn *, tôi chỉ chọn CHỈ các phím từ Chỉ mục. Điều này (rõ ràng) là nhiều hơn-to-điểm và nhanh hơn nhiều.
Đã thêm: Đọc & Chỉ mục ngăn chặn bộ nhớ cache.

Dưới đây là các mã hiện tại: (hy vọng nó hữu ích cho người khác)

CREATE VIEW _IndexView 
as 
-- Easy way to access sysobject and sysindex data 
SELECT 
so.name as tablename, 
si.name as indexname, 
CASE si.indid WHEN 1 THEN 1 ELSE 0 END as isClustered, 
CASE WHEN (si.status & 2)<>0 then 1 else 0 end as isUnique, 
dbo._GetIndexKeys(so.name, si.indid) as Keys, 
    CONVERT(bit,CASE WHEN EXISTS (SELECT * FROM sysconstraints sc WHERE object_name(sc.constid) = si.name) THEN 1 ELSE 0 END) as IsConstraintIndex 
FROM sysobjects so 
INNER JOIN sysindexes si ON so.id = si.id 
WHERE (so.xtype = 'U')--User Table 
AND  ((si.status & 64) = 0) --Not statistics index 
AND ( (si.indid = 0) AND (so.name <> si.name) --not a default clustered index 
     OR 
     (si.indid > 0) 
    ) 
AND si.indid <> 255 --is not a system index placeholder 

UNION 
SELECT 
so.name as tablename, 
si.name as indexname, 
CASE si.indid WHEN 1 THEN 1 ELSE 0 END as isClustered, 
CASE WHEN (si.status & 2)<>0 then 1 else 0 end as isUnique, 
dbo._GetIndexKeys(so.name, si.indid) as Keys, 
CONVERT(bit,0) as IsConstraintIndex 
FROM sysobjects so 
INNER JOIN sysindexes si ON so.id = si.id 
WHERE (so.xtype = 'V')--View 
AND  ((si.status & 64) = 0) --Not statistics index 
GO 


CREATE PROCEDURE _CacheTableToSQLMemory 
@tablename varchar(100) 
AS 
BEGIN 
DECLARE @indexname varchar(100) 
DECLARE @xtype varchar(10) 
DECLARE @SQL varchar(MAX) 
DECLARE @keys varchar(1000) 

DECLARE @cur CURSOR 
SET @cur = CURSOR FOR 
SELECT v.IndexName, so.xtype, v.keys 
FROM _IndexView v 
INNER JOIN sysobjects so ON so.name = v.tablename 
WHERE tablename = @tablename 

PRINT 'Caching Table ' + @Tablename 
OPEN @cur 
FETCH NEXT FROM @cur INTO @indexname, @xtype, @keys 
WHILE (@@FETCH_STATUS = 0) 
BEGIN 
     PRINT ' Index ' + @indexname 
     --BEGIN TRAN 
      IF @xtype = 'V' 
       SET @SQL = 'SELECT ' + @keys + ' FROM ' + @tablename + ' WITH (noexpand, INDEX (' + @indexname + '))' -- 
      ELSE 
       SET @SQL = 'SELECT ' + @keys + ' FROM ' + @tablename + ' WITH (INDEX (' + @indexname + '))' -- 

      EXEC(@SQL) 
     --ROLLBACK TRAN 
     FETCH NEXT FROM @cur INTO @indexname, @xtype, @keys 
END 
CLOSE @cur 
DEALLOCATE @cur 

END 
GO 
+2

Cứ 15 phút một lần? Nếu cơ sở dữ liệu của bạn là 50 GB và bạn đã cho SQL Server 100 GB bộ nhớ, thì bạn chỉ phải thực hiện việc này một lần, khi khởi động. –

+1

Tại sao bạn chèn vào bảng tạm thời? –

+0

@MartinSmith Tôi nghi ngờ dev, người đã viết nó thấy rằng nhanh hơn để chọn tất cả dữ liệu đó vào bảng #temp hơn là hiển thị kết quả vào một loạt các lưới trong SSMS (có thể qua kết nối mạng rất chậm). –

Trả lời

15

Trước hết, có một thiết lập gọi là "minumum server Memory" trông hấp dẫn. Phớt lờ nó đi. From MSDN:

Lượng bộ nhớ được cơ sở dữ liệu thu được hoàn toàn phụ thuộc vào khối lượng công việc được đặt trên cá thể. Một cá thể SQL Server không xử lý nhiều yêu cầu có thể không bao giờ đạt đến bộ nhớ máy chủ min.

Điều này cho chúng ta biết rằng việc đặt bộ nhớ tối thiểu lớn hơn sẽ không bắt buộc hoặc khuyến khích bất kỳ bộ nhớ đệm trước nào. Bạn có thể có other reasons to set this, nhưng việc điền trước vùng đệm không phải là một trong số chúng.

Vì vậy, bạn có thể làm gì để tải trước dữ liệu? Dễ thôi. Chỉ cần thiết lập một công việc đại lý để thực hiện một select * từ mọi bảng. Bạn có thể lên lịch để "Bắt đầu tự động khi Sql Agent Bắt đầu". Nói cách khác, những gì bạn đã làm là khá gần với cách tiêu chuẩn để xử lý này.

Tuy nhiên, tôi cần phải đề nghị ba thay đổi:

  1. Đừng cố gắng sử dụng một bảng tạm thời. Chỉ cần chọn từ bảng. Bạn không cần phải làm bất cứ điều gì với các kết quả để có được Sql Server để tải vùng đệm của bạn: tất cả những gì bạn cần làm là chọn. Bảng tạm thời có thể buộc máy chủ sql sao chép dữ liệu từ vùng đệm sau khi tải ... bạn sẽ kết thúc (một thời gian ngắn) lưu trữ mọi thứ hai lần.
  2. Không chạy 15 phút một lần. Chỉ cần chạy nó một lần khi khởi động, và sau đó để nó một mình. Sau khi được phân bổ, phải mất rất nhiều để có được Sql Server để phát hành bộ nhớ. Nó chỉ là không cần thiết để chạy lại này hơn và hơn.
  3. Đừng cố gợi ý chỉ mục. Gợi ý chỉ là: gợi ý. Sql Server là miễn phí để bỏ qua những gợi ý, và nó sẽ làm như vậy cho các truy vấn mà không có sử dụng rõ ràng cho chỉ mục. Cách tốt nhất để đảm bảo chỉ mục được tải sẵn là tạo truy vấn rõ ràng sử dụng chỉ mục đó. Một gợi ý cụ thể ở đây là đặt hàng các kết quả theo thứ tự như chỉ mục. Điều này thường sẽ giúp Sql Server sử dụng chỉ mục đó, bởi vì sau đó nó có thể "đi bộ chỉ mục" để tạo ra các kết quả.
+1

Hoặc tạo một quy trình hệ thống và đánh dấu nó làm thủ tục khởi động; điều này sẽ ngăn chặn không cần thiết churn nếu bạn đã bao giờ khởi động lại SQL Server Agent mà không cần khởi động lại động cơ. –

+1

Nó chỉ là gợi ý khóa mà đôi khi SQL Server bỏ qua. Những gợi ý chỉ mục sẽ đưa ra một lỗi nếu nó không thể tạo ra một kế hoạch. –

1

Đây không phải là câu trả lời, nhưng để bổ sung câu trả lời của Joel Coehoorn, bạn có thể xem dữ liệu bảng trong bộ nhớ cache bằng cách sử dụng câu lệnh này. Sử dụng tính năng này để xác định xem tất cả các trang có nằm trong bộ nhớ cache như bạn mong muốn không:

USE DBMaint 
GO 
SELECT COUNT(1) AS cached_pages_count, SUM(s.used_page_count)/COUNT(1) AS total_page_count, 
name AS BaseTableName, IndexName, 
IndexTypeDesc 
FROM sys.dm_os_buffer_descriptors AS bd 
INNER JOIN 
(
SELECT s_obj.name, s_obj.index_id, 
s_obj.allocation_unit_id, s_obj.OBJECT_ID, 
i.name IndexName, i.type_desc IndexTypeDesc 
FROM 
(
SELECT OBJECT_NAME(OBJECT_ID) AS name, 
index_id ,allocation_unit_id, OBJECT_ID 
FROM sys.allocation_units AS au 
INNER JOIN sys.partitions AS p 
ON au.container_id = p.hobt_id 
AND (au.type = 1 OR au.type = 3) 
UNION ALL 
SELECT OBJECT_NAME(OBJECT_ID) AS name, 
index_id, allocation_unit_id, OBJECT_ID 
FROM sys.allocation_units AS au 
INNER JOIN sys.partitions AS p 
ON au.container_id = p.partition_id 
AND au.type = 2 
) AS s_obj 
LEFT JOIN sys.indexes i ON i.index_id = s_obj.index_id 
AND i.OBJECT_ID = s_obj.OBJECT_ID) AS obj 
ON bd.allocation_unit_id = obj.allocation_unit_id 
INNER JOIN sys.dm_db_partition_stats s ON s.index_id = obj.index_id AND s.object_id = obj.object_ID 
WHERE database_id = DB_ID() 
GROUP BY name, obj.index_id, IndexName, IndexTypeDesc 
ORDER BY obj.name; 
GO 
+0

Lưu ý rằng nếu một chỉ mục đang được xây dựng/xây dựng lại trong khi điều này được chạy, truy vấn có thể không hoàn thành cho đến khi chỉ mục được hoàn thành xây dựng. – Nuzzolilo

Các vấn đề liên quan