【问题标题】:Create an in-memory readonly cache for data stored in SQL Server为存储在 SQL Server 中的数据创建内存中只读缓存
【发布时间】:2016-02-10 16:51:58
【问题描述】:

我有一个关于应用程序性能的问题:我有很多表,每个表都有数百万条记录。我在不同的标准(由用户在运行时指定)使用连接、where 子句和 orderby 对它们执行 select 语句。我想对我的记录进行分页,但无论我如何处理我的 SQL 语句,我都无法达到直接从内存中获取页面的性能。基本上,当我必须使用一些运行时动态指定标准过滤我的记录时,问题就出现了。我尝试了所有方法,例如将 ROW_NUMBER() 函数与“where RowNo between”子句结合使用,我尝试过 CTE、临时表等。这些 SQL 解决方案只有在我不包含过滤的情况下才能正常运行。还要记住,我希望我的解决方案尽可能通用(想象一下,我的应用程序中有几个列表,这些列表实际上显示了分页的数百万条记录,并且这些记录是用非常复杂的 sql 语句构造的)。

我所有的表都有一个 INT 类型的主键。

所以,我有一个想法:为什么不只为 select 语句创建一个“服务器”。服务器首先从所有表中加载所有记录并将它们存储到一些 HashSets 中,其中每个 T 具有一个 Id 属性,GetHashCode () 返回该 Id,并且实现 Equals 使得只有当 Id 相等时两个记录才“相等”(不要不要尖叫,稍后您会看到为什么我不使用所有记录数据进行散列和比较)。

到目前为止一切顺利,但有一个问题:如何将内存中的集合与数据库记录同步?我的想法是我必须找到一个解决方案,例如我只加载差异变化。因此,我为每个要缓存的表发明了一个变更日志表。在这个变更日志中,我只执行标记脏行(更新或删除)的插入,并记录新插入的 id,所有这些机制都是使用触发器实现的。因此,每当内存选择出现时,我首先检查是否必须同步某些内容(通过查询更改日志)。如果必须应用某些内容,我会加载变更日志,将这些更改应用到内存中,最后我正在清除该变更日志(或者可能还记得我应用的最高变更日志 id 是什么……)。

为了能够在 O ( N ) 中应用变更日志,其中 N 是变更日志的大小,我正在使用这个算法:

for each log.
identify my in-memory Dictionary <int, T> where the key is the primary key.

if it's a delete log then call dictionary.Remove (id) ( O ( 1 ))
if it's an update log, then call also dictionary.Remove (id) ( O (1))  and move this id into an "to be inserted" collection
if it's an insert log, move this id into a  "to be inserted" collection.

 finally, refresh cache by selecting all data from the corresponding table where Id in ("to be inserted").

为了过滤,我将一些表达式树编译成 Func , bool > functors。使用这种机制,我的执行速度比 SQL 快。

我知道 SQL 2012 具有缓存支持,新的即将推出的 SQL 版本将支持更多,但我的客户端有 SQL Server 2005,所以......我无法从这些东西中受益。

我的问题:你怎么看?这是个坏主意?有更好的方法吗?

【问题讨论】:

标签: c# sql sql-server caching memory


【解决方案1】:

SQL Server 的开发人员做得非常好。我认为这是相当不可能的。

除非您的数据具有某种可能有助于加快处理速度并且优化器无法意识到的隐式结构,否则这种“我自己做快速技巧”的方法将无济于事 - 通常...

性能问题总是在发生的地方首先得到解决:

  1. 表结构和关系
  2. 索引和统计数据
  3. SQL 语句的质量

如果设计和查询都很好,即使是数百万行也没有问题...

如果您的查询进行大量计算,或者您需要从棘手的结构中检索数据(具有递归读取的嵌套列表、XML...),我会使用 Data-Warehouse-Path 并编写一些非规范化表快速选择。当然,您将不得不处理这样一个事实,即您正在阅读“旧”数据。如果您的数据没有太大变化,您可以立即触发对非规范化结构的所有更改。但这要看你的实际情况。

如果您愿意,您可以将您的一个非执行查询连同相关的结构详细信息一起发布并要求审核。 Stack-Exchange 上有专门的小组,例如“代码审查”。如果不是很大,你也可以在这里试试……

【讨论】:

  • 感谢您的回复。基本上,一个复杂的查询几乎可以立即抓取一个页面,但是当我必须处理过滤时(想象一个具有标题行的 GUI 表,并且对于每一列,您可以使用“like”模式搜索)我的执行速度非常慢(检测剩余的具有过滤组合的数据以及检测当前页面等)的想法是,由于明显的原因,我无法从我的结果中索引每一列
  • @GeorgeLica 您的情况非常具体。你所描述的可能是一件好事。在不了解您的情况的情况下,我只能给出一些一般性提示,例如:您可能将大 RAM 用作 RAM 磁盘,您可能将 SSD 用于只读目的,您可能会构建某种索引结构,您可能会考虑连接和全文搜索。尤其是LIKE是个坏事,如果你不从头开始搜索的话。您可能会考虑永久保存的语音等效项(例如soundex)。祝你好运(你可能会考虑投票 :-)))
  • 我忘了提到我的客户有 sql 2005 express :D 所以我的计算能力仅限于单个处理器。为只读数据搜索创建服务器缓存有一个好处:我正在将创建索引和数据结构的工作转移到另一台机器上,我可以在该机器上随意扩展。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
相关资源
最近更新 更多