2014-05-22 10 views
21

में पूरे डेटाबेस को प्री-कैश करने के लिए मजबूर करना हमारे पास 100+ जीबी रैम वाले सर्वर पर 50 जीबी SQL 2012 डेटाबेस वाला क्लाइंट साइट है।SQL सर्वर को मेमोरी

जैसा कि एप्लिकेशन का उपयोग किया जाता है, SQL सर्वर स्मृति में डीबी को कैशिंग करने का एक अच्छा काम करता है लेकिन कैशिंग से प्रदर्शन वृद्धि दूसरी बार क्वेरी होती है, पहले नहीं।

अधिकतम करने के लिए कैश पहली बार प्रश्नों चलाए जा रहे हैं हिट की कोशिश करने के लिए, हम एक proc कि पूरे डीबी के भीतर हर तालिका के हर सूचकांक के माध्यम से दोहराता, यह चल रहा है ने लिखा है:

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

एक मजबूर करने की कोशिश में जितना संभव हो उतना डेटा के लिए बड़े, बदसूरत, समेकित पढ़ा। हमने इसे हर 15 मिनट में चलाने के लिए निर्धारित किया है, और यह सामान्य रूप से एक महान काम करता है।

अन्य बाधाओं, हार्डवेयर चश्मा, क्वेरी प्लान, या क्वेरी ऑप्टिमाइज़ेशन पर बहस किए बिना, क्या किसी के पास इस कार्य को पूरा करने के बारे में कोई बेहतर विचार है?

अद्यतन
सुझावों के लिए धन्यवाद। "INTO # कैश" हटा दिया गया। परीक्षण & ने बफर भरने पर कोई फर्क नहीं पड़ता।
जोड़ा गया: चयन करने के बजाय *, मैं केवल इंडेक्स से चाबियाँ चुन रहा हूं। यह (जाहिर है) अधिक से अधिक बिंदु है और बहुत तेज़ है।
जोड़ा गया: & कैश प्रतिबंध सूचकांक भी पढ़ें। (आशा है कि यह किसी और के लिए उपयोगी है) सब से

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

हर 15 मिनट? यदि आपका डेटाबेस 50 जीबी है और आपने SQL सर्वर 100+ जीबी मेमोरी दी है, तो आपको स्टार्टअप पर केवल एक बार ऐसा करना होगा। –

+1

आप एक temp तालिका में क्यों डालने जा रहे हैं? –

+0

@ मार्टिनस्मिथ मुझे संदेह है कि जिस देव ने इसे लिखा है, वह पाया गया है कि एसएसएमएस में संभवतः आउटपुट को एक बहुत ही धीमी नेटवर्क कनेक्शन पर आउटपुट प्रस्तुत करने के बजाय #temp तालिका में उस डेटा को चुनना तेज़ था। अधिकांश पैरामीटर के लिए –

उत्तर

15

सबसे पहले, वहाँ एक सेटिंग "न्यूनतम भी सर्वर स्मृति" कहा जाता है कि आकर्षक दिखाई देता है:

यहाँ वर्तमान कोड है। अनदेखी करो इसे। From MSDN:

डेटाबेस इंजन द्वारा अधिग्रहित स्मृति की मात्रा पूरी तरह से उदाहरण पर रखे गए वर्कलोड पर निर्भर है। एक SQL सर्वर उदाहरण जो कई अनुरोधों को संसाधित नहीं कर रहा है, वह कभी भी सर्वर सर्वर मेमोरी तक नहीं पहुंच सकता है।

यह हमें बताता है कि एक बड़ी न्यूनतम मेमोरी सेट करने से किसी भी पूर्व-कैशिंग को मजबूर या प्रोत्साहित नहीं किया जाएगा। आपके पास other reasons to set this हो सकता है, लेकिन बफर पूल को भरना उनमें से एक नहीं है।

तो डेटा को प्री-लोड करने के लिए आप क्या कर सकते हैं? यह आसान है। प्रत्येक तालिका से select * करने के लिए बस एक एजेंट नौकरी स्थापित करें। आप इसे "एसक्यूएल एजेंट शुरू होने पर स्वचालित रूप से प्रारंभ" करने के लिए शेड्यूल कर सकते हैं। दूसरे शब्दों में, आप जो भी कर रहे हैं वह इसे संभालने के मानक तरीके के करीब है।

हालांकि, मैं तीन परिवर्तनों का सुझाव की जरूरत है:

  1. अस्थायी तालिका का उपयोग करने की कोशिश मत करो। बस टेबल से चुनें। एसक्यूएल सर्वर को अपने बफर पूल को लोड करने के लिए आपको परिणामों के साथ कुछ भी करने की आवश्यकता नहीं है: आपको केवल चयन करना है। एक अस्थायी तालिका एसक्यूएल सर्वर को लोड करने के बाद बफर पूल से डेटा की प्रतिलिपि बनाने के लिए मजबूर कर सकती है ... आप चीजों को संग्रहित कर सकते हैं (संक्षिप्त रूप से) दो बार
  2. इसे हर 15 मिनट में न चलाएं। इसे स्टार्टअप पर बस एक बार चलाएं, और फिर इसे अकेला छोड़ दें। एक बार आवंटित होने पर, एसक्यूएल सर्वर को स्मृति जारी करने में बहुत कुछ लगता है। इसे बार-बार चलाने के लिए जरूरी नहीं है।
  3. किसी सूचकांक को संकेत देने का प्रयास न करें। संकेत सिर्फ यही हैं: संकेत। एसक्यूएल सर्वर उन संकेतों को अनदेखा करने के लिए स्वतंत्र है, और यह उन प्रश्नों के लिए ऐसा करेगा जो इंडेक्स के लिए स्पष्ट उपयोग नहीं करते हैं। यह सुनिश्चित करने का सबसे अच्छा तरीका है कि इंडेक्स प्री-लोडेड है, वह क्वेरी तैयार करना है जो स्पष्ट रूप से उस इंडेक्स का उपयोग करती है। इंडेक्स के समान क्रम में परिणामों को ऑर्डर करने का एक विशिष्ट सुझाव यहां दिया गया है। इससे अक्सर एसक्यूएल सर्वर उस इंडेक्स का उपयोग करने में मदद करेगा, क्योंकि तब यह परिणाम उत्पन्न करने के लिए "इंडेक्स चल सकता है"।
+1

या एक सिस्टम प्रक्रिया बनाएँ और इसे स्टार्टअप प्रक्रिया के रूप में चिह्नित करें; यदि आप कभी भी इंजन को पुनरारंभ किए बिना SQL सर्वर एजेंट को पुनरारंभ करते हैं तो यह अनावश्यक मंथन को रोक देगा। –

+1

यह केवल संकेतों को लॉक कर रहा है कि SQL सर्वर कभी-कभी अनदेखा करता है। उन इंडेक्स संकेतों में कोई त्रुटि होगी यदि यह कोई योजना नहीं दे सका। –

1

यह कोई जवाब नहीं है, लेकिन जोएल कोहुर्न के उत्तर को पूरक करने के लिए, आप इस कथन का उपयोग करके कैश में तालिका डेटा देख सकते हैं। यह निर्धारित करने के लिए इसका उपयोग करें कि क्या सभी पृष्ठ कैश में रह रहे हैं जैसा कि आप उम्मीद करेंगे:

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

ध्यान दें कि यदि यह चलाया जाता है तो एक इंडेक्स बनाया/पुनर्निर्मित किया जा रहा है, तो क्वेरी समाप्त होने तक क्वेरी पूर्ण नहीं हो सकती है। – Nuzzolilo