سؤال

ومربع يتم تشغيل هذا الاستعلام على هو خادم مخصص يعمل في مراكز البيانات.

ومعالجات AMD Opteron 1354 رباعية النوى 2.20GHz 2GB من ذاكرة الوصول العشوائي ويندوز سيرفر 2008 x64 (نعم أنا أعرف أن 2GB من ذاكرة الوصول العشوائي فقط، وأنا الترقية إلى 8GB عندما يذهب المشروع الحية).

وهكذا ذهبت من خلال وخلق 250،000 الصفوف وهمية في جدول إلى التأكيد حقا اختبار بعض الاستعلامات التي LINQ إلى SQL يولد وتأكد من انهم لا الرهيب ولقد لاحظت واحد منهم كان يأخذ مبلغ سخيف من الزمن.

وكان هذا الاستعلام وصولا الى 17 ثانية مع الفهارس ولكن أزلت لهم من أجل هذه الإجابة للانتقال من البداية الى النهاية. المؤشرات الوحيدة هي المفاتيح الأساسية.

Stories table --
[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NOT NULL,
[CategoryID] [int] NOT NULL,
[VoteCount] [int] NOT NULL,
[CommentCount] [int] NOT NULL,
[Title] [nvarchar](96) NOT NULL,
[Description] [nvarchar](1024) NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[UniqueName] [nvarchar](96) NOT NULL,
[Url] [nvarchar](512) NOT NULL,
[LastActivityAt] [datetime] NOT NULL,

Categories table --
[ID] [int] IDENTITY(1,1) NOT NULL,
[ShortName] [nvarchar](8) NOT NULL,
[Name] [nvarchar](64) NOT NULL,

Users table --
[ID] [int] IDENTITY(1,1) NOT NULL,
[Username] [nvarchar](32) NOT NULL,
[Password] [nvarchar](64) NOT NULL,
[Email] [nvarchar](320) NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[LastActivityAt] [datetime] NOT NULL,

وحاليا في قاعدة البيانات هناك 1 مستخدم، 1 فئة و 250،000 القصص وحاولت لتشغيل هذا الاستعلام.

SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
INNER JOIN Users ON Users.ID = Stories.UserID
ORDER BY Stories.LastActivityAt

وسؤال يأخذ 52 ثانية لتشغيل، تحوم استخدام وحدة المعالجة المركزية في 2-3٪، Membery هو 1.1GB، 900MB حر ولكن استخدام القرص يبدو خارج نطاق السيطرة. انها @ 100MB / ثانية مع 2/3 من ذلك الكائن يكتب tempdb.mdf وبقية يقرأ من tempdb.mdf.

والآن بالنسبة للجزء مثيرة للاهتمام ...

SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
INNER JOIN Users ON Users.ID = Stories.UserID

SELECT TOP(10) *
FROM Stories
INNER JOIN Users ON Users.ID = Stories.UserID
ORDER BY Stories.LastActivityAt

SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
ORDER BY Stories.LastActivityAt

جميع 3 من هذه الأسئلة هي الى حد كبير حظة.

وخطة EXEC عن الاستعلام الأول.
http://i43.tinypic.com/xp6gi1.png

وخطط اكسيك لمدة 3 الاستفسارات الأخرى (بالترتيب).
http://i43.tinypic.com/30124bp.png
http://i44.tinypic.com/13yjml1.png
http://i43.tinypic.com/33ue7fb.png

وأي مساعدة سيكون محل تقدير كبير.

وخطة EXEC بعد إضافة الفهارس (وصولا الى 17 ثانية مرة أخرى).
http://i39.tinypic.com/2008ytx.png

ولقد حصلت على الكثير من ردود الفعل مفيدة من الجميع وأشكر لكم، وأنا حاولت زاوية جديدة في هذا. I الاستعلام عن قصص أحتاج، ثم في استعلامات منفصلة الحصول على فئات والمستخدمين ومع 3 الاستفسارات انها لم تكن الا 250ms ... أنا لا أفهم هذه القضية ولكن اذا كان يعمل وفي 250ms لا يقل في الوقت الحاضر وسوف أكون العصا مع ذلك. هنا هو رمز أنا استخدامها لاختبار هذا.

DBDataContext db = new DBDataContext();
Console.ReadLine();

Stopwatch sw = Stopwatch.StartNew();

var stories = db.Stories.OrderBy(s => s.LastActivityAt).Take(10).ToList();
var storyIDs = stories.Select(c => c.ID);
var categories = db.Categories.Where(c => storyIDs.Contains(c.ID)).ToList();
var users = db.Users.Where(u => storyIDs.Contains(u.ID)).ToList();

sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
هل كانت مفيدة؟

المحلول

وحاول إضافة فهرس على Stories.LastActivityAt. أعتقد أن تفحص فهرس متفاوت في خطة التنفيذ قد يكون راجعا إلى الفرز.

وتحرير: منذ عودة الاستعلام بلدي في لحظة مع صفوف بايت فقط بضعة طويلة، ولكن قد تم تشغيل لمدة 5 دقائق بالفعل ومازال مستمرا بعد أن أضاف VARCHAR 2K، وأعتقد أن ميتش لديه نقطة. هذا هو حجم تلك البيانات التي يتم تعديلا حول من أجل لا شيء، ولكن هذا يمكن ان تكون ثابتة في الاستعلام.

ومحاولة وضع الصلة وفرزها وأعلى (10) في طريقة عرض أو في استعلام متداخلة، ومن ثم الانضمام إلى الخلف ضد الجدول قصة للحصول على باقي البيانات فقط لل10 الصفوف التي تحتاج إليها.

ومثل هذا:

select * from 
(
    SELECT TOP(10) id, categoryID, userID
    FROM Stories
    ORDER BY Stories.LastActivityAt
) s
INNER JOIN Stories ON Stories.ID = s.id
INNER JOIN Categories ON Categories.ID = s.CategoryID
INNER JOIN Users ON Users.ID = s.UserID

إذا كان لديك فهرس على LastActivityAt، وهذا ينبغي تشغيل سريع جدا.

نصائح أخرى

وحتى إذا قرأت الجزء الأول بشكل صحيح، فإنه يستجيب في 17 ثانية مع فهرس. لا يزال فيه بعض الوقت للتحرك محدثا صوتا من أصل 10 السجلات. أنا أفكر أن الوقت في النظام من قبل الشرط. اريد فهرس على LastActivityAt، هوية المستخدم، معرف_الفئة. لمجرد التسلية، وإزالة النظام من قبل ومعرفة ما اذا كان إرجاع 10 سجلات بسرعة. إذا كان الأمر كذلك، فأنت أعلم أنه ليس في ينضم إلى جداول أخرى. كما أنه سيكون من المفيد لتحل محل * مع الأعمدة المطلوبة حيث أن جميع أعمدة الجدول 3 هي في بيانات tempdp كما كنت الفرز - كما ذكر نيل

وإذا نظرنا إلى تنفيذ خطط ستلاحظ هذا النوع اضافية - وأعتقد أن هذا الأمر الذي سوف يستغرق بعض الوقت. أفترض كان لديك فهرس مع 3 وكان 17 ثانية ... لذلك قد ترغب مؤشر واحد للمعايير الانضمام (حاليا، معرف_الفئة) وآخر للlastactivityat - معرفة ما إذا كان الذي ينفذ أفضل. كما أنه سيكون من الجيد لتشغيل الاستعلام من خلال معالج مؤشر ضبط.

وبلدي الاقتراح الأول هو إزالة *، واستبدالها مع الحد الأدنى من الأعمدة التي تحتاج إليها.

وثانيا، هناك الزناد المعنية؟ شيء من شأنه أن تحديث الحقل LastActivityAt؟

واستنادا إلى طلب مشكلتك، حاول إضافة مؤشر تركيبة على طاولة Stories (معرف_الفئة هوية المستخدم، LastActivityAt)

وأنت maxing خارج أقراص في الإعداد للجهاز.

ونظرا تعليقاتكم حول وضعك وضع البيانات / سجل / TEMPDB ملف، وأعتقد أن أي كمية من ضبط سوف تكون مرتجلة.

و250،000 صفوف صغيرة. تخيل مدى سوء مشاكلك ستكون مع 10 مليون صف.

وأقترح عليك نقل TEMPDB على محرك الأقراص الفعلي الخاص به (الأفضل على RAID 0).

وطيب، حتى بلدي آلة اختبار ليست سريعة. فعلا انها بطيئة حقا. انها 1.6 غيغاهرتز، ن 1 غيغابايت من ذاكرة الوصول العشوائي، لا أقراص متعددة، مجرد واحد (قراءة بطيئة) قرص لخادم SQL، نظام التشغيل، وإضافات.

وأنا خلقت الجداول الخاصة بك مع المفاتيح الأساسية والأجنبية محددة. إدراج 2 فئات، 500 مستخدم عشوائي، وقصص 250000 عشوائية.

وتشغيل الاستعلام الأول أعلاه يأخذ 16 ثانية (أي خطة مخبأ إما). إذا مؤشر I العمود LastActivityAt أحصل على النتائج في أقل من ثانية (لا ذاكرة التخزين المؤقت خطة هنا أيضا).

وفيما يلي النص كنت تفعل كل هذا.

    --Categories table --
Create table Categories (
[ID] [int] IDENTITY(1,1) primary key NOT NULL,
[ShortName] [nvarchar](8) NOT NULL,
[Name] [nvarchar](64) NOT NULL)

--Users table --
Create table Users(
[ID] [int] IDENTITY(1,1) primary key NOT NULL,
[Username] [nvarchar](32) NOT NULL,
[Password] [nvarchar](64) NOT NULL,
[Email] [nvarchar](320) NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[LastActivityAt] [datetime] NOT NULL
)
go

-- Stories table --
Create table Stories(
[ID] [int] IDENTITY(1,1) primary key NOT NULL,
[UserID] [int] NOT NULL references Users ,
[CategoryID] [int] NOT NULL references Categories,
[VoteCount] [int] NOT NULL,
[CommentCount] [int] NOT NULL,
[Title] [nvarchar](96) NOT NULL,
[Description] [nvarchar](1024) NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[UniqueName] [nvarchar](96) NOT NULL,
[Url] [nvarchar](512) NOT NULL,
[LastActivityAt] [datetime] NOT NULL)

Insert into Categories (ShortName, Name) 
Values ('cat1', 'Test Category One')

Insert into Categories (ShortName, Name) 
Values ('cat2', 'Test Category Two')

--Dummy Users
Insert into Users
Select top 500
UserName=left(SO.name+SC.name, 32)
, Password=left(reverse(SC.name+SO.name), 64)
, Email=Left(SO.name, 128)+'@'+left(SC.name, 123)+'.com'
, CreatedAt='1899-12-31'
, LastActivityAt=GETDATE()
from sysobjects SO 
Inner Join syscolumns SC on SO.id=SC.id
go

--dummy stories!
-- A Count is given every 10000 record inserts (could be faster)
-- RBAR method!
set nocount on
Declare @count as bigint
Set @count = 0
begin transaction
while @count<=250000
begin
Insert into Stories
Select
  USERID=floor(((500 + 1) - 1) * RAND() + 1)
, CategoryID=floor(((2 + 1) - 1) * RAND() + 1)
, votecount=floor(((10 + 1) - 1) * RAND() + 1)
, commentcount=floor(((8 + 1) - 1) * RAND() + 1)
, Title=Cast(NEWID() as VARCHAR(36))+Cast(NEWID() as VARCHAR(36))
, Description=Cast(NEWID() as VARCHAR(36))+Cast(NEWID() as VARCHAR(36))+Cast(NEWID() as VARCHAR(36))
, CreatedAt='1899-12-31'
, UniqueName=Cast(NEWID() as VARCHAR(36))+Cast(NEWID() as VARCHAR(36)) 
, Url=Cast(NEWID() as VARCHAR(36))+Cast(NEWID() as VARCHAR(36))
, LastActivityAt=Dateadd(day, -floor(((600 + 1) - 1) * RAND() + 1), GETDATE())
If @count % 10000=0
Begin
Print @count
Commit
begin transaction
End
Set @count=@count+1
end 
set nocount off
go

--returns in 16 seconds
DBCC DROPCLEANBUFFERS
SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
INNER JOIN Users ON Users.ID = Stories.UserID
ORDER BY Stories.LastActivityAt
go

--Now create an index
Create index IX_LastADate on Stories (LastActivityAt asc)
go
--With an index returns in less than a second
DBCC DROPCLEANBUFFERS
SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
INNER JOIN Users ON Users.ID = Stories.UserID
ORDER BY Stories.LastActivityAt
go

وهذا النوع هو بالتأكيد حيث لديك بطيئة أسفل والتي تحدث. فرز أساسا ينجز في بيانات tempdp وسوف جدول كبير يسبب الكثير لإضافتها. وجود مؤشر على هذا العمود بالتأكيد تحسين الأداء على النظام من قبل.

وأيضا، في تحديد بك SQL خادم الابتدائي ومفاتيح الخارجية يساعد إيمينسلي

وأسلوب لديك مسرود في التعليمات البرمجية أنيقة، وأساسا نفس الاستجابة التي cdonner كتب إلا في ج # وليس SQL. ضبط فإن ديسيبل ربما تعطي نتائج أفضل!

و- كريس

هل مسح ذاكرة التخزين المؤقت SQL Server قبل تشغيل كل الاستعلام؟

في SQL 2000، انها شيء من هذا القبيل DROPCLEANBUFFERS DBCC. جوجل الأمر لمزيد من المعلومات.

وإذا نظرنا إلى الاستعلام، كنت أود أن يكون مؤشرا ل

وCategories.ID Stories.CategoryID Users.ID Stories.UserID

وربما Stories.LastActivityAt

ولكن نعم، يبدو وكأنه يمكن أن تكون النتيجة وهمية 'كوس التخزين المؤقت.

عند كنت قد عملت مع SQL Server لبعض الوقت، وسوف تكتشف أنه حتى أصغر التغيرات على استفسار يمكن أن يسبب أوقات الاستجابة مختلفة إلى حد كبير. من ما قرأت في السؤال الأول، والنظر في خطة الاستعلام، وأظن أن محسن قررت أن أفضل أسلوب هو العمل على تشكيل نتيجة جزئية ومن ثم فرز هذا كخطوة منفصلة. النتيجة الجزئية هي مزيج مركب من المستخدمين وقصص الجداول. ويتكون هذا في tempdb. لذلك ويرجع ذلك إلى تشكيل وثم فرز هذا الجدول المؤقت الوصول إلى القرص المفرط.

وأنا أتفق على أن الحل يجب أن يكون لإنشاء فهرس مجمع على Stories.LastActivityAt، Stories.UserId، Stories.CategoryId. أمر مهم جدا، ويجب أن يكون LastActivityAt الحقل الأول.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top