让我从描述场景开始。我有一个带有 SQL Server 2008 的 MVC 3 应用程序。在其中一个页面中,我们显示了从数据库返回的产品列表,并且每个登录用户都是唯一的。用于返回产品列表的 SQL 查询(实际上是 VIEW)非常昂贵。
- 它基于非常复杂的业务需求,现阶段无法更改。
- 数据库架构无法更改或重新设计,因为它被其他应用程序使用。
- 有 50k 个产品和 5k 个用户(每个用户可以访问 1 到 50k 个产品)。
为了显示登录用户的产品页面,我们使用:
SELECT TOP X * FROM [VIEW] WHERE UserID = @UserId -- where 'X' is the size of the page
上面的查询最多返回 50 行(最大页面大小)。WHERE 子句将行数限制为最多 50k(用户有权访问的产品)。加载页面大约需要 5 到 7 秒,而这正是上面的 SQL 查询在 SQL 中运行所需的时间。 问题:
用户进入产品页面,很可能使用分页,重新排序结果,进入详细信息页面等,然后返回列表。并且每次显示结果需要5-7s。
这是不可接受的,但同时业务团队已经接受第一次加载产品页面可能需要 5-7 秒。因此,我们想到了CACHING。
我们现在有两个选项可供选择,至少对我而言,最“明显”的一个是使用 .Net 缓存(在内存中/在 proc 中)。(请注意,由于我们的提供商/托管合作伙伴的技术限制,目前不允许分布式缓存)。
但我对此不太满意。我们最终可能会在内存中有很多产品(当有 50 或 100 个用户同时登录时),这可能会导致服务器上的其他问题,例如 .Net 在我们的代码插入新项目时不断删除缓存项目以释放空间。
第二个选项:
这里的主要问题是生成用户 x 产品 x 访问视图非常昂贵,因此我们认为我们可以创建一个平面表(或者换句话说,数据库中所有产品 x 用户的缓存)。该表将完全是视图的结果。但是,如果添加新产品、更改用户权限等,结果可能随时更改。因此我们需要不断刷新表(这可能需要几秒钟),这开始变得有点复杂。
同样,尽管我们可以实现某种缓存提供程序,并且根据用户的请求,我们将运行原始 SQL 查询并从视图中选择产品(5-7 秒,只接受一次)并将结果保存在平面中SQL 中名为 ProductUserAccessCache 的表。下一个请求,我们将通过快速查询从这个缓存表中获取值(因为我们可以很容易地识别为该特定用户缓存的结果),而无需在 SQL 中进行计算。每当添加产品或更改权限时,我们都会截断缓存表,并在新请求时为请求的用户重新填充该表。这对我来说似乎并不太复杂,但我们在这里所做的基本上是创建一个新的缓存“提供程序”。
- 有没有人有这种问题的经验?
- 使用 .Net 缓存(在 proc 中)会更好吗?
- 有什么建议么?