1. 堆叠切换用户介绍
堆叠切换用户(Stacked User Switching)是指多个用户共享同一个SQL Server进程,且每个用户都有一个堆栈,SQL Server在不同的用户之间切换,并在堆栈之间进行切换。MSSQL中的堆叠切换用户功能被称为SQL Server众多强大功能之一,是实现用户身份验证以及管理数据库访问控制的重要手段。
2. 堆叠切换用户的优点
2.1 减少内存使用
MSSQL采用堆叠切换用户机制,降低了内存使用率,并提高了数据库服务器的性能。相对于已经关闭的会话进程拥有的内存空间,堆叠切换用户可以在相当长时间内不进行清除,而仅仅切换用户。这种方式使得MSSQL可以在不占用过多内存的条件下执行多个并发会话,从而避免了频繁创建会话的额外开销。
2.2 解决内存泄漏问题
在没有堆叠切换用户的情况下,当存在大量并发请求时,SQL Server可能会出现内存泄漏。因为每个请求都产生自己的进程,内存耗费大。而堆叠切换用户使得请求可以共享SQL Server进程,从而大大降低了内存消耗。
2.3 提高数据访问速度
由于堆叠切换用户可以共享SQL Server进程,因此可以减少服务器之间跨越的通信和数据备份。此外,堆叠切换用户还可以使不同的用户并发地访问服务器,提高了数据的访问速度。
3. 如何使用堆叠切换用户
堆叠切换用户功能由SQL Server自动进行,无需配置或手动启动。只需要在开启会话之后,就可以通过“切换用户”命令将服务器切换到不同的堆栈中,并在不同的堆栈中执行操作。
4. 堆叠切换用户的实现原理
MSSQL堆叠切换用户是通过“上下文切换”(Context Switching)实现的,即将运行的线程从一个堆栈切换到另一个堆栈,使不同连接共享同一个SQL Server进程。上下文切换是Linus操作系统中的一个概念,指在操作系统内核中用于上下文的保存和还原,以实现多任务交替执行。
CREATE PROCEDURE dbo.uspGetBillOfMaterials @StartProductID
AS
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
DECLARE @CheckDate datetime;
SET @CheckDate = GETDATE();
WITH n( n ) AS (-- base case
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9),
r3( n ) AS (-- numbers 0-999
SELECT 0
FROM n AS deka
UNION ALL
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM n AS uni
CROSS JOIN n AS deka
CROSS JOIN n AS hecto),
cteBOM( ProductAssemblyID, ComponentID, PerAssemblyQty, StandardCost, ListPrice, BillOfMaterialsLevel, RecursionLevel ) AS (
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
CASE
WHEN bc.StandardCost IS NOT NULL THEN bc.StandardCost
ELSE ISNULL(b.StandardCost, 0)
END,
CASE
WHEN bc.ListPrice IS NOT NULL THEN bc.ListPrice
ELSE ISNULL(b.ListPrice, 0)
END,
0 AS BillOfMaterialsLevel, 0 AS RecursionLevel
FROM Production.BillOfMaterials AS b
LEFT OUTER JOIN Production.ProductCostHistory AS bc ON bc.ProductID = b.ComponentID AND bc.StartDate <= @CheckDate
WHERE b.ProductAssemblyID = @StartProductID
AND b.EndDate is null
AND b.ComponentID is not null
UNION ALL
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
CASE
WHEN bc.StandardCost IS NOT NULL THEN bc.StandardCost
ELSE ISNULL(b.StandardCost, 0)
END,
CASE
WHEN bc.ListPrice IS NOT NULL THEN bc.ListPrice
ELSE ISNULL(b.ListPrice, 0)
END,
BOI.BillOfMaterialsLevel + 1, cte.RecursionLevel + 1
FROM Production.BillOfMaterials AS b
LEFT OUTER JOIN Production.ProductCostHistory AS bc ON bc.ProductID = b.ComponentID AND bc.StartDate <= @CheckDate
JOIN cteBOM AS BOI ON BOI.ComponentID = b.ProductAssemblyID
JOIN cteBOM AS cte ON cte.ProductAssemblyID = b.ComponentID
WHERE b.EndDate is null)
SELECT DISTINCT b.ProductAssemblyID,
p.Name AS ProductAssemblyName,
b.ComponentID,
c.Name AS ComponentName,
b.PerAssemblyQty,
b.StandardCost,
b.ListPrice,
b.BillOfMaterialsLevel,
b.RecursionLevel
FROM cteBOM AS b
JOIN Production.Product AS p ON p.ProductID = b.ProductAssemblyID
JOIN Production.Product AS c ON c.ProductID = b.ComponentID
ORDER BY BillOfMaterialsLevel, ProductAssemblyName, ComponentID, RecursionLevel;