波士顿代码营 27

使用分布式缓存扩展 .NET 应用程序

伊克巴尔汗
总裁兼技术传播者

由于事务负载的增长,您的 .NET 应用程序可能会遇到数据库或存储瓶颈。 了解如何使用分布式缓存消除瓶颈并扩展您的 .NET 应用程序。 本次演讲涵盖:

  • .NET 应用程序中可扩展性瓶颈的快速概览
  • 分布式缓存的描述以及它如何解决性能问题
  • 您在哪里可以在应用程序中使用分布式缓存
  • 分布式缓存中的一些重要特性
  • 使用分布式缓存的动手示例

我的名字是伊克巴尔汗。 我是一名技术布道者 Alachisoft. 我们是一家位于旧金山湾区的软件公司。 我个人在坦帕,但我们有这个产品叫 NCache. 它是一个 .NET 分布式缓存。 今天我将讨论如何使用分布式缓存扩展 .NET 应用程序。 这不是谈论 NCache,它是关于可伸缩性问题以及如何通过分布式缓存解决它。 你该怎么办? 你应该如何使用它? 有哪些最佳实践? 如果您有任何问题,请随时阻止我。 因此,我们可以进行更多的互动讨论。 让我开始吧。

什么是可扩展性?

因此,让我们先了解一些定义。 第一是可扩展性。 可扩展性不是性能。 您可能有一个应用程序对五个用户执行得非常好,但只有当它对 5,000 或 50,000 或 500,000 个用户执行相同的方式时,它才具有可扩展性。 因此,可扩展性实际上是峰值负载下的高性能。 如果您的应用程序在 5 个用户时表现不佳,那么您需要参加一些其他会谈。

线性可扩展性和非线性可扩展性

线性可扩展性更多的是应用程序部署概念。 当您部署应用程序时,如果您可以在部署中添加更多服务器,并且如果您可以以线性方式增加事务容量,则通过添加更多服务器,那么您的应用程序架构是线性可扩展的,否则它是非线性可扩展的。

线性可伸缩性

这意味着,在一定数量的服务器之后,添加更多服务器实际上会使事情变得更糟,这意味着您的应用程序中的某个地方存在一些瓶颈,这些瓶颈并不能仅仅通过添加更多服务器来解决。 您绝对不想要非线性可伸缩性,并且您肯定想要应用程序中的线性可伸缩性。

线性可伸缩性

什么类型的应用程序需要可扩展性?

什么类型的应用程序需要可扩展性? 这些是 ASP.NET 应用程序。 这些是网络服务。 这些是物联网后端,主要是网络服务。 这些是大数据处理应用程序,在 .NET 中并不常见,越来越多的应用程序是在 Java 中完成的,但这些应用程序也需要可伸缩性和任何其他服务器应用程序。 例如,您可能有,您可能正在为一家拥有数百万客户的大型银行工作,而他们,您知道,客户打电话来更改地址,或者他们可能想要发行一张新卡,或者他们正在从一张银行转账资金为了合规目的,所有这些交易都需要在某个预定的时间范围内处理。 因此,您再次必须在一个时间范围内处理数百万个请求,而您可能无法通过单台计算机完成这些请求。 因此,即使在这种情况下,您也需要能够扩展。 因此,如果您有任何需要可扩展性的应用程序,那么这是一个正确的话题,您来了。

可扩展性问题

所以,让我们也定义可伸缩性问题。 什么是可扩展性问题? 好吧,应用程序层以线性方式扩展。 您可能有一个 Web 应用程序 ASP.NET 或 Web 服务。 您将在其前面有一个负载平衡器,它将流量均匀地路由到所有服务器。 您可以添加更多服务器,没问题。 一切正常。 我早些时候与某人谈论数据库成为瓶颈,他们不同意我的看法。 所以,我认为这是一个进行讨论的好机会。

因此,数据库本身的执行速度非常快。 性能没有问题。 数据库非常智能,它们在自己的服务器中进行大量内存缓存,但可扩展性是它们在设计上无法实现的,因为您可以添加,例如,您可以添加 10、20、30、40、50 台服务器层。 我们的客户在其应用层中拥有 50 台或更多服务器,因为他们拥有如此多的用户。 想象一下,您是一家大型银行或一家航空公司。 您有数以百万计的人访问您的网站。 你已经为一张去夏威夷的机票或其他什么东西做了这个重要的促销活动。 每个人都会来,他们会搜索航班,他们想买机票。 您可以在这里添加更多服务器没问题。 数据库执行速度超快,软件很棒,但设计数据库不是分布式数据库。 它保存在一个地方。 也许你可以拥有一个由两台服务器组成的集群,主动-主动,主动-被动,但它们更多的是为了可靠性而不是可扩展性,真的。

所以,你不能真的在这里有 10 台服务器,除非它是 NoSQL database, 那么你也能。 但是,对于关系数据库,它们中的任何一个都可以是 SQL server、Oracle、Db2、MySQL,它们都非常快。 他们甚至可能在内存处理方面做了很多工作,但随着您需要越来越多地扩展,它们将成为瓶颈。 基本上,当您有数千个或更多同时或并发用户时,您会开始注意到性能问题,并且随着您从 1,000 到 5,000、5,000 到 10,000 的增长,您会开始看到更多。 所以,我们看到的是,你知道,如果你有 1000 个用户,你可能会也可能不会使用缓存,但是当你增长到 5,000、10,000、15,000 个用户时,你肯定会感到痛苦,并且最终会获得这些可扩展性瓶颈。

现在关系数据库或大型机遗留数据库都有这个问题。 这是原因之一 NoSQL database运动的开始就是因为这个。 虽然,它还有其他好处,我们有一个产品。 让我快点来作为参考。 所以,我们有一个缓存产品,但我们也有一个 NoSQL 文档数据库。 就像文档数据库一样,它是一个开源数据库。

所以, NoSQL database 绝对不存在可扩展性问题。 但是,这并不总是答案。 这是为什么? 因为它是一个答案,一个 NoSQL database 希望您将所有数据存储在其中。 因此,除非您将数据存储在 NoSQL database 这意味着您至少停止对那部分数据使用关系数据库,它不会解决您的问题。 所以,在很多情况下你可以移动一些数据,很多数据到一个 NoSQL database 这就是推动 NoSQL 移动。 但是,关系数据库、遗留大型机数据,它们将继续存在。 出于技术和商业原因,您不能只希望它们消失。 因此,无论您要解决什么可伸缩性问题,您都必须使用图中的关系数据库来解决它们。 这就是为什么 NoSQL database 并不总是答案。

解决方案:内存中分布式缓存

而且,您解决该问题的方法是,这是在过去五到十年左右出现的一种新的最佳实践,即内存中分布式缓存。 有些人也称其为内存数据网格。 微软曾经称数据结构。 虽然,他们不断改变他们的定义。 但是,它是内存中的分布式键值存储,它现在是您架构的重要组成部分。 如果您想要一个可扩展的应用程序,您需要编程,您需要在开发应用程序时考虑到分布式缓存。 因此,您可以确保应用程序永远不会成为瓶颈。

线性可伸缩性

现在什么是分布式缓存? 它本质上是两台或更多台低成本服务器。 这些不是高端数据库服务器,它们是 Web 服务器类型的盒子。 通常双 CPU,8 核配置是非常典型的。 16 gig 的 RAM 几乎是平均水平。 我们看到 16 到 32 场演出就像是记忆中的甜蜜点。 超过 64 场演出,我们甚至不向我们的客户推荐。 我们说添加另一个盒子而不是让这个盒子更坚固。 因为,如果添加超过 64 gig,.NET 应用程序就会有一个称为垃圾收集器的东西,它需要大量的处理能力。 因此,您拥有的内存越多,CPU 必须越快,垃圾收集器才能收集所有内存。

因此,拥有更多的服务器比拥有真正高端的少数服务器要好。 当然至少 2 个,因为您需要可靠性。 如果任何服务器出现故障,但在 4 比 1 或 5 比 1 的比率之后,那么,通常你会在应用程序层中拥有 2、3、4 或 5 台服务器。 我们看到的最常见的是 4 到 10 之间,当然还有很多客户使用超过 10 的,但 4 和 10 几乎就是我们所看到的最佳位置。 并且,为此您使用 2 个服务器集群。 当你增长超过 10 或 12 个时,你会添加第三个或第四个,依此类推。 而且,因为分布式缓存是键值存储,所以它是分布式的。 它以智能方式在多台服务器之间复制数据,因此您可以获得可靠性,但同时您不会花费大量时间进行复制。 例如,如果每条数据都必须复制到所有服务器,那就是您不需要的大量额外处理。 所以,它需要做的就是这种类型的智能复制。 有时,您确实需要以这种方式进行复制,但这些都是特殊情况。 因此,当您拥有分布式缓存时,您将有大约 80% 的时间使用它。 所有的读取都将从它完成,所有的更新和一些读取都将在数据库和缓存中完成。 因此,如果您可以将大约 80% 的数据库流量捕获到缓存中,那么您的数据库突然变得非常轻量。 它没有很多事情要做。 因此,无论它必须做什么,它都会做得更快。 因此,性能和可扩展性都会提高。

这个内存中的分布式缓存,无论是 NCache, 是否是 Redis 在 .NET 方面,在 Java 方面有更多的参与者,无论您选择哪种产品,分布式缓存几乎都是现在给定的最佳实践。 您必须将其合并到您的架构中。

在我继续之前,到目前为止有什么问题吗? 所以,是的,一个逻辑缓存可以跨越多台服务器。 的情况下 NCache 您可以在同一台服务器上拥有多个缓存。 因此,出于隔离目的,每个缓存都成为自己的容器,但任何一个缓存都可以跨越所有五台服务器。 当我说跨度时,它意味着一些数据在一台服务器上,一些在其他服务器上,你知道,然后复制是它的一个单独部分,可能会或可能不会根据你的喜好来完成。 但是,是的,所有服务器都用于存储该缓存,并且缓存必须是一致的。 它必须始终正确,或者您可以说它最终必须正确,但非常接近始终正确。 无论你在缓存中存储什么,如果你更新,你并不关心数据在哪里,你可能甚至不知道数据在哪里。 你所知道的是这个集群有我的数据,每当我获取数据时,我可能已经从这个服务器存储了它,我试图在之后立即在这里读取它,我应该能够得到相同的副本。 如果我能做到这一点,那么这将成为一个一致或连贯的缓存。 因此,它始终确保更新的数据完整性。

这是您的偏好,我将根据您实际使用缓存的方式进行更详细的讨论。 所以,你应该做的一件事……我的意思是到目前为止的主要目标是说服你,如果你想要可扩展性,你需要将它作为你架构的一部分。

分布式缓存的常见用途

因此,既然我们已经同意您需要分布式缓存,那么首先想到的问题是您如何使用它? 你在哪里使用它? 你用它来做什么? 其中保存了哪些数据以及与该数据相关的所有问题。

应用程序数据缓存

因此,存在三个高级用例。 第一个是应用程序数据缓存,这就是我刚才所说的,即你在数据库中有数据,你获取它并缓存它。 因此,在应用程序数据缓存中,目标是……但我刚刚提到您不想访问数据库太多,您希望减少数据库流量,以便您的应用程序可以扩展。 但是,数据现在存在于两个地方。 它存在于缓存中,它存在于数据库中。 那么,每当数据存在于两个地方时,首先想到的问题是什么? 两者之间同步。 那么,是否一致? 因为,如果数据不一致,那么您将仅限于将缓存用于只读数据。 事实上,当你问大多数人时,你用缓存做什么? 膝跳反应是只读数据。 你知道,对于任何会在实时或运行时发生真正变化的数据,我都不愿意使用缓存。 但是,如果您不能将缓存用于事务数据,我将术语用于频繁更改的数据,那么数据可能占您数据的 80% 到 90%。 所以,这意味着 80% 到 90% 的时间你甚至不能使用缓存,如果你甚至不能使用缓存,那么它就像一个 NoSQL database. 你知道,这并不总是答案。 而且,你希望它是答案,所以你知道,所以一个好的分布式缓存必须让你相信你缓存的任何东西都将与数据库保持一致。 如果不是,那么该缓存不适合您。 所以,这是第一件事,这是第一个用例。

ASP.NET 特定缓存

第二个用例是,如果您有 ASP.NET 应用程序。

  1. ASP.NET 会话缓存

    您可以将会话存储在缓存中。 您知道,几乎所有 ASP.NET 应用程序都有会话。 它们要么保持在 In-Proc 模式,要么保持在 SQL Server 模式。 SQL Server 不是保持会话的正确位置,原因有两个,当然,第一个原因是您在数据库中保存的数据越多,您的可伸缩性瓶颈就越大。 第二个是会话被保存为 blob,而关系数据库并不是真正为 blob 设计的。 它们是为您可以索引和搜索的更多结构化数据而设计的。 然而,分布式缓存键值,值始终是一个 blob。 因此,这些会话非常适合分布式缓存。

  2. ASP.NET View State 高速缓存

    第二种类型的 ASP.NET 数据是,如果你没有使用很多现有的 ASP.NET 应用程序仍然没有的 MVC 框架,那么你有一个叫做 查看状态 而且,视图状态只不过是一个加密的字符串,它被保存并从 Web 服务器发送到浏览器,然后返回到 Web 服务器,它可以变得非常大。 它可以轻松达到 100 千字节,因此会消耗大量额外带宽。 旅行需要更多时间。 当您将其乘以您正在处理的数百万个请求时,您的带宽消耗成本也会上升很多。 因此,这是在服务器端进行缓存并且只发送一个小密钥的理想情况。 因此,下一次应该使用 View State 时,会发回密钥并从缓存中获取 View State。

  3. ASP.NET 输出缓存

    ASP.NET 特定缓存的第三个示例是页面输出。 它是 ASP 的一部分.NET framework 称为输出缓存,它允许您缓存页面的输出(如果它不会更改),您可以使用分布式缓存来缓存它,而不是将其单独保存在每个 Web 服务器中,然后变成需要同步的多个副本。 因此,这三个用例或三个示例适用于 ASP.NET 特定的缓存用例。

现在,与此处的应用程序数据缓存不同,问题的性质非常不同。 这里的数据只存在于缓存中。 我的意思是所有这些数据不再存储在其他任何地方。 因此,它没有存储在数据库中。 因此,您不再有数据库和缓存之间的同步问题。 但是,你现在有另一个问题。 如果你有一个内存缓存,那是你数据的唯一存储。 最大的担忧是什么? 那有什么问题呢? 它消失了。 是的,它可以消失。 如果任何一台服务器出现故障并且服务器确实出现故障,包括 Windows。 因此,当服务器出现故障时,您会丢失数据,尤其是其中一些数据,我的意思是视图状态和会话状态,我的意思是即使您丢失了输出缓存,您也只需重新执行页面,但这两个。 例如,会话状态,您刚刚有一位航空公司客户进行了各种航班搜索,他即将下订单并且服务器崩溃并且他的会话丢失,他必须真正登录。 你知道,你可能会失去那个客户。 所以,你绝对不想输。 因此,您不希望这些数据消失。 所以,在这方面,同步是这里的大问题,它是可靠性。

因此,可靠性是通过复制来处理的。 任何不进行复制的缓存都不能在其中存储会话。 顺便说一下,ASP.NET 特定缓存的好处是不需要编程。 它完全适合 ASP.NET framework. 不幸的是,您必须为应用程序数据缓存进行编程。 在 Java 方面,您不必这样做,或者实际上它们正在出现更多标准。 有一个称为 JCache 的标准,如果您对其进行编程,那么任何第三方缓存都只需插入它,然后您就不必坚持使用任何一个缓存。 更重要的是,如果您使用像 Hibernate 这样的 OR 映射引擎或在 .NET NHibernate 的情况下,您可以插入缓存。 实体框架直到 EF 核心或直到 EF 核心之前它不允许第三方缓存自动插入。 尽管我们已经为 EF6 实现了 ADO .NET 提供程序。 这更多的是 ADO.NET 级别的缓存,因此,您正在缓存查询,这比不缓存要好,但它不如实体级别的缓存强大,您还可以跟踪更新。

实体框架或 EFCore 缓存

新的 EF7 或 EF Core 具有更加灵活的架构。 它允许您插入第三方缓存。 因此,当您迁移到 EF Core 时,您将能够插入分布式缓存,例如 NCache 无需任何编程。 所以,你只是在做你的标准编程和缓存插件。但除此之外,你必须这样做。 但是,即使在那个插件中,您也无法使用我将要讨论的所有功能。 至少在 ASP.NET 中,这是最容易获胜的。 当您引入缓存时,由于不涉及任何编程,您只需要进行一些基本的健全性测试,您的应用程序就会突然看到性能和可伸缩性的重大提升。

在我继续之前有任何问题吗? 在应用程序方面还是在缓存方面? 在缓存方面。 缓存就像数据库。 就像您调用数据库然后进行异常处理一样。 如果缓存中出现问题,缓存将抛出异常,您捕获它,然后您必须采取适当的措施。 但是,与数据库不同,由于您可能有检查约束或其他参照完整性约束,更新可能会因数据完整性而失败,缓存中不存在这些约束。 缓存将接受您提供的所有数据。 缓存更新或操作失败的唯一情况是系统出现问题。 是的,灾难性的事情。 因此,您不必担心缓存不会在正常操作中更新您的内容。

安保行业

因此,这取决于您使用的缓存产品。 NCache 内置了所有这些功能。您可以打开 保安,因此,每个使用缓存的用户都必须经过身份验证/授权。 它还具有许多其他安全性,例如内置加密。因此,如果您拥有的应用程序非常敏感,则可以在数据被缓存之前对其进行加密,而所有这些都由缓存自动完成,而无需您做任何额外的努力。 因为,正如我所说,最大的用户之一 NCache 是金融服务业。 因为他们有很多网上银行。 那里有很多电子商务,他们的数据非常敏感。 因此,这取决于您选择哪种产品。

通过事件共享运行时数据

第三个用例是 通过事件共享运行时数据. 就像你使用消息队列一样,MSMQ 是一个,RabbitMQ 是另一个。 它们在多个位置的分布式环境的消息传递方面具有更多功能。 如果您的应用程序都在一个数据中心中并且您需要这样做 发布/订阅 作为数据共享类型,分布式缓存比任何其他消息队列更具可扩展性。 它可能没有那么多功能,但您可能不需要所有这些功能。 但是,它更快,更具可扩展性,这对于 NCache 对于提供此功能的其他分布式缓存也是如此。 因为,这整个集群又变成了一个消息总线。 因此,您知道,如果您正在进行大量活动,事件会很快传播,因为它们不止一台服务器来处理这些事件,而且随着负载的增长,您可以添加更多服务器。

所以,现在很多这些服务器应用程序都有内置的工作流程,你知道的。 一个应用程序在哪里做某事,一旦完成,其他应用程序就会做其他事情。 这就是 Pub/Sub 或生产者/消费者概念的来源。 有一个事件通知功能。 NCache 有这个功能叫 连续查询 这是一个非常强大的功能,在 Java 端也存在,但其他 .NET 缓存都没有。

因此,运行时数据共享也是您应该强烈考虑通过分布式缓存进行的事情,让您的生活变得更加简单,而不是合并多个其他产品。 即使在这里,问题的性质通常也与此相同,即您尝试共享的数据仅存在于缓存中。 它可能存在于数据库中,但以不同的形式存在,因为你已经构建了它,你已经产生了它,你知道,你已经把很多数据放在一起,并以最终或中间形式与某人共享别的。 所以,如果你丢失了这些数据,你必须重做整个事情。 因此,虽然它没有会话那么糟糕,但它仍然对性能有很多影响。 因此,您不想丢失这些数据。 所以,这里再次关注的是确保缓存是可靠的。

因此,这就是分布式缓存提供的三个高级用例。 正如我所说,分布式缓存本质上是一个分布式内存数据库,一个键值存储。

动手演示

在我详细介绍如何执行这些操作之前,我将快速向您展示分布式缓存是什么样的? 因此,您可以看到如果您在应用程序中使用它会怎样。 我当然要使用 NCache 作为例子。 NCache 是一个开源缓存。 因此,如果您没有钱或没有预算,您无需购买即可使用它。 如果您的项目对业务更敏感,那么当然购买企业版。 因此,我在 Azure 中设置了一些要使用的 VM。 我将使用 2 节点缓存集群,因此,我有“demo1”和“demo2”作为我的缓存服务器,“demo-client”是我的应用程序服务器框。 所以,这就是您的 ASP.NET 服务器框。 所以,一个 NCache 客户端确实是您的应用程序服务器。

我登录到,比方说,演示客户端。 所以,我要做的第一件事就是创建一个新的缓存。 所以,我将使用这个名为 NCache 经理. 所以,它是一个资源管理器风格的工具。 我只想说创建一个新的集群缓存。 在 NCache 所有缓存都被命名。

我将选择存储或复制策略。

我将继续并在此处指定我的第一个缓存服务器。 指定我的第二个。

我将继续,我会在这里说我的服务器应该消耗多少内存。 在你的情况下,它会更多。 我刚刚指定了一场演出。

因此,如果该内存被消耗,您可以指定驱逐策略。 因此,假设至少最近使用的策略意味着驱逐一些最近最少使用的项目。

所以,我刚刚明白了。 我将继续添加一个客户端节点,这是我的盒子,我将继续并启动缓存。

那么,您将其设置在什么机器上? 这是蔚蓝。 所以,这两个是我的demo1和demo2。 所以,我实际上已经登录到这个盒子并且我正在使用 NCache 经理在这里,我在这里创建一个由 demo1 和 demo2 组成的集群。

所以,既然我已经开始了,我可以继续,我可以查看统计数据。 所以,我可以看到一些 PerfMon 计数器活动。 我也可以启动这个监控工具 NCache 这让我看到了一些东西,然后我将快速运行这个名为压力测试工具的工具。 它使您可以快速测试环境中的缓存以了解其运行方式。

因此,该工具就像您的应用程序。 你可以用它来模拟不同类型的负载,不同类型的操作。 例如,我在这里每秒处理大约 600 个请求,每个请求大约 700 到 800 个。

让我在这里再运行一个压力测试工具实例。 所以,你会看到负载只会翻倍。 因此,随着我不断添加越来越多的应用程序实例,缓存上的负载将增加,直到这两个服务器将达到最大值,然后您只需添加第三个。

而且,您可以在运行时完成所有这些操作,而无需停止任何操作。 所以,突然你的基础设施......想想这个,如果你的数据库开始阻塞,你真的可以添加另一台服务器并摆脱这个问题吗? 你不能。 因为,就其性质而言,这与任何特定数据库无关,而是所有关系数据库。 但是,如果是缓存,只需添加另一台服务器,现在缓存就可以工作了。

所以,这就是缓存的样子。 如您所见,它易于使用和配置。 我的意思是我唯一没有做的就是安装。 这只是一个 Windows 安装程序,你知道,它不需要那么长时间。 所以,除了计算一个两节点集群之外,我花了大约五分钟。

而且,既然我已经配置了所有这些东西,我实际上可以在这个盒子上运行的应用程序上指定这个缓存名称。 所以,如果我有更多的客户端盒子,我会继续在这里添加它们。

对于这两个,您可能有 8 个或 10 个。 因此,您可以将它们全部添加,然后一旦您执行此缓存演示缓存就可以在那里使用,然后您只需去更新您的东西。

所以,既然我们知道了缓存是什么样的,现在让我快速转到下一个主题。 因此,正如我所说,使用缓存的最简单方法是将其用于会话。 我怎么做? 所以,我有 ASP.NET 应用程序。 我会进去打开 web.config,然后我会做两件事。 首先,我将去添加程序集。

...
<compilation defaultLanguage="c#" debug="true" targetFramework="4.0">
    <compilers>
        <compiler language="c#" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" extension=".cs" compilerOptions="/d:DEBUG;TRACE" />
    </compilers>
    <assemblies>
        <add assembly="Alachisoft.NCache.SessionStoreProvider, Version=4.6.0.0, Culture=neutral, PublicKeyToken=1448e8d1123e9096" />
    </assemblies>
</compilation>
...

的情况下 NCache 那是实现 ASP.NET 会话状态提供程序. 所以,就是这样 NCache 插入 ASP.NET framework. 任何第三方缓存都必须这样做。 因此,您只需在程序集中添加该行,然后您只需转到会话标签,然后将其指定为特定于您选择的分布式缓存。

...
<sessionState cookieless="false" regenerateExpiredSessionId="true" mode="Custom" customProvider="NCacheSessionProvider" timeout="20">
    <providers>
        <add name="NCacheSessionProvider" type="Alachisoft.NCache.Web.SessionState.NSessionStoreProvider" sessionAppId="WebF1" cacheName="democache" writeExceptionsToEventLog="false" enableLogs="false" />
    </providers>
</sessionState>
...

的情况下 NCache 您只需复制此内容,您需要确保几件事。 首先,您需要确保模式是“自定义”。 所以,因为定制意味着第三方存储。 然后,“超时”确保它是您想要的,以及您可以保留的所有其他默认值,只需在此处指定一个缓存名称,就在那里。 所以,它应该只是“demoCache”。 一旦您进行此更改并再次运行应用程序,或者实际上只要您保存它,您就会知道,ASP.NET 工作进程会回收。 它会捡起来 NCache 配置,突然你会看到每个会话都是一个计数。 所以,所有这些,你知道的,我们看到的那些 NCache 在这个 PerfMon 中。

因此,这些将存储 540 个会话,当然,当您添加更多会话时,它会增加计数。

因此,只需稍加努力,您就可以立即大大提升您的应用程序。 视图状态也是如此。 使用输出缓存会涉及更多配置,但视图状态和会话可以在几分钟内完成,无需任何努力,突然间应用程序使用了巨大的提升。

应用程序数据缓存概述 (API)

所以现在,让我们转向应用程序数据缓存,这是本次讨论的主要内容。 因此,如果您需要将缓存用于应用程序数据缓存,则必须对其 API 进行编程。 那是因为,仍然没有标准的 API。 所以,我们所做的是,我们选择了尽可能接近 ASP.NET 缓存对象的 API。

缓存连接
Cache cache = NCache.InitializeCache("myCache");
cache.Dispose();
获取数据
Employee employee = (Employee) cache.Get("Employee:1000");
Employee employee = (Employee) cache["Employee:1000"];
bool isPresent = cache.Contains("Employee:1000");
写入数据
cache.Add("Employee:1000", employee);
cache.AddAsync("Employee:1000", employee);

cache.Insert("Employee:1000", employee);
cache.InsertAsync("Employee:1000", employee);
cache["Employee:1000"] = employee;

Employee employee = (Employee) cache.Remove("Employee:1000");
cache.RemoveAsync("Employee:1000");

NCache 自 2005 年以来一直存在,所以,现在已经快 11 年半了。 但是,它看起来与 ASP.NET 缓存对象非常相似。 还有一些额外的东西,假设你通过缓存名称连接到缓存。 你得到一个缓存句柄。 然后那个缓存句柄就是你用来做的 缓存.获取. Get 包含 Add、Insert、Remove 并且有一个 Async 版本,这意味着不要等待缓存更新,当然,当您调用 Async 时,您可以指定回调。 因此,如果出现问题,您的回调会被调用。

让我向您展示这在视觉效果中的样子,就像在适当的应用程序中一样。 所以,如果你有一个……这是一个标准的控制台应用程序。 您需要参考缓存的程序集。 的情况下 NCache,就这两个程序集, Alachisoft.NCache。运行, Alachisoft.NCache.Web. 您在此处指定相同的命名空间,然后在应用程序的开头指定,对于 ASP.NET,这可能位于 全球.asax 在 Init 方法或应用程序启动方法中。 但是,在这里,假设您根据缓存名称和缓存名称连接缓存,如您所知,至少在 NCache 这就是如何识别缓存并获得缓存句柄的方式。 然后你创建你的对象并做一个 缓存。添加. 您指定密钥。 这不是一把好钥匙。 它应该有更多的独特性。 但是,假设您指定了 key,而这里的 value 是实际对象。

public static void Main(string[] args)
{
    try
    {
        //Initialize cache via 'initializeCache' using 'Cache Name'
        //to be initialized. 
        Cache cache = NCache.InitializeCache("demoCache");
        cache.Clear();

        //Another method to add item(s) to cache is via CacheItem  object
        Customer customer = new Customer();
        customer.Name = "David Johnes";
        customer.Age = 23;
        customer.Gender = "Male";
        customer.ContactNo = "12345-6789";
        customer.Address = "Silicon Valley, Santa Clara, California";

        DateTime calendar = new DateTime();
        calendar.AddMinutes(1);

        //Adding item with an absolute expiration of 1 minute
        cache.Add("Customer:DavidJohnes", customer, calendar, Cache.NoSlidingExpiration, CacheItemPriority.Normal);
        Customer cachedCustomer = (Customer) cache.Get("Customer:DavidJohnes");
        ...

数据过期

而且,在这种情况下 NCache 正在做这件事叫 绝对到期. 所以让我来吧。 因此,我们讨论了希望保持缓存新鲜的应用程序数据缓存。 所以,如果你不能保持缓存新鲜,那么你就强制将它用于只读数据。 那么,如何保持缓存新鲜呢? 方法不止一种。 几乎每个缓存都有它的第一种方式称为绝对过期。 您可以在此处指定要将其保留在缓存中的时间。 所以,你对缓存说我只感觉舒服一分钟,我认为这些数据不应该在缓存中停留超过一分钟,因为,我认为如果它在缓存中保存超过一分钟,其他人将对其进行更新,并且缓存将过时。 因此,您正在对缓存应该保存您的数据多长时间进行有根据的猜测。 而且,当然,您缓存的每个数据都应该有某种到期时间。 差不多……我的意思是一些数据,你几乎可以假设它永远不会过期,所以你可以说没有过期。 但是,99% 的数据或超过 90% 的数据需要过期。 因此,绝对过期是基于有根据的猜测使缓存保持新鲜的原因。

还有一个叫作过期 滑动到期. 它的目的完全不同。 虽然它使用同名调用过期。 滑动到期表示如果没有人在这么长的时间内触摸这个对象,比如说 10 分钟,20 分钟,就会过期。 而且,这用于更多的瞬态数据。 因此,这用于更多 ASP.NET 特定数据,您实际上只是在进行清理。 你是说,你知道,没有其他人需要它了。 因此,如果用户在 20 分钟内不再使用该会话,那么您就是说该用户已注销,因此请删除该会话。 所以,这是一个清理到期。 然后绝对到期,这是一个同步目的。 绝对过期的目的是同步。 再一次,你可以做到,你可以在 10 秒、15 秒、1 分钟、5 分钟、10 分钟内过期。 最常见的到期时间可能是一分钟或两分钟。

但是,过期有问题。 那是你在猜测。 您是说我认为将其缓存这么长时间是可以的。 但是,不能保证。 在某些情况下可能有,但在许多情况下并不能保证。 如果有另一个应用程序也在访问同一个数据库并且他们去更新那个数据怎么办。 可能有一些脚本正在运行。 因此,每当发生类似的事情时,到期是不够的。 而且,这就是您需要缓存来监控数据库更改的地方。

如果您可以指定缓存中的任何内容与数据库中的相应数据之间的关联,并且您可以对缓存说请监视此数据集。 如果此数据集发生更改,请继续从缓存中删除该项目。

数据库依赖

让我告诉你这是如何做到的。 所以,有一个功能叫做 SQL 依赖 在 ADO.NET 中 NCache 用途。 这使得 NCache 成为您的数据库的客户。 而且,这可能是 SQL 数据库,也可能是 Oracle。 接着 NCache 告诉数据库在该数据集更改并且该数据集是您指定的 SQL 语句时通知它。 大多数情况下,它将对应于表中的一个或多个行,这些行用于构造您缓存的此对象。 所以,例如,在这里,所以,如果我要去 SQL 依赖项,同样的事情。 你有一个缓存句柄,然后当你添加时,这个 缓存。添加,您现在指定,以防万一 NCache 这个东西叫做缓存项,它是一种包含值和一些其他元数据的结构。 包含的元数据之一称为 SQL 缓存依赖项,它是 NCache 映射到服务器端的 ADO.NET SQL 缓存依赖项的类。 您向它传递一个连接字符串,然后向它传递一条 SQL 语句。

private static void AddProductToCacheWithDependency(Product product)
    {
        // Any change to the resultset of the query will cause cache to invalidate the dependent data
        string queryText = String.Format("SELECT ProductID, ProductName, QuantityPerUnit, UnitPrice FROM dbo.PRODUCTS WHERE PRODUCTID = {0}", product.Id);

        // Let's create SQL depdenency
        CacheDependency sqlServerDependency = new SqlCacheDependency(_connectionString, queryText);

        CacheItem cacheItem = new CacheItem(product);
        cacheItem.Dependency = sqlServerDependency;

        // Inserting Loaded product into cache with key: [item:1]
        string cacheKey = GenerateCacheKey(product);
        _cache.Add(cacheKey, cacheItem);
    }

因此,您会说“从产品中选择一些列,其中产品 ID 是任何值”。 因此,这是您将其映射到的一种产品。 所以,你说 NCache 使用此 SQL 语句,使用此连接字符串并监视该数据集或要求 SQL 服务器监视该数据集。 实际上,这段代码在这里运行,对吧。 因为,那是 NCache 是。

因此,这随后与缓存集群通信,缓存服务器实际上进行 ADO.NET 调用,缓存服务器现在与数据库通信并成为客户端。 而且,缓存服务器是接收通知的服务器。 而且,如果数据库通知它该数据已更改,则缓存服务器实际上是从缓存中删除该项目的服务器。 因此,通过内置该功能,您突然拥有了将数据保存在缓存中的灵活性,您甚至无法预测它何时会发生变化,您知道。 但是,您只知道它可能会以不可预知的方式发生变化,因此,您可以使用指定 SQL 依赖项。

这是一个非常强大的功能。 这是允许缓存让您缓存事务数据的功能。 即使您始终无法控制更新,并且因此您将缓存各种数据,因为该缓存将变得真正有用​​。 而且,如果底层数据库支持数据库通知(在 SQL Server 和 Oracle 的情况下就是这种情况),那么这可以通过多种方式完成,那么它将使用 SQL 依赖或 Oracle 依赖特性,如果不支持,则 NCache 至少有一个内置的轮询机制,它也可以让你指定它。 ... 有一种机制可以让您指定一个特殊的表 NCache 监视它以及其中更改的任何行都有相应的缓存项,然后将其删除。

而且,第三种选择是您实际上从 CLR 过程中进行缓存调用。 因此,每当数据更改时,都会有一个数据库触发器调用该过程,该过程调用缓存。 而且,它可以添加数据、更新数据或从缓存中删除数据。 CLR 过程的唯一问题是您不想进行……我的意思是您想进行异步调用。 因为,不管你叫什么,实际上你的数据库现在已经变成了缓存的客户端,就像倒退一样,对吧。 因此,它正在尝试更新将进入此处的内容,然后可能会被复制。 因此,所有这些都发生在数据库事务中,并且如您所知,数据库事务很快就会超时。 因此,通过进行异步调用,您可以立即进行缓存调用,然后返回控制权。

因此,这三种方法可以确保缓存与关系数据库保持同步。 当然,同样的逻辑也适用于非关系型。 如果您的数据在某个地方的云中怎么办? 或者它在大型机中。 你知道,你有一个 web 方法调用来总是去获取它。 那么,万一 NCache 至少,您可以拥有一个自定义依赖项功能,您的代码在缓存集群上注册, NCache 每隔 15 秒调用一次,然后说继续,请检查您的数据源并查看该数据是否已更改。 如果有,则触发一个事件,然后执行相同的逻辑。

所以,又 保持缓存新鲜 是分布式缓存中最重要的东西。 如果你不能保持缓存的新鲜,那么它的使用就会受到很大的限制。

直读和直写

还有一个功能叫做 直读和直写. Read-Through 是一个非常有用、非常强大的功能。 它本质上又是您在缓存服务器上运行的代码。 它在哪里? 这里。 因此,假设您实现了一个通读接口,以防万一 NCache. 现在 read-through/write-through 是一个概念,由 NCache. 它也由许多 Java 播放器实现,但在 .NET 端 NCache 是唯一拥有它的人。 所以,通读接口本质上就是你的代码。 它有一个 Init 方法、一个 Dispose 方法、Connect 和 Disconnect 以及 Load 方法。

...
// Perform tasks like allocating resources or acquiring connections
public void Init(IDictionary parameters, string cacheId)
{
    object connString = parameters["connstring"];
    sqlDatasource = new SqlDatasource();
    sqlDatasource.Connect(connString == null ? "" : connString.ToString());
}

// Perform tasks associated with freeing, releasing, or resetting resources.
public void Dispose()
{
    sqlDatasource.DisConnect();
}

// Responsible for loading an object from the external data source.
public ProviderCacheItem LoadFromSource (string key)
{
    ProviderCacheItem cacheItem = new ProviderCacheItem(sqlDatasource.LoadCustomer(key));
    cacheItem.ResyncOptions.ResyncOnExpiration = true;
    // Resync provider name will be picked from default provider.
    return cacheItem;
}
...

它为您提供了一个密钥,并根据您应该找出去哪里获取数据的密钥。 而且,您不仅返回数据,还返回一些元数据,例如过期和其他相关内容。 通读的价值是什么? 所以,同样,这段代码是你实现的,你在……上注册的东西。所以,正是这段代码在缓存服务器上运行。 所以,我们已经讨论了自定义依赖项。 因此,如果您有一个自定义数据源,那是在缓存服务器上运行的代码,而通读也是在缓存服务器上运行的东西。

通读有什么用? 为什么要使用通读? 有两三个好处。 第一,您将所有持久性代码整合到一个地方。 因此,如果您有多个应用程序使用缓存,假设您有多个应用程序,您知道,它们不必一遍又一遍地实现该代码。 因此,它保存在缓存服务器本身中。 这是第一个好处。 第二个好处是,当您使某些内容过期时,比方说,您由于过期或数据库同步而使某些内容过期,而不是从缓存中删除该项目,为什么不重新加载它。 因为,如果您删除它,下次应用程序需要它时,无论如何它都会重新加载它。 因此,您可以使用它映射通读,并说明如果该项目过期或如果它得到...如果它是自动同步的并且我需要删除它,我将只调用通读。 那么,为什么调用通读是好的呢? 我的意思是有什么大不了的? 为什么不只是通过应用程序重新加载它? 好吧,如果你有一个高流量的应用程序,很可能很多用户会要求同样的数据。 你有成千上万的这些项目,它们都在自己的时间到期。 因此,每当一个项目过期时,您将有数百个甚至数千个请求针对同一项目访问数据库。 并且将全部再次更新缓存。 因此,很多不必要的流量都流向了数据库。 哪一个,如果你有一个通读,它永远不会离开缓存。 它将始终保留在缓存中,并且在某些时候它会被更新。 因此,数据库的流量永远不会发生。

第二个功能是直写,它的工作原理与直读完全一样,当然它是用于写入的。 所以,让我告诉你。 例如,再次在这里,您有 Init,您有 Dispose,但现在您有一个 Write 并为您提供实际对象,然后写入可以是添加、更新或删除,对吧。 所以,这三个都叫写。 因此,无论该操作类型是什么,您都可以去更新您的数据库或数据源。

#region IWriteThruProvider Members
public OperationResult WriteToDataSource(WriteOperation operation)
{
    bool result = false;
    OperationResult operationResult = new OperationResult(operation, OperationResult.Status.Failure);
    Customer value = (Customer)operation.ProviderCacheItem.Value;
    if (value.GetType().Equals(typeof(Customer)))
    {
        result = sqlDatasource.SaveCustomer((Customer)value);
    }
    if (result) operationResult.DSOperationStatus = OperationResult.Status.Success;
    return operationResult;
}

public OperationResult[] WriteToDataSource(WriteOperation[] operation)
{
    throw new Exception("The method or operation is not implemented.");
}

现在,直写还有另一个好处。 就代码的通用性而言,它与通读具有相同的好处。 直写的第二个好处称为后写。 就是这样,你可以更新。 因此,有一个称为 write-behind 的功能,您可以在其中以同步方式立即更新缓存,但数据库更新以异步方式发生。 为什么这是一件好事? 好吧,因为那个数据库很慢。 因此,如果您知道,如果您确信数据库更新将及时完成……那么为什么不将其排队是可以预见的。 它排队,缓存实际上在后台执行此操作。 该队列也被复制到多个服务器。 因此,如果任何一台服务器出现故障,队列不会丢失。

我认为您必须在可扩展性的背景下看到所有内容。 无论什么速度快,即使它是内存数据库,也都在一台服务器上。 因此,发送的更新越多,发送给它的读取越多,数据库中的负载越多,它变得越慢。 就像单点故障一样。 而已。 这就是为什么所有这些都存在的真正原因,然后在所有这些现有的背景下,您可以获得所有额外的好处。 如果你有一个后写功能,突然间你的应用程序只需要以同步方式更新缓存。 因为,一旦缓存更新,缓存将以异步方式处理数据库更新。 而且,该队列有自己的优化。 它可以进行批量更新,也可以复制。 因此,write-behind 具有性能,您所说的速度部分与 write-through 和 write-behind 更相关,然后与 read-through 相关。

通读也是一种东西,你知道,在某些情况下它可能比应用程序访问它更快,但在后写中它肯定总是更快。

任何问题? 我只剩下几分钟了。 我将讨论另一个功能,然后我会走到最后。 因此,习惯于进行独立的 In-Proc 内存缓存(如 ASP.NET 缓存对象)的人,当他们移动到分布式缓存时,我们的许多客户都会打电话给我们说,你知道,你承诺缓存会实际上提高了我的表现。 我的表现确实下降了。 这是为什么? 有人能猜到吗? 为什么这实际上会减慢,一旦您移动到分布式缓存而不是 In-Proc 缓存。 网络延迟。 网络延迟就是其中之一。 更大的问题是序列化和反序列化,这是一个巨大的成本。

客户端缓存或近缓存

那么,一些分布式缓存正在做什么,包括 NCache, 他们实现了这个叫做 a 客户端缓存,有人称它为靠近缓存。 它是该缓存的一个子集,保存在应用程序服务器中。 它甚至可以是 In-Proc,也可以是 OutProc。 但是,很多时候它是In-Proc。 因此,它为您提供了与独立 In-Proc 缓存相同的好处,只是它与此同步。

而且,这是一个较小的子集,所以假设,这可能是每台服务器 16 gig,这可能只是一个 gig。 而且,它是一个移动的窗口,对吧? 因此,无论您要缓存什么,都将被保留,然后当您继续操作时,旧项目要么过期,要么被驱逐,然后新数据被缓存。

因此,客户端缓存为您提供真正的性能。 它在应用程序进程中以对象形式保存数据。 所以,如果你要取它几百次或几十次,第一次当然需要从分布式缓存中取它。 也许,您第一次会从数据库中获取它。 它进入分布式缓存,然后进入客户端缓存,但一旦完成,所有进一步的读取都将在您自己的堆中完成,这是超快的。 所以,这是一个非常强大的功能。 你肯定想利用这一点。

因此,您需要确定几件事。 分布式缓存,就像在生产环境中的数据中心中运行的数据库一样。 因此,它必须满足一些架构要求。 它必须具有高可用性。 它必须赋予数据可靠性和可扩展性。 所以,它必须做智能复制。

我不打算详细介绍这个。 你可以进去看看。 您可以在线进行研究。 例如,如果您有多个数据中心,您希望您的数据库能够复制,为什么不使用缓存呢? 你知道。 为什么缓存不应该以同步方式跨多个可用......所以,是的,一个好的缓存应该是。

.NET 分布式缓存选项

如果您正在开发 .NET 应用程序,此时您知道,您有两种缓存选项。 一个是 Redis 微软已经在 Azure 上安装了。 另一个是 NCache. 正如我所说,在 Java 方面,有许多非常优秀的玩家。

所以,我只想做一个非常高级的比较。

NCache 是最古老的 .NET 缓存。 它自 2005 年以来一直在市场上。它是原生的 .NET。 它也是开源的,就像 Redis. 它也可以在 Azure 或 Amazon 上使用。 Redis ......所以,有两个版本 Redis, 由 Redis 实验室,仅在 Linux 上运行。 事实上,微软在 Azure 中使用了该版本。 而且,微软移植到 Windows 的版本,他们甚至自己都不使用它。 实际上,我们听到了很多来自客户的投诉。 它不是很稳定。 但是,在 Azure 之外,您拥有的唯一 Windows 版本是 Microsoft 移植的版本,即 MS Open Tech。 但是,除此之外,如果你去 Redis 他们只会给你Linux版本。 在 Azure 本身上有两个区别 NCache 和 Azure 就是这样 NCache 实际上给你一个缓存服务器作为一个虚拟机。 所以,你可以自己运行所有的服务器端代码,监控事情。 Redis 为您提供缓存即服务。 因此,缓存是一个黑匣子。 您只需调用一个非常简单的 API。 而且,这样做的局限性在于您无法获得我们刚才谈到的所有功能。 你如何进行数据库同步? 通读通写。 实际上,我什至没有进行 SQL 搜索,因为我们没有时间。 因此,这是您可以执行此操作的两种方法。 如果您想了解更多有关这方面的信息,您可以访问我们的网站,我们有一个完整的 详细对比文件. 所以,如果我要进入 Redis vs NCache 在这里,您可以看到完整的文档。 你也可以下载它。 这是基于他们的文档和我们的文档的逐个功能比较,然后您可以查看哪个满足您的需求。 任何问题? 我的演讲到此结束。 谢谢你。

有没有依赖 .NET Core 框架还是可以与 .NET 4.0 一起使用? 好问题。 所以, NCache 服务器支持 .NET frameworks。 它是 4.0 或更高版本。 这 NCache 客户端默认为 .NET framework 当然。 我们即将发布 .NET……。 所以,我们支持 ASP.NET Core。 所以 .NET Core 可以在 Linux 上运行,我们还不支持,因为它有限制。 但是, .NET Core 在 ASP 上运行.NET framework, 仅在 Windows 下,尤其是 ASP.NET Core NCache 会支持它。 非常感谢你们。

接下来做什么?

 

注册每月电子邮件通讯以获取最新更新。

联系我们

联系电话
©版权所有 Alachisoft 2002 - 版权所有。 NCache 是 Diyatech Corp. 的注册商标。