Много хора са внедрили ASPState в своята среда. Някои хора използват опцията в паметта (InProc), но обикновено виждам, че се използва опцията за база данни. Тук има някои потенциални неефективности, които може да не забележите на сайтове с малък обем, но това ще започне да се отразява на производителността, когато обемът ви в мрежата се увеличава.
Модел за възстановяване
Уверете се, че ASPState е настроен на просто възстановяване – това драстично ще намали въздействието върху дневника, което може да бъде причинено от големия обем (преходни и до голяма степен за еднократна употреба) записи, които вероятно ще отидат тук:
ПРОМЕНЯ БАЗА ДАННИ ASPState SET RECOVERY SIMPLE;
Обикновено тази база данни не трябва да е в състояние на пълно възстановяване, особено след като сте в режим на възстановяване след бедствие и възстановявате базата си данни, последното нещо, за което трябва да се притеснявате, е да се опитвате да поддържате сесии за потребители във вашето уеб приложение – които е вероятно да да бъде отдавна изчезнал от времето, когато сте възстановили. Не мисля, че някога съм попадал в ситуация, при която възстановяването в даден момент е било необходимост за преходна база данни като ASPState.
Минимизиране/изолиране на I/O
Когато първоначално настройвате ASPState, можете да използвате -sstype c
и -d
аргументи за съхраняване на състоянието на сесията в персонализирана база данни, която вече е на друго устройство (точно както бихте направили с tempdb). Или, ако вашата база данни tempdb вече е оптимизирана, можете да използвате -sstype t
аргумент. Те са обяснени подробно в документите на режимите на състоянието на сесията и ASP.NET SQL Server на MSDN.
Ако вече сте инсталирали ASPState и сте преценили, че ще се възползвате от преместването му в неговия собствен (или поне различен) том, тогава можете да планирате или да изчакате кратък период на поддръжка и да изпълните следните стъпки:
ПРОМЕНЯ БАЗА ДАННИ ASPState SET SINGLE_USER С НЕЗАБАВНО ОТМЕНЯНЕ; ПРОМЕНИ БАЗА ДАННИ ASPState SET OFFLINE; ПРОМЕНЯНЕ НА ФАЙЛА ЗА МОДИФИКАЦИЯ НА ASPState на базата данни (ИМЕ =ASPState, FILENAME ='{нов път}\ASPState.mdf'); ПРОМЕНЯНЕ НА ФАЙЛА ЗА МОДИФИКАЦИЯ НА ASPState на базата данни (ИМЕ =ASPState_log, FILENAME ='{нов път}\ASPState_);В този момент ще трябва ръчно да преместите файловете в
<new path>
, и след това можете да върнете базата данни онлайн:ПРОМЕНЯ БАЗА ДАННИ ASPState SET ONLINE; ALTER DATABASE ASPState SET MULTI_USER;Изолиране на приложения
Възможно е да се насочат повече от едно приложение към една и съща база данни за състоянието на сесията. Препоръчвам против това. Може да искате да насочите приложения към различни бази данни, може би дори в различни случаи, за да изолирате по-добре използването на ресурси и да осигурите максимална гъвкавост за всичките си уеб свойства.
Ако вече имате няколко приложения, използващи една и съща база данни, това е добре, но ще искате да следите въздействието, което всяко приложение може да окаже. Рекс Танг от Microsoft публикува полезна заявка, за да види пространство, изразходвано от всяка сесия; ето модификация, която ще обобщи броя на сесиите и общия/ср. размер на сесията за приложение:
ИЗБЕРЕТЕ a.AppName, SessionCount =COUNT(s.SessionId), TotalSessionSize =SUM(DATALENGTH(s.SessionItemLong)), AvgSessionSize =AVG(DATALENGTH(s.SessionItemLong))FROM dbo.ASPStatesTIN s JOempSessionItemLong ASPStateTempApplications КАТО ON SUBSTRING(s.SessionId, 25, 8) =SUBSTRING(sys.fn_varbintohexstr(CONVERT(VARBINARY(8), a.AppId)), 3, 8) GROUP BY a.AppNameORDER BY DESC TotalSession;Ако установите, че имате едностранно разпространение тук, можете да настроите друга база данни ASPState другаде и вместо това да насочите едно или повече приложения към тази база данни.
Направете по-удобни изтривания
Кодът за
dbo.DeleteExpiredSessions
използва курсор, заместващ единиченDELETE
в по-ранни реализации. (Мисля, че това се основава до голяма степен на тази публикация от Грег Лоу.)Първоначално кодът беше:
СЪЗДАВАНЕ НА ПРОЦЕДУРА DeleteExpiredSessionsAS DECLARE @now DATETIME SET @now =GETUTCDATE() DELETE ASPState..ASPStateTempSessions WHERE Изтича <@now RETURN 0GO(И все още може да бъде, в зависимост от това къде сте изтеглили източника или преди колко време сте инсталирали ASPState. Има много остарели скриптове за създаване на базата данни, въпреки че наистина трябва да използвате aspnet_regsql.exe.)
В момента (от .NET 4.5) кодът изглежда така (някой знае ли кога Microsoft ще започне да използва точка и запетая?).
ПРОМЕНИ ПРОЦЕДУРА [dbo].[DeleteExpiredSessions]КАКТО ЗАДАДЕТЕ NOCOUNT ON SET DEADLOCK_PRIORITY LOW DECLARE @now datetime SET @now =GETUTCDATE() CREATE TABLE #tblExpiredSessions (SessionID nvarSEYEssion(88) INMARICHESSION (8) ) ИЗБЕРЕТЕ SessionID ОТ [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED) WHERE Изтича <@now IF @@ROWCOUNT <> 0 BEGIN ДЕКЛАРИРАНЕ ExpiredSessionCursor CURSOR LOCAL FORWARD_ONLY FROM FORWARD_ONLY FROMED_ONLY FROMED_ONLY READ_tvarsEssionsPiR_Sh. СЛЕДВАЩ ОТ ExpiredSessionCursor В @SessionID ДОКАТО @@FETCH_STATUS =0 ЗАПОЧНЕТЕ ИЗТРИВАНЕ ОТ [ASPState].dbo.ASPStateTempSes sions WHERE SessionID =@SessionID И Изтича <@now FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID END CLOSE ExpiredSessionCursor DEALLOCATE ExpiredSessionCursor END DROP TABLE #tblExpiredSessions> ВРЪЩА 0Идеята ми е да имам щастлива среда тук – не се опитвайте да изтриете ВСИЧКИ редове с един замах, но също така не играйте един по един удар на мол. Вместо това изтрийте
n
редове наведнъж в отделни транзакции – намаляване на продължителността на блокиране и също така минимизиране на въздействието върху регистрационния файл:ПРОМЕНИ ПРОЦЕДУРА dbo.DeleteExpiredSessions @top INT =1000ASBEGIN SET NOCOUNT ON; ДЕКЛАРИРАНЕ @now DATETIME, @c INT; ИЗБЕРЕТЕ @now =GETUTCDATE(), @c =1; ЗАПОЧНЕТЕ ТРАНЗАКЦИЯ; WHILE @c <> 0 BEGIN;WITH x AS ( ИЗБЕРЕТЕ TOP (@top) SessionId ОТ dbo.ASPStateTempSessions WHERE Изтича <@now ORDER BY SessionId ) DELETE x; SET @c =@@ROWCOUNT; АКО @@TRANCOUNT =1 ЗАПОЧНЕТЕ ИЗВЪРШВАНЕ НА ТРАНЗАКЦИЯ; ЗАПОЧНЕТЕ ТРАНЗАКЦИЯ; КРАЙ КРАЙ АКО @@TRANCOUNT =1 ЗАПОЧВАНЕ НА ТРАНЗАКЦИЯТА; ENDENDGOЩе искате да експериментирате с
TOP
в зависимост от това колко зает е вашият сървър и какво влияние има върху продължителността и заключването. Може също да помислите за внедряване на изолация на моментни снимки – това ще наложи известно въздействие върху tempdb, но може да намали или елиминира блокирането, което се вижда от приложението.Също така по подразбиране заданието
ASPState_Job_DeleteExpiredSessions
работи всяка минута. Помислете да наберете това малко назад – намалете графика до може би на всеки 5 минути (и отново, много от това ще се сведе до това колко заети са вашите приложения и тестване на въздействието на промяната). И от друга страна, уверете се, че е активирана – в противен случай таблицата ви със сесии ще расте и ще расте без отметка.По-рядко сесии с докосване
Всеки път, когато се зареди страница (и ако уеб приложението не е създадено правилно, вероятно няколко пъти при зареждане на страница), съхранената процедура
dbo.TempResetTimeout
се извиква, като се гарантира, че времето за изчакване за тази конкретна сесия се удължава, докато те продължават да генерират активност. На натоварен уеб сайт това може да причини много голям обем активност на актуализиране на таблицатаdbo.ASPStateTempSessions
. Ето текущия код заdbo.TempResetTimeout
:ПРОМЕНЯ ПРОЦЕДУРА [dbo].[TempResetTimeout] @id tSessionId КАТО АКТУАЛИЗАЦИЯ [ASPState].dbo.ASPStateTempSessions SET Изтича =DATEADD(n, Timeout, GETUTCDATE()) WHERE SessionId =@id RETURN 0Сега си представете, че имате уеб сайт с 500 или 5000 потребители и всички те лудо щракат от страница на страница. Това вероятно е една от най-често наричаните операции във всяка реализация на ASPState и докато таблицата е натисната на
SessionId
– така че въздействието на всяко отделно изявление трябва да бъде минимално – като цяло това може да бъде значително разточително, включително върху дневника. Ако времето за изчакване на сесията ви е 30 минути и актуализирате времето за изчакване за сесия на всеки 10 секунди поради естеството на уеб приложението, какъв е смисълът да го правите отново 10 секунди по-късно? Докато тази сесия се актуализира асинхронно в някакъв момент преди да изтекат 30-те минути, няма чиста разлика за потребителя или приложението. Така че реших, че можете да приложите по-мащабируем начин за "докосване" на сесиите, за да актуализирате техните стойности за изчакване.Една идея, която имах, беше да внедря опашка от брокер на услуги, така че приложението да не чака действителното записване да се случи – то извиква
dbo.TempResetTimeout
съхранена процедура, а след това процедурата за активиране поема асинхронно. Но това все пак води до много повече актуализации (и регистрационни дейности), отколкото е наистина необходимо.По-добра идея, IMHO, е да внедрите таблица на опашката, в която само вмъквате, и по график (така че процесът завършва пълен цикъл за известно време, по-кратко от времето за изчакване), той ще актуализира само изчакването за всяка сесия, която вижда веднъж , без значение колко пъти са *опитвали* да актуализират времето си за изчакване в рамките на този период. Така че една проста таблица може да изглежда така:
CREATE TABLE dbo.SessionStack( SessionId tSessionId, -- nvarchar(88) - разбира се, те трябваше да използват типове псевдоними EventTime DATETIME, Processed BIT NOT NULL DEFAULT 0); СЪЗДАВАНЕ НА КЛУСТРИРАН ИНДЕКС и НА dbo.SessionStack(EventTime);GOИ тогава бихме променили процедурата за наличност, за да изместим активността на сесиите върху този стек, вместо да докосваме директно таблицата със сесиите:
ПРОМЕНЯ ПРОЦЕДУРА dbo.TempResetTimeout @id tSessionIdASBEGIN SET NOCOUNT ON; INSERT INTO dbo.SessionStack(SessionId, EventTime) SELECT @id, GETUTCDATE();ENDGOКлъстерираният индекс е в
smalldatetime
колона за предотвратяване на разделяне на страници (с потенциална цена на гореща страница), тъй като времето за събитие за докосване на сесия винаги ще се увеличава монотонно.Тогава ще ни е необходим фонов процес за периодично обобщаване на нови редове в
dbo.SessionStack
и актуализирайтеdbo.ASPStateTempSessions
съответно.СЪЗДАЙТЕ ПРОЦЕДУРА dbo.SessionStack_ProcessASBEGIN ЗАДАДЕТЕ NOCOUNT ON; -- освен ако не искате да добавите tSessionId към модела или ръчно към tempdb -- след всяко рестартиране ще трябва да използваме основния тип тук:CREATE TABLE #s(SessionId NVARCHAR(88), EventTime SMALLDATETIME); -- стекът вече е вашата гореща точка, така че влизайте и излизайте бързо:UPDATE dbo.SessionStack SET Processed =1 OUTPUT inserted.SessionId, inserted.EventTime INTO #s WHERE Обработен IN (0,1) -- в случай, че някой неуспешен последен време И EventTimeМоже да искате да добавите повече контрол върху транзакциите и обработка на грешки около това – аз просто представям една нестандартна идея и можете да се побъркате по това, колкото искате. :-)
Може да си помислите, че бихте искали да добавите неклъстериран индекс към
dbo.SessionStack(SessionId, EventTime DESC)
за улесняване на фоновия процес, но мисля, че е по-добре да се съсредоточи дори и най-малките печалби в производителността върху процеса, който потребителите чакат (всяко зареждане на страница), а не върху този, който не чакат (фоновият процес). Така че предпочитам да платя разходите за потенциално сканиране по време на фоновия процес, отколкото да плащам за допълнителна поддръжка на индекса по време на всяко отделно вмъкване. Както при клъстерирания индекс в таблицата #temp, тук има много „зависи“, така че може да искате да поиграете с тези опции, за да видите къде вашата толерантност работи най-добре.Освен ако честотата на двете операции не трябва да е драстично различна, бих планирал това като част от
ASPState_Job_DeleteExpiredSessions
работа (и помислете за преименуване на тази работа, ако е така), така че тези два процеса да не се тъпчат един друг.Една последна идея тук, ако установите, че трябва да мащабирате още повече, е да създадете множество
SessionStack
таблици, където всяка отговаря за подмножество от сесии (да речем, хеширана върху първия знак наSessionId
). След това можете да обработвате всяка таблица на свой ред и да запазите тези транзакции много по-малки. Всъщност бихте могли да направите нещо подобно и за задачата за изтриване. Ако се направи правилно, би трябвало да можете да ги поставите в отделни задачи и да ги изпълнявате едновременно, тъй като – на теория – DML трябва да засяга напълно различни набори от страници.Заключение
Това са моите идеи засега. Бих искал да чуя за вашия опит с ASPState:Какъв мащаб сте постигнали? Какви тесни места сте наблюдавали? Какво сте направили, за да ги смекчите?