Nhiều người đã triển khai ASPState trong môi trường của họ. Một số người sử dụng tùy chọn trong bộ nhớ (InProc), nhưng thông thường tôi thấy tùy chọn cơ sở dữ liệu đang được sử dụng. Có một số điểm kém hiệu quả tiềm ẩn ở đây mà bạn có thể không nhận thấy trên các trang web có khối lượng thấp, nhưng điều đó sẽ bắt đầu ảnh hưởng đến hiệu suất khi khối lượng web của bạn tăng lên.
Mô hình khôi phục
Đảm bảo ASPState được đặt thành khôi phục đơn giản - điều này sẽ giảm đáng kể tác động đến nhật ký có thể gây ra bởi khối lượng lớn các lần ghi (tạm thời và phần lớn dùng một lần) có khả năng xảy ra ở đây:
ALTER DATABASE ASPState SET RECOVERY ĐƠN GIẢN;
Thông thường, cơ sở dữ liệu này không cần phải được khôi phục hoàn toàn, đặc biệt là vì nếu bạn đang ở chế độ khôi phục sau thảm họa và khôi phục cơ sở dữ liệu của mình, thì điều cuối cùng bạn nên lo lắng là cố gắng duy trì các phiên cho người dùng trong ứng dụng web của bạn - những người có khả năng đã biến mất từ lâu khi bạn đã khôi phục. Tôi không nghĩ rằng mình đã từng gặp phải tình huống mà khôi phục tại thời điểm là cần thiết cho một cơ sở dữ liệu tạm thời như ASPState.
Thu nhỏ / cô lập I / O
Khi thiết lập ASPState ban đầu, bạn có thể sử dụng -sstype c
và -d
các đối số để lưu trữ trạng thái phiên trong cơ sở dữ liệu tùy chỉnh đã nằm trên một ổ đĩa khác (giống như bạn làm với tempdb). Hoặc, nếu cơ sở dữ liệu tempdb của bạn đã được tối ưu hóa, bạn có thể sử dụng -sstype t
lý lẽ. Những điều này được giải thích chi tiết trong tài liệu Chế độ trạng thái phiên và ASP.NET SQL Server Công cụ đăng ký trên MSDN.
Nếu bạn đã cài đặt ASPState và xác định rằng bạn sẽ có lợi khi chuyển nó sang ổ đĩa riêng (hoặc ít nhất là một ổ đĩa khác), thì bạn có thể lên lịch hoặc đợi một khoảng thời gian bảo trì ngắn và làm theo các bước sau:
ALTER DATABASE ASPState SET SINGLE_USER VỚI ROLLBACK NGAY LẬP TỨC; ALTER DATABASE ASPState SET OFFLINE; ALTER DATABASE ASPState MODIFY FILE (NAME =ASPState, FILENAME ='{new path} \ ASPState.mdf'); ALTER DATABASE ASPState MODIFY FILE (NAME =ASPState_log, FILENAME ='{new path} \ ASPState_log.ldf');Tại thời điểm này, bạn sẽ cần phải di chuyển các tệp đến
<new path>
theo cách thủ công và sau đó bạn có thể đưa cơ sở dữ liệu trực tuyến trở lại:ALTER DATABASE ASPState SET ONLINE; ALTER DATABASE ASPState SET MULTI_USER;Cô lập các ứng dụng
Có thể trỏ nhiều hơn một ứng dụng tại cùng một cơ sở dữ liệu trạng thái phiên. Tôi khuyên bạn không nên làm điều này. Bạn có thể muốn trỏ các ứng dụng đến các cơ sở dữ liệu khác nhau, thậm chí có thể trên các trường hợp khác nhau, để tách biệt tốt hơn việc sử dụng tài nguyên và cung cấp tính linh hoạt tối đa cho tất cả các thuộc tính web của bạn.
Nếu bạn đã có nhiều ứng dụng sử dụng cùng một cơ sở dữ liệu thì không sao, nhưng bạn sẽ muốn theo dõi tác động của từng ứng dụng. Rex Tang của Microsoft đã xuất bản một truy vấn hữu ích để xem không gian được sử dụng bởi mỗi phiên; đây là một sửa đổi sẽ tóm tắt số lượng phiên và tổng số phiên / kích thước phiên trung bình cho mỗi ứng dụng:
SELECT a.AppName, SessionCount =COUNT (s.SessionId), TotalSessionSize =SUM (DATALENGTH (s.SessionItemLong)), AvgSessionSize =AVG (DATALENGTH (s.SessionItemLong)) FROM dbo.ASPStateTempSessions AS sLEFT OUTER JOIN dbo. ASPStateTempApplication AS a ON SUBSTRING (s.SessionId, 25, 8) =SUBSTRING (sys.fn_varbintohexstr (CONVERT (VARBINARY (8), a.AppId)), 3, 8) GROUP BY a.AppNameORDER BY TotalSessionSize DESC;Nếu bạn thấy rằng bạn có một bản phân phối lệch ở đây, bạn có thể thiết lập một cơ sở dữ liệu ASPState khác ở nơi khác và trỏ một hoặc nhiều ứng dụng vào cơ sở dữ liệu đó.
Thực hiện xóa thân thiện hơn
Mã cho
dbo.DeleteExpiredSessions
sử dụng con trỏ, thay thế mộtDELETE
duy nhất trong các triển khai trước đó. (Tôi nghĩ điều này phần lớn dựa trên bài đăng này của Greg Low.)Ban đầu mã là:
TẠO THỦ TỤC XóaExpiredSessionsAS DECLARE @now DATETIME SET @now =GETUTCDATE () DELETE ASPState..ASPStateTempSessions WHERE hết hạn <@now RETURN 0GO(Và vẫn có thể xảy ra, tùy thuộc vào nơi bạn tải xuống nguồn hoặc cách bạn đã cài đặt ASPState cách đây bao lâu. Có rất nhiều tập lệnh lỗi thời để tạo cơ sở dữ liệu, mặc dù bạn thực sự nên sử dụng aspnet_regsql.exe.)
Hiện tại (kể từ .NET 4.5), mã trông như thế này (có ai biết khi nào Microsoft sẽ bắt đầu sử dụng dấu chấm phẩy không?).
THỦ TỤC BÁO CÁO [dbo]. [DeleteExpiredSessions] NHƯ ĐẶT SỐ KHOẢN TRÊN ĐẶT KHOÁ KHÓA_PRIORITY THẤP HÓA DANH SÁCH @now datetime SET @now =GETUTCDATE () TẠO BẢNG #tblExpiredSessions (SessionID nvarchar (88) KHÔNG ĐẦY ĐỦ TỪ KHÓA CHÍNH) ) CHỌN SessionID TỪ [ASPState] .dbo.ASPStateTempSessions VỚI (READUNCOMMITTED) Nơi hết hạn <@now IF @@ ROWCOUNT <> 0 BEGIN DECLARE Hết hạn TIẾP THEO TỪ ExpiredSessionCursor INTO @SessionID WHILE @@ FETCH_STATUS =0 BẮT ĐẦU XÓA khỏi [ASPState] .dbo.ASPStateTempSes sions WHERE SessionID =@SessionID AND Expires <@now FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID END CLOSE ExpiredSessionCursor DEALLOCATE ExpiredSessionCursor END DROP TABLE #tblExpiredSessions TRỞ LẠI 0Ý tưởng của tôi là có một phương tiện vui vẻ ở đây - đừng cố xóa TẤT CẢ các hàng trong một lần bị rơi, nhưng cũng đừng chơi từng hàng một. Thay vào đó, hãy xóa
n
các hàng tại một thời điểm trong các giao dịch riêng biệt - giảm thời gian chặn và cũng giảm thiểu tác động đến nhật ký:ALTER THỦ TỤC dbo.DeleteExpiredSessions @top INT =1000ASBEGIN ĐẶT SỐ TÀI KHOẢN BẬT; DECLARE @now DATETIME, @c INT; CHỌN @now =GETUTCDATE (), @c =1; BẮT ĐẦU GIAO DỊCH; WHILE @c <> 0 BEGIN; WITH x AS (CHỌN ĐẦU (@top) SessionId TỪ dbo.ASPStateTempSessions TẠI ĐÓ Hết hạn <@now ORDER BY SessionId) XÓA x; ĐẶT @c =@@ ROWCOUNT; IF @@ TRANCOUNT =1 GIAO DỊCH CAM KẾT BẮT ĐẦU; BẮT ĐẦU GIAO DỊCH; KẾT THÚC KẾT THÚC NẾU @@ TRANCOUNT =1 BẮT ĐẦU GIAO DỊCH CAM KẾT; HẾT HẾTBạn sẽ muốn thử nghiệm với
TOP
tùy thuộc vào mức độ bận rộn của máy chủ của bạn và tác động của nó đến thời lượng và khóa. Bạn cũng có thể muốn xem xét triển khai tính năng cô lập ảnh chụp nhanh - điều này sẽ buộc một số tác động đến tempdb nhưng có thể giảm hoặc loại bỏ việc chặn được nhìn thấy từ ứng dụng.Ngoài ra, theo mặc định, công việc
ASPState_Job_DeleteExpiredSessions
chạy mỗi phút. Cân nhắc quay số lại một chút - giảm lịch trình xuống có thể 5 phút một lần (và một lần nữa, phần lớn điều này sẽ phụ thuộc vào mức độ bận rộn của các ứng dụng của bạn và kiểm tra tác động của sự thay đổi). Và mặt khác, hãy đảm bảo rằng nó đã được bật - nếu không, bảng phiên của bạn sẽ phát triển và phát triển không được kiểm soát.Phiên chạm ít thường xuyên hơn
Mỗi khi một trang được tải (và, nếu ứng dụng web không được tạo đúng cách, có thể nhiều lần mỗi lần tải trang), quy trình được lưu trữ
dbo.TempResetTimeout
được gọi, đảm bảo rằng thời gian chờ cho phiên cụ thể đó được kéo dài miễn là chúng tiếp tục tạo ra hoạt động. Trên một trang web bận rộn, điều này có thể gây ra khối lượng hoạt động cập nhật rất lớn so với bảngdbo.ASPStateTempSessions
. Đây là mã hiện tại chodbo.TempResetTimeout
:THỦ TỤC THAY THẾ [dbo]. [TempResetTimeout] @id tSessionId AS UPDATE [ASPState] .dbo.ASPStateTempSessions SET Expires =DATEADD (n, Timeout, GETUTCDATE ()) WHERE SessionId =@id RETURN 0Bây giờ, hãy tưởng tượng bạn có một trang web với 500 hoặc 5.000 người dùng, và tất cả họ đều đang nhấp chuột điên cuồng từ trang này sang trang khác. Đây có lẽ là một trong những hoạt động được gọi thường xuyên nhất trong bất kỳ triển khai ASPState nào và trong khi bảng được khóa trên
SessionId
- vì vậy tác động của bất kỳ tuyên bố riêng lẻ nào phải ở mức tối thiểu - nói chung điều này có thể gây lãng phí đáng kể, kể cả trên nhật ký. Nếu thời gian chờ phiên của bạn là 30 phút và bạn cập nhật thời gian chờ cho một phiên cứ sau 10 giây do tính chất của ứng dụng web, thì việc làm lại 10 giây sau đó có ích gì? Miễn là phiên đó được cập nhật không đồng bộ vào một thời điểm nào đó trước khi hết 30 phút, không có sự khác biệt thực nào đối với người dùng hoặc ứng dụng. Vì vậy, tôi nghĩ rằng bạn có thể triển khai một cách có thể mở rộng hơn để "chạm" vào các phiên để cập nhật giá trị thời gian chờ của chúng.Một ý tưởng mà tôi có là triển khai hàng đợi của nhà môi giới dịch vụ để ứng dụng không phải đợi quá trình ghi thực sự xảy ra - nó gọi
dbo.TempResetTimeout
thủ tục được lưu trữ, và sau đó thủ tục kích hoạt tiếp quản không đồng bộ. Nhưng điều này vẫn dẫn đến nhiều bản cập nhật (và hoạt động ghi nhật ký) hơn mức thực sự cần thiết.Một ý tưởng tốt hơn, IMHO, là triển khai một bảng hàng đợi mà bạn chỉ chèn vào và theo lịch trình (sao cho quá trình hoàn thành một chu kỳ đầy đủ trong một khoảng thời gian ngắn hơn thời gian chờ), nó sẽ chỉ cập nhật thời gian chờ cho bất kỳ phiên nào. nhìn thấy một lần , cho dù họ đã * cố gắng * bao nhiêu lần để cập nhật thời gian chờ của mình trong khoảng thời gian đó. Vì vậy, một bảng đơn giản có thể trông như thế này:
TẠO BẢNG dbo.SessionStack (SessionId tSessionId, - nvarchar (88) - tất nhiên họ phải sử dụng các loại bí danh EventTime DATETIME, Processed BIT NOT NULL DEFAULT 0); TẠO CHỈ SỐ ĐƯỢC ĐIỀU CHỈNH và TRÊN dbo.SessionStack (EventTime); ĐIVà sau đó, chúng tôi sẽ thay đổi quy trình chứng khoán để đẩy hoạt động phiên vào ngăn xếp này thay vì chạm trực tiếp vào bảng phiên:
THỦ TỤC THAY THẾ dbo.TempResetTimeout @id tSessionIdASBEGIN ĐẶT SỐ TÀI KHOẢN; CHÈN VÀO dbo.SessionStack (SessionId, EventTime) SELECT @id, GETUTCDATE (); ENDGOChỉ mục được phân nhóm nằm trên
smalldatetime
để ngăn chặn việc tách trang (với chi phí tiềm ẩn của một trang nóng), vì thời gian sự kiện cho một lần chạm phiên sẽ luôn tăng một cách đơn điệu.Sau đó, chúng ta sẽ cần một quy trình nền để tóm tắt định kỳ các hàng mới trong
dbo.SessionStack
và cập nhậtdbo.ASPStateTempSessions
theo đó.TẠO THỦ TỤC dbo.SessionStack_ProcessASBEGIN ĐẶT TÀI KHOẢN BẬT; - trừ khi bạn muốn thêm tSessionId vào mô hình hoặc thủ công vào tempdb - sau mỗi lần khởi động lại, chúng tôi sẽ phải sử dụng kiểu cơ sở ở đây:CREATE TABLE #s (SessionId NVARCHAR (88), EventTime SMALLDATETIME); - ngăn xếp hiện là điểm phát sóng của bạn, vì vậy hãy vào và ra nhanh chóng:CẬP NHẬT dbo.SessionStack SET Processed =1 OUTPUT insert.SessionId, insert.EventTime INTO #s WHERE Processed IN (0,1) - trong trường hợp lần trước không thành công time AND EventTimeBạn có thể muốn thêm nhiều quyền kiểm soát giao dịch và xử lý lỗi xung quanh vấn đề này - Tôi chỉ đang trình bày một ý tưởng khác, và bạn có thể làm điều này điên cuồng như bạn muốn. :-)
Bạn có thể nghĩ rằng bạn muốn thêm một chỉ mục không phân cụm trên
dbo.SessionStack(SessionId, EventTime DESC)
để tạo điều kiện thuận lợi cho quá trình chạy nền, nhưng tôi nghĩ tốt hơn là nên tập trung ngay cả những lợi ích hiệu suất nhỏ nhất vào quá trình mà người dùng chờ đợi (mỗi lần tải trang) thay vì quá trình mà họ không chờ đợi (quá trình nền). Vì vậy, tôi thà trả chi phí quét tiềm năng trong quá trình chạy nền hơn là trả chi phí duy trì chỉ mục bổ sung trong mỗi lần chèn. Giống như chỉ mục được phân nhóm trên bảng #temp, có rất nhiều "điều đó phụ thuộc" ở đây, vì vậy bạn có thể muốn sử dụng các tùy chọn này để xem mức độ dung sai của bạn hoạt động tốt nhất ở đâu.Trừ khi tần suất của hai hoạt động cần phải khác nhau đáng kể, tôi sẽ lập lịch trình này như một phần của
ASPState_Job_DeleteExpiredSessions
công việc (và cân nhắc đổi tên công việc đó nếu có) để hai quy trình này không thay đổi lẫn nhau.Một ý tưởng cuối cùng ở đây, nếu bạn thấy mình cần mở rộng quy mô hơn nữa, là tạo nhiều
SessionStack
các bảng, trong đó mỗi bảng chịu trách nhiệm về một tập con các phiên (giả sử được băm trên ký tự đầu tiên củaSessionId
). Sau đó, bạn có thể xử lý lần lượt từng bảng và giữ các giao dịch đó nhỏ hơn nhiều. Trên thực tế, bạn cũng có thể làm điều gì đó tương tự cho công việc xóa. Nếu được thực hiện đúng cách, bạn sẽ có thể đưa chúng vào các công việc riêng lẻ và chạy chúng đồng thời, vì - về lý thuyết - DML sẽ ảnh hưởng đến các nhóm trang hoàn toàn khác nhau.Kết luận
Đó là những ý tưởng của tôi cho đến nay. Tôi muốn nghe về trải nghiệm của bạn với ASPState:Bạn đã đạt được loại thang đo nào? Bạn đã quan sát thấy những loại nút thắt cổ chai nào? Bạn đã làm gì để giảm thiểu chúng?