从以前的 “classic” ASP 到目前的 ASP.NET 4.5 Web Forms,很多开发者依赖ASP.NET 会话状态作为重要的临时保存每个用户的数据的主要手段。 它的特征是在用户访问web应用程序的过程中,允许开发者存储和读取用户的数据。 会话数据是自动从存储中持续保存和恢复,并且自动过期删除。

问题

使用Session State的替代方案这些内容超出了本文的范围。 对于依赖Session State的应用程序也有陷阱,最常见的是访问每个用户,每个请求的Session基础数据。这种独特的访问是维持Session State的一致性的一种方式,并且是通过设计实现了这种方式。 如果你对这样残暴细节的设计感兴趣, 他们在题名为“锁定会话状态数据”章节这里解释了。会话状态常见于ASP.NET Web窗体应用程序,而ASP.NET MVC在较小程度上使用了TempData(POST数据到GET作为例子) 。

Web应用程序主要利用Session状态来协调彼此间的工作。作为对比,客户端脚本较多的重量级web应用通常拥有更高的并发请求,这种情况下使用Session状态访问资源需要对Session加锁和解锁,从而成为了Web应用的瓶颈。不限制类型的Web应用将会成为另外一个瓶颈因为需要足够的存储空间维持它们的会话的状态。有三种途径优化Session状态的访问,使得一些请求可以不需要session或者使用只读的,但是如果加载后应用规模持续增大,最终仍然会有瓶颈。

目前的状况

基于这些考虑目前的ASP.NET的会话状态仍然使用的非常普遍。在许多领域我不断看到许多消费者在有大量扩展的Web应用中使用会话状态。对大量企业级用户而言,内部使用ASP.NET窗体应用更为普遍。对于这些消费者而言,如何选择Session State存储提供商是相当关键的。这些提供商必须将Session字典的内容序列化存储在耐用的设备上并反序列化从中提取数据(通常使用BLOB应用程序)。这里有许多提供商可供选择,包括Microsoft和第三方开发者提供的工具。目前Microsoft提供了下面的Session存储工具,假定ASP.NET应用部署在企业内部:

Session Provider

Can be Highly Available?

Can be Geo Redundant?

Can be used in Web Farms?

Performance?

In-Proc

No

No

No

Excellent

State Server

No

No

Yes

Good

SQL Server (Traditional)

Yes

Yes

Yes

Fair

AppFabric Caching

Yes

No

Yes

Good

SQL Server (In-Memory)

Yes*

Yes

Yes

Excellent


*需要在 in-memory表中将模式和数据标记为持久的

如果你的应用程序需要Session State高可用,同时支持跨web farm的部署,你可以从Microsoft提供的选项,仅限于SQL Server或者AppFabric Caching.SQL Server有一个增加的优势,它可以跨越数据中心来提供地理冗余(geo-redundancy).而AppFabric则受限于单一数据中心.实际应用中,这两种解决方案都工作良好.但是,传统的SQL Server实现常常遇到瓶颈,产生的原因是基于单一磁盘的表存在竞争.竞争导致阻塞,死锁,或者其它不友好的变化.这影响了它存储和恢复会话的时间.另外,在删除操作过程中,当先前的会话数据由于锁的扩大和竞争的延续而清除了,这也会存在问题.

SQL Server 2014的新选项

为了解决老版本SQL Server安装包的性能问题,SQL Server团队最近发布了新的安装包“Microsoft ASP.NET Session State provider for SQL Sever In-Memory”作为NugGet包.在this case study中有这个安装包不可思议的性能提升的证明.它在ASP.NET应用程序中使用Session State,每秒处理250,000个请求!这个新实现使用了SQL Server 2014称为"Hekaton"的内存优化表特性.这需要这个产品的2014版本.这个安装包如何在老版本SQL Server会话状态安装包上有所提升的呢?

1. 会话存储的持续性是通过内存优化表而不是磁盘表.对于繁重的访问模型,如存储会话状态等,内存优化表是全事务的,可持续性和理想的.这类表使用无锁数据结构和优化的,多版本的并发控制.

2. 为了更进一步提升性能,可用本地编译存储过程来恢复和存储会话数据. 本质上,这是一种新型的存储过程,它被编译为本地机器代码.

3. 这两个SQL 2014产品特性,解决了主要的性能和竞争问题,这些问题存在于基于磁盘实现的旧的传统的SQL Server安装包.安装和配置这个程序是相当直接的.通过NuGet包管理控制台,可以按照下面的方式安装:   
   
Install-Package Microsoft.Web.SessionState.SqlInMemory.  

    在你的应用程序中,NuGet包将增加一个到Microsoft.Web.SessionState.SqlInMemory的引用,同时也会增加一个名为ASPStateInMemory.sql的脚本文件来安装SQL Server 2014 Session State数据库.这个文件包含了必须的DDL来安装数据库.在SQL脚本中有一些项,你想要审查或者最可能审查或修改的:

1. 这个数据库的名字默认是ASPStateInMemory.

2. 数据库的主文件组路径.

3. 数据库的MEMORY_OPTIMIZED_DATA文件组路径.

4. BUCKET_COUNT的大小信息,它基于会话中的项的预期大小.

5. 让会话中的表可持久或非可持久的决定(涉及到是否需要会话高可用)

    以上的第五个部分,需要对已经存在的SQL Server会话数据库作一些分析,它可能像计算传统ASP.NET SQL Server会话模式里BLOB列的DATALENGTH()一样简单.对于InProc或者StateServer来说,决定会话项的平均大小会更加困难,但可以通过捕获w3wp.exe或者StateServer进程的内存dump来实现,其间检查在会话字典中的项的数目和大小.对于InProc或者StateServer,关于会话中的项的数目,有性能计数.最好的建议总是测试和调整.

让基于内存的会话高可用

默认,SQL Server 2014基于内存会话的内存优化表是标记为非持久的.这意味着,这些表里的数据变化是过渡性的一致.这些变化没有记录在日志中,这意味着如果SQL Server重启了,服务器重启了或者任何形式的故障恢复发生(FCI或者AlwaysOn),所有的会话数据将丢失.设置这个默认值是因为性能.为了让这些内存优化表可持续,需要在ASPStateInMemory.sql脚本中做三点改变.在脚本中有一些注释解释了为何需要做这些改变.

按下面的方式修改SessionItems表. 

1. 修改WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY)

2. 为WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_AND_DATA)

3. 取消语句的注释(注意最后的逗号): Id bigint IDENTITY,

4. 取消语句的注释(注意最后的逗号,根据需要修改1000000 * 2为real值,在这个语句前读T-SQL注释以选择一个起始值): CONSTRAINT [PK_SessionItems_Id] PRIMARY KEY NONCLUSTERED HASH (Id) WITH (BUCKET_COUNT = 2000000),          

修改会话表

1. 修改WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY

2. 为WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_AND_DATA)

    只要做了这些修改,我们可以让数据库成为部分SQL Server AlwaysOn可用性组.当故障恢复时,会话数据将会保留.由于增加的重试逻辑,所以,当一个自动的或者人为的故障恢复发生时,连接池中的过期连接不会产生异常抛出给终端用户.

请注意,即使我们设置表为非可持续的,将会话数据库放入SQL Server AlwaysOn可用性组,但会话表中的数据在复制(只有schema可用)时不可用.对于客户负荷,这个"schema only"复制模型,通过使用非可持续内存优化表,已足够来保证性能提升.

这个最简单的高可用的拓扑对SQL Server In-Memory来说是最合适的,它与下面的类似:

1. 位于子网(数据中心)A的SQL Server 2014 Node 1

2. 位于子网(数据中心)B的SQL Server 2014 Node 2

3. 位于子网(数据中心)C的文件共享

这个拓扑提供了地理冗余,自动故障恢复和维持了1/3的数据中心完整的丢失连接.Windows Server 2012 R2的动态特性,使得自动维持2个数据中心的丢失连接成为可能.(最后的男人的场景[last man standing scenario]).

ASP.NET 配置文件

在 ASP.NET web 应用程序的配置文件web.config中,配置一个新的provider,并且按照下面编辑它。

<sessionState mode="Custom" customProvider="SqlInMemoryProvider">   
  <providers>    
    <add name="SqlInMemoryProvider"     
         type="Microsoft.Web.SessionState.SqlInMemoryProvider"     
         connectionString="Data Source=AGAspNet; Initial Catalog=ASPStateInMemory;Integrated Security=True;" />    
  </providers>    
</sessionState>

在上面的代码片段中, ‘AGAspNet’  是SQL Server 2014 中永远可用的监听者名字。

一个快速的例子

使用ASP.NET网页表单4.5应用,并在会话中编写简单的带有时间戳的字符串,SQL Server 2014中便生成如下的数据:

注意在SQLNode1-2014中,AspStateInMemory数据库的位置.接下来,我们手动进行故障恢复可用性组.

在SQLNode2-2014上,会话现在可用了,而且不会干扰ASP.NET应用程序.简单的敲击web应用程序的F5,以获取来自会话的数据,而不会向客户端抛出异常.

 

过期会话会怎么样呢?
 

在旧的SQL Server会话中,一个SQL Agent作业创建后用来删除过期会话.在新版本中,提供了一个必须被作业调用的存储过程[dbo].[DeleteExpiredSessions].默认,会话超时时间为20分钟.每次一个会话项被访问,超时被重置以保持用户会话“存活”.

概述

在新的会话状态中,有许多有意思的细节.我鼓励你为自己而深入研究代码.你将会发现它是一段奇妙的学习旅程,其间是关于SQL Server 2014 基于内存的OLTP "Hekaton"特性的性能和限制.一个特别的属性包含在代码中,以模拟内存中存储BLOB类型的数据.内存优化表现在不支持BLOB类型.序列化的会话字典和可能的大BLOB数据类型有什么不同呢?其中使用的预处理程序(sprocs)将序列化的会话分拆为7000字节的数据块,以增强大的会话项数据的存储.

精明的读者可能已经发现,在我的屏幕截图中, [SessionItems]表中并没有数据行,但在[Sessions]表中有一行数据.如果我的会话内容超过了7000字节,你应该会在[SessionItems]表中看到"溢出"(spill over)行.关于这方面,在ASP.NET会话存储之外,有许多其它潜在的应用程序,我很可能在下一篇文章中深入挖掘这点.

本地编译存储过程也值得一看.有一些技巧来处理本地编译存储过程的限制,如缺少CASE语句的支持.这个限制是因为,只要预处理程序(sproc)编译为本地代码,是不允许进行分支的!

如果你在考虑使用这个新特性,有如下关键点和问题需要考虑:

1. 内存优化表由内存来支撑!你的SQL Server在峰值负载时,有足够的内存来包含所有的会话数据吗?

2. 默认表是非可持久的.请仔细考虑你的高可用性的需求.不管是可持久还是非可持久,性能将超出你现在使用的传统SQL Server数据库的会话和模式.

阅读SQL文件中的注释,调整BUCKET_COUNT为非簇集HASH索引.以下是SessionItems表DDL语句的代码片段.