2016 年 DEV 交叉路口

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

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

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

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

概述

大家好。 我的名字是伊克巴尔汗。 我是技术布道者 Alachisoft. 我们是一家位于旧金山湾区的软件公司。 NCache 是我们的旗舰产品,它是分布式缓存,这是我今天的主题。 我的话题不是关于 NCache 今天,它的缓存一般。 NosDB 是我们拥有的另一个产品,它是开源的 NoSQL database 对于.NET。 我将使用 Azure 来演示分布式缓存,以便大家了解它的实际使用方式。

因此,今天的主题是为分布式缓存扩展 .NET 应用程序。 我更喜欢有更多的互动讨论。 所以,正如我所说,如果你们有问题,请举手。 所以,我们可以在那个时候讨论这个问题,而不是等到最后。 我发现这使对话变得更有意义。

那么,让我开始吧。 好的! 所以,我们将通过一些定义。 我相信你们中的大多数人已经知道这一点,但这是为了完成目的。

可扩展性

第一个定义是可扩展性。 可扩展性是峰值负载下的高应用程序性能。 因此,如果您有一个 ASP.NET 应用程序或任何执行速度超快的 .NET 应用程序,假设有五个用户,那么它不一定是可扩展的。 当然,如果它不能在五个用户中快速执行,那么您还有其他问题。 但是大多数应用程序在五个用户的情况下运行速度确实非常快,但是当你达到 5,000 或 50,000 或 500,000 个用户时,事情才真正开始崩溃。 因此,如果您希望您的应用程序具有可扩展性,它必须在峰值负载下运行。

线性可伸缩性

线性可扩展性意味着您的应用程序架构和部署策略,如果以这样一种方式完成,即添加更多服务器为您提供处理更多事务的增量能力,而不是线性可扩展性。

线性可伸缩性

这意味着如果您有两台服务器,并且如果您添加,假设有一千个用户和三台服务器,您应该有 1500 个用户或类似的东西。

非线性可扩展性

但是,如果您的应用程序如果您的架构或部署不是线性可扩展的,那么它看起来更像是一条上下起伏的对数曲线,这意味着在某个点之后添加更多服务器并不重要。

非线性可扩展性

它会让事情变慢。 如果您有更多交易,您就无法退出。 你不能让自己摆脱这个问题。 因此,您不仅需要可伸缩性,还需要线性可伸缩性。

哪些应用程序需要可扩展性?

以下应用程序通常具有这些类型的问题。 这些是 ASP.NET Web 应用程序,这些是 Web 服务,这些是 Internet IOT 后端,通常也是 Web 服务,您可能有在 .NET 中通常不常见的大数据处理,但是,大数据处理也是一些东西需要扩展或任何其他服务器应用程序。 您可能是一家有合规要求来处理一定数量交易的金融机构。

因此,您可能有一个服务器应用程序具有合规性要求,比方说,如果您是银行电汇应用程序,您需要在下一个工作日或特定时间电汇资金。 因此,您需要能够处理越来越多的交易。 因此,如果您拥有这些应用程序之一,那么您就来对了。

可扩展性问题

所以,让我们定义可伸缩性问题。 你们中的大多数人都知道今天的应用程序架构,如果您有一个 ASP.NET 或 Web 服务应用程序,那么应用程序层的架构规模会线性变化。 因此,您可以添加更多服务器,通常不会有问题。 问题确实出在您的数据存储上,当我使用数据存储这个词时,我指的是关系数据库和大型机。 有遗留数据。 您传统上使用并成为瓶颈的任何数据存储,当它成为瓶颈时,您就会遇到这个问题。 这 NoSQL databases,我应该说他们并不总是答案。 但 NoSQL 运动的开始部分是因为这个。 因为关系数据库不可扩展所以 NoSQL databases 是可扩展的,但是它们并不是在所有情况下都很好,因为你知道,它们要求你将所有数据从现有数据库移动到一个 NoSQL database 您可以对大量新数据执行此操作,但出于业务目的和技术原因,传统业务数据、您的客户、他们的帐户,所有这些数据都必须保持相关性。 技术原因当然是关系数据库的生态系统无法与任何其他数据库相匹配 NoSQL database 和商业原因当然是同一个性质的。

所以, NoSQL database 并不总是答案,即使我们有 NoSQL database 我们销售的产品称为 NosDB,仅用作关系数据库的扩充。 因此,您无法摆脱关系数据库。 关系数据库将继续存在。 所以,你需要接受这个现实。 因此,您需要解决关系数据库的可扩展性问题。

可扩展性解决方案

解决方案当然是您应该使用内存中的分布式缓存。 NCache 就是这样一种解决方案。 这是一个开源的分布式缓存。 我们是市场上最古老的 .NET 分布式缓存。 我们已经存在了 10 年,实际上现在已经 11 年了。 而且,我们是唯一真正的原生 .NET 缓存。

你们大多数人都听说过 Redis, 对? 所以,两年多以前,我们从未听说过 Redis 因为他们真的没有专注于此。 直到微软与他们合作开发 Azure。

分布式缓存部署

内存中分布式缓存的好处是您可以将它与现有数据库一起使用。 因此,您可以解决关系数据库通过内存中分布式缓存为您提供的问题。 那么,你如何解决这个问题呢? 你解决了这个问题,让我们在这里看看这张照片。

分布式缓存部署
分布式缓存架构

因此,您有一个应用程序层,即您的 Web 应用程序、Web 服务、任何其他服务器应用程序,您可以在此处添加更多服务器。 对于 Web 应用程序和 Web 服务,通常有一个负载均衡器,我没有在其中绘制。 因此,您可以在这一层添加更多服务器。 您不能在数据库层添加更多服务器。 是的! 您可以在 NoSQL 层,但是,正如我所说,这并不总是答案。 所以,你必须解决这两个问题。 因此,您在应用层和数据库之间放置了一个内存分布式缓存。

分布式缓存通常形成一个集群。 因此,并非所有分布式缓存都形成集群。 Memcached 即使它是分布式缓存,也从未形成集群。 Redis 确实形成了一个集群, NCache 肯定会形成一个集群。 分布式缓存在这里形成了一个由两个或多个服务器组成的集群。 我说两个的原因是出于冗余目的和复制目的以及许多其他目的以及可扩展性目的。 如果您只需要一个缓存服务器,您可能不需要分布式缓存。 因此,您应该至少有两台缓存服务器,并且这个缓存集群实际上将所有缓存服务器的内存和 CPU 资源汇集到一个逻辑容量中。 这意味着当您添加更多服务器时,您将获得更多内存、更多 CPU 处理能力和更多网卡容量。 这是可扩展性的三个瓶颈; 内存、CPU和网卡。 如今的网卡是……XNUMX Gb 或 XNUMX Gb 几乎是标准的。 除非您的对象很大,否则很难使用一张千兆卡或十吉比特。 但是,如果您的对象很大,那么大意味着每个对象有数百千字节,那么如果您有大量流量,则很容易将网卡最大化。 但是,如果你有更多的服务器,当然还有更多的网卡,那么内存也是一样的。

它是内存分布式缓存的原因是内存比磁盘快得多,这才是真正增加价值的原因。 它更快,更具可扩展性。 因此,这里的目标是捕获大约 80% 的应用程序访问分布式缓存。 因此,只有 20% 将留给数据库。

许多人最初将缓存视为性能提升。 是的! 这是一个性能提升,因为内存比这更快。 但更重要的是,这是一种可扩展性需求,因为在基础架构中如果没有这样的东西,您将无法扩展。 事实上,越来越多的公司现在几乎将其作为标准,就像他们将在其应用程序环境中拥有一个数据库一样,他们也将拥有一个分布式缓存。 有人称它为 Java 端的内存数据网格,这是一个术语。 有人称其为数据结构,但分布式缓存是 .NET 生态系统最常见的名称。 所以,这是一个基础设施。 安装到位后,您可以将其用作真正强大的工具。 应用程序服务器和缓存层之间的比率通常约为 4:1、5:1,假设这些服务器在事务方面负载相当高。 您也可以超过 5:1,具体取决于这些的性质。 而且,典型的缓存服务器大约有 16 gig 到 32 gig 的内存和双 CPU 四核类型的配置。 所以,不是一个非常高端的盒子。 事实上,你不希望在这一层有一个非常高端的盒子。 你想要更多的盒子而不是一些非常高端的盒子。 如果您添加更多内存,您可以达到 128 或 256 gig 内存,但更多内存意味着您需要拥有更强大的 CPU。 这是为什么? 因为,如果你有更多的内存,你的堆就更大,你的垃圾收集将是一个更大的任务,而且垃圾收集不是 .NET 中最快的事情,它会占用你的 CPU。 所以,你看起来越来越像一个数据库。 因此,每个缓存服务器最好有 16 到 32 gig 是一个相当不错的最佳位置。 到现在为止还有什么问题吗?

NCache 可扩展性数字

这是可扩展性数字 NCache. 不同的缓存会有不同的数量,但目标是使其具有可扩展性。 因此,读取以这种方式扩展,写入正在扩展。 读取比写入慢,因为复制发生在读取中。 我会谈谈这些能力。

NCache 可扩展性数字

您需要复制的原因是内存是易失的。 因此,如果任何一台服务器出现故障,您将丢失该数据。 因此,您不想在很多情况下丢失这些数据。

分布式缓存的常见用途

到目前为止,讨论的目标是向您展示为什么需要分布式缓存。 而且,既然我们已经确定了这种情况,让我们来谈谈您将使用分布式缓存的目的是什么。

应用数据缓存

最常见的用例是我所说的应用程序数据缓存。 您可以在此处缓存数据库中存在的数据。 因此,您可以尽可能多地缓存它,然后提高性能和可伸缩性。

在应用程序数据缓存用例中要注意的主要事情是数据存在于两个地方。 一个在数据库中,一个在缓存中。 每当发生这种情况时,您首先想到的问题是如果两个地方都存在会出现什么问题? 是的,一致性!

所以,一个好的分布式缓存来处理它真的很重要。 因为,如果缓存无法处理数据必须在两个地方保持一致的事实,那么您将被迫缓存更多只读数据。 只读数据约占数据的 10% 到 15% 或 20%。 大部分数据就是我所说的事务数据。 这些是您的客户您的帐户。 那是正在发生变化的数据。 它可能每隔几秒钟就会改变一次,尽管大多数情况下它可能每两分钟改变一次。 因此,即使您可以将其缓存一分钟或 30 秒,您仍然会受益于您将多次阅读它,如果您将其乘以一天中发生的交易总数,您将拥有数百万笔交易不再进入数据库。 因此,对于应用程序数据缓存来说,一个好的分布式缓存必须处理这种一致性非常重要,我将介绍那些对于保持这种一致性非常重要的特性。 因此,应用程序数据缓存会缓存永久数据。 永久数据意味着它永久存在于您的数据库中。

ASP.NET 特定缓存

第二个用例是,如果您有一个 ASP.NET 应用程序,这当然也适用于其他 Web 应用程序,但我专注于 .NET。 你可以缓存你的会话状态,你的视图状态,如果你没有使用 MVC 框架,你可以缓存你的输出,页面输出。 所有这些数据本质上都是临时的。 它不是永久性的。 任何不是永久的数据都不应该真正存在于数据库中。 这是一个瞬态数据。

当数据是瞬态的,当它是临时的并且它不存在于数据库中时,它只存在于缓存中,最担心的问题是什么? 坚持……或者缺乏坚持。 所以,如果你不坚持,你会丢失数据。 如果这个缓存服务器出现故障,而你有这个购物篮或其他东西,假设你是一家航空公司,而你的这个客户刚刚完成了这次航班搜索,他们将购买 10 张或 5,000 张价值至少 XNUMX 美元的机票,并且他们说的最后一页提交或任何最后一页,突然会话消失了,因为缓存服务器关闭了,他们必须重新开始。 整个活动都丢失了。 你可能会失去那个客户。 不是很好的体验。

因此,您缓存的任何暂时的东西,缓存都必须复制。 任何不进行复制的缓存都不是可行的缓存。 而且,复制是有成本的,因此,缓存必须进行有效且高效的复制。 会话状态是分布式缓存的一个非常常见的用例,因为会话在 ASP.NET 中非常常见。

通过事件共享运行时数据

第三种常见的或实际上不太为人所知的第三种用例称为运行时数据共享。 也就是说,如果您有多个需要共享的应用程序。 一个应用程序产生一些东西或更新一些东西,另一个应用程序或该应用程序的另一个实例需要使用。 通常,您会传统上使用消息队列,或者您只需将该数据放入数据库中,其他应用程序就会拉取。 但是,分布式缓存非常适合该用例。 它不能代替消息队列。 消息队列还有其他用途,但如果您的应用程序运行在同一个数据中心,所有实例和它们都需要共享数据,那么这是一个更具可扩展性的数据或数据共享平台,因为所有应用程序都连接到同一个平台,并且平台可以在 Pub/Sub 模型中触发事件。 Pub 意味着一个应用程序是发布者,他们发布一些东西并触发一个事件。 将通知所有订阅者,他们将使用该数据。

还有其他类型的通知。 当某些项目被修改时,您的应用程序可能会对某些项目表现出兴趣,如果该项目发生变化,请通知我。 或者有一个 连续查询 功能 NCache 有这就像一个 SQL 依赖 SQL Server 中的功能,其中 NCache 允许您说出一种 SQL 类型的查询 选择客户 WHERE Customers.City = "New York". 因此,如果任何具有此条件的客户曾经添加、更新或从缓存中删除,则通知。

因此,这是一种更智能的方式来监控缓存的变化。 因此,所有这些都允许您在运行时以非常快速且可扩展的方式在应用程序之间共享数据。 而且,这也是瞬态数据,尽管许多数据存在于数据库中,但您共享它的形式可能不存在。 所以,它是短暂的。 所以,这也必须被复制。 到现在为止还有什么问题吗? 要么你们已经完全了解这些东西,要么我超级好。

应用数据缓存概述

所以,让我们通过一些源代码来了解如何......您应该使用哪些功能以及如何使用它们。 我要使用 NCache 作为示例,但正如我所说,我的重点更多地放在实际功能上。

这是使用任何缓存的典型方式。

Customer Load(string customerId)
{
    // Key format: Customer:PK:1000
    string key = "Customers:CustomerID:" + customerId;
    
    Customer cust = (Customer) _cache[key];
    
    if (cust == null)
    {
        // Item not in cache so load from db
        LoadCustomerFromDb(cust);
        // Add item to cache for future reference
        _cache. Insert(key, cust);
    }
    return cust;
}

您将从数据库加载客户。 在访问数据库之前,您将检查缓存并使用键,一个基于字符串的键。 让我们说 客户:客户 ID 实际的客户 ID 可能是 1000 或其他东西,你说检查缓存。 如果你有它在缓存中,不需要去数据库,你得到它。 如果缓存中没有它,则转到数据库,加载该客户并将其放入缓存中。 当你把它放入缓存时,下次你来的时候,你或其他任何人来你都会在缓存中找到它。 所以,这是一个非常简单的范例。 一旦你这样做了,每个人都可以找到比缓存更有价值的东西。

当然,还有很多其他功能,您可以使用大量您认为无论如何都需要的数据预先填充缓存。 因此,您将预先保存大量的数据库命中,然后您仍将增量地继续向缓存中添加未在缓存中找到的数据。 例如,这是一个视觉工作室项目。 如果你要使用 NCache 您将链接其中两个程序集。 一个是 NCache。运行 一个是 NCache.Web. 您将在此处类似地使用其中的两个命名空间 NCache。运行NCache.Web.缓存. 我们将命名空间命名为非常接近 ASP.NET 缓存。 所以,你知道,当 NCache 出来 ASP.NET 缓存是唯一可用的缓存。 所以,你已经有了这个,在你的应用程序开始时,这当然是一个控制台应用程序,你的将是不同的。 您将连接到缓存并获得缓存句柄。 每个缓存都被命名并且你有一个缓存句柄,然后你将你的对象添加到缓存中。 所以,比方说,你刚刚添加了,你做了 缓存。添加 这里。 您指定您的密钥。 虽然,这可能不应该是大卫琼斯,它应该是某种客户 ID,然后你有实际的对象,然后你已经指定了到期时间。 并且,您指定一分钟的绝对到期时间。 你是说一分钟后这个项目从缓存中过期。 您刚刚保留的其他所有内容均默认设置。 而且,以后你可以做 缓存.获取 并从另一个地方获得相同的客户。 所以,只是简单的缓存操作。

using System;
using Alachisoft.NCache.Runtime;
using Alachisoft.NCache.Web.Caching;
using Alachisoft.NCache.sample.data;

namespace BasicOperations
{
    public class BasicOperations
    {
        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,所有缓存都被命名。 我现在将快速向您展示缓存的外观,然后我们将回到代码。 我在 Azure 中设置了一堆虚拟机。 所以,你可以运行 NCache 在 Azure 中,在 Amazon 中,在本地。 在所有情况下,缓存服务器本身只是虚拟机。 这只是 Windows 2008、2012 虚拟机。 Azure 中的缓存客户端可以是 VM,也可以是 Web 角色,也可以是辅助角色,也可以是网站。

演示缓存 Azure

创建集群缓存

我已经登录到演示客户端,就在这里。 我现在要快速创建一个缓存。 所以,我可以展示缓存的样子。 使用这个工具叫做 NCache 经理,图形工具,让你……我要来这里说它创建一个“新集群缓存”。

创建集群缓存

所有缓存在 NCache 被命名。 所以,我只是要命名我的缓存。 我此时将其他所有内容都设为默认值。

指定缓存名称

我将为分区副本选择一个拓扑。 我将在本次演讲结束时快速讨论这一点。 分区副本 是我的拓扑。

缓存拓扑

我将使用异步复制。

复制策略

我将选择我的第一个缓存服务器,它是 demo2。 所以,这是我的两个缓存节点。

添加缓存节点

我会点击下一步。 我将采用所有默认值。 我将指定要分配给此缓存的内存量。 因此,这样缓存不会消耗比这更多的内存。 我刚刚做了一场演出,但你的当然会更大。 那是一个分区的大小。

内存大小

因此,一旦缓存使用了那么多内存,缓存就已满。 因此,它要么会拒绝任何新项目,要么会驱逐一些现有项目。 所以,我要说,驱逐 5% 的缓存,我会说最近最少使用的是要使用的算法,我刚刚创建了缓存。

我将继续为此添加一个客户端。

添加客户端节点

所以,我刚刚创建了一个缓存,我们将启动缓存。

启动缓存

模拟压力并监控缓存统计信息

我将选择统计信息,因此,我可以使用一些 PerfMon 统计信息。 我还将监视集群。 所以,我刚刚启动了缓存。 此缓存称为演示缓存。 我将快速测试它。 所以,我只是运行了一个压力测试工具 NCache 并让您在自己的环境中快速测试缓存。

压力测试工具

所以,这个缓存现在正在工作。 所以,正在发生的事情是,在客户端机器上,客户端意味着他们的应用程序服务器机器我有一个配置文件,就在这里。 所以,我刚刚创建了缓存,它现在知道缓存服务器是什么。 现在,让我回到代码。 所以,当我实际上选择了这个缓存名称时,这就是实际发生的事情,我的应用程序现在连接到集群中的所有缓存服务器并给了我一个缓存句柄,这样当我做一个 缓存。添加,它实际上会将它添加到缓存中的适当位置,并为我完成所有复制,所有这些都完成了。

因此,API 隐藏了所有这些细节,但我想向您展示缓存在幕后的样子,以及使用 NCache 在那种情况下。 所以,让我们回到我们的主要内容。 这是 API 的样子。

  • 缓存连接
    ICache cache = CacheManager.GetCache(“myDistributedCache”);
    cache.Dispose();
  • 获取数据
    Employee employee = cache.Get<Employee>("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);
    
    Employee employee = (Employee) cache.Remove("Employee:1000");
    cache.RemoveAsync("Employee:1000");

你做一个 cache.Get、cache.Contains、cache.Add、Insert、Remove. 插入意味着如果它不存在则添加,否则更新。

应用数据缓存功能

好的,现在我们已经了解了缓存是什么样的,什么是简单的 API。 让我们来谈谈我们谈到的功能。 这对于分布式缓存很重要。

保持缓存新鲜

所以,我们说的第一件事是分布式缓存必须保持数据新鲜,缓存新鲜。 因此,它有四种方法可以做到这一点。 第一个是 呼气,其中很多缓存都有,几乎每个缓存都可以让你过期的东西。

所以,有一个 绝对到期 并且有一个 滑动到期. Absolute Expiration 就是我刚才给你看的,也就是说,它说这个项目到期,不管发生什么,让我们从现在开始五分钟。 而且,我之所以这么说是因为正如我所说,这些数据存在于数据库中,我只能相信它五分钟,它不会在数据库中发生变化。 我不想将其保留在缓存中的时间超过缓存,因为它可能会在数据库中更改。 因此,您正在猜测数据的性质。 一些数据可以缓存数小时和数天。 你知道,这可能是你的查找表。 可能是您的定价每天更改一次之类的。 因此,您可以将其缓存 24 小时。

其他数据是您的交易数据。 您只能将其缓存 30 秒或一分钟,因为只要您感觉舒适即可。 因此,绝对过期适用于永久数据,它是您估计或猜测将数据保存在缓存中的安全时间的一种方式。 这很重要。 我想在绝对到期和滑动之间做出区分。

滑动过期基本上是说当没有人再触摸它时,从缓存中删除这个项目,在这个时间间隔内。 触摸意味着获取或更新。 因此,例如,会话状态。 当您注销会话状态时,任何人都不再接触。 因此,大约 20 分钟后,需要将其从缓存中删除。

滑动到期通常用于瞬态数据。 这更像是一种清理操作。 它与保持数据新鲜无关。 它与摆脱它有关,因为您不再需要它。 但是,绝对过期是您保持数据新鲜所需要的。 其次,你知道,到期是一件非常重要的事情。 每个缓存都必须拥有它,其中大多数都拥有,事实上我认为所有这些,至少绝对过期。

将缓存与数据库同步

第二个功能是他们大多数人不做的事情。 而且,这是您希望将缓存与数据库同步的地方。

使用数据库依赖

你说,你知道,我真的无法预测数据库中更新的频率或时间。 我不知道数据库中的数据何时会更新。 因为,我有多个正在更新它的应用程序。 可能是其他人直接接触数据。 所以,我只想要缓存来监控数据库。 因此,缓存应该知道数据库中的这种变化。 这是一个功能, NCache 拥有。 它被称为 SQL 依赖。 实际上,它使用了一种称为 SQL 依赖的 SQL 服务器特性,其中 NCache 成为数据库的客户端。

让我快速向您展示它的外观。 所以,如果我在这里有缓存。 所以,再一次,我们以同样的方式处理库,然后连接到缓存。 现在,当您将项目添加到缓存时,就在此处。 如果我来到这里然后我说'添加产品到缓存并依赖项',去定义。 所以,在这里你要对这个缓存说,这是我的 SQL 语句。

private static void AddProductToCacheWithDependency (Product product)
{
// Any change to the resulset 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.ProductID);

//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);
}

private static string GenerateCacheKey (Product product)
{
string cacheKey = "Product#" + product.productID;
return cachekey;
}

因此,您的 SQL 语句通常会反映表中的这一行。 所以,如果你添加一个产品,它应该是带有产品 ID 的 SQL 语句等于这个。 因此,您正在为 SQL Server 创建此 SQL 语句。 因为,这是您作为 SQL 依赖项的一部分传递给 SQL Server 的内容。 你会将它传递给 NCache. 因此,您在客户端框上将其传递给 NCache. 因此,您将其指定为您的 SQL 语句。 您正在创建一个“缓存项”对象,并在缓存项中指定“SQLServer 依赖项”。 所以, SQLCacheDependency 是一类 NCache, 实际上。 这与 SQL Server 依赖项不同。 除了 SQL 缓存依赖项外,它具有相同的名称。 这会保留该 SQL 语句,您指定该语句并将项目添加到缓存中。 所以,你已经做到了,此时你正坐在这个盒子上,就在这里。

分布式缓存

所以,你把它传递给 NCache. 它转到其中一个缓存服务器。 该缓存服务器现在成为数据库的客户端。 因为,您还指定了连接字符串信息。 您可以在此处指定连接字符串。 所以, NCache 服务器成为您数据库的客户端。 现在该数据库可以是 SQL 服务器,也可以是 Oracle 和 NCache 将建立 SQL 依赖连接。 SQL Server 将在 SQL Server 中创建一个数据结构来监控数据集。

所以,就像 NCache 具有我谈到的用于运行时数据共享的连续查询功能,其中 NCache 正在监视所有客户,其中客户。 城市 = “纽约”。 现在 SQL Server 正在 SQL Server 中监视此数据集,如果从 SQL 数据库中添加、更新或删除任何符合该条件的行,SQL Server 会通知缓存服务器。 缓存服务器现在知道该数据在数据库中已更改。 所以,它有两个选择。 它可以从缓存中删除默认行为,也可以重新加载新副本。

使用直读/直写

它会重新加载一个新副本的方式是,如果你使用另一个未来 NCache 被称为 通读. 我将跳过这个,我将不得不回到这个。 所以,有一个叫做通读的功能就是你的代码。 您实现了一个通读处理程序,我将向您展示通读处理程序。 所以,这里有一个 IReadThrough提供.

{
    // <summary>
    // Contains methods used to read an object by its key from the master data source.
    // </summary>

    public class SqlReadThruProvider : Alachisoft.Ncache.Runtime.DatasourceProviders. IReadThruProvider
    {
        private SqlDatasource sqlDatasource;
        // <summary>
        // Responsible for loading the object from the external data source.
        // Key is passed as parameter.
        // <param name="key">item identifier; probably a primary key</param>
        // <param name="exh">Current expiration hint; you can modify the value and attach a new hint</param>
        // <param name="evh">Current eviction hint; you can modify the value and attach a new hint</param>
        // <returns></returns

        public void LoadFromSource(string key, out ProviderCacheItem cacheItem)
        {
            cacheItem = new ProviderCacheItem(sqlDatasource.LoadCustomer(key));
            cacheItem.ResyncItemonExpiration = true;
            cacheItem.ResyncProviderName = sqlDatasource.ConnString;
        }
        // <summary>
        // Perform tasks like allocating resources or acquiring connections
        // </summary>
        ...

你实现这个。 它有一个初始化它的 Init 方法。 Dispose 方法,实际上类似于 Get 方法。 因此,get 将密钥传递给您,然后您将其归还缓存项。 因此,您转到您的数据源并加载该项目,指定到期时间或任何其他内容,然后将其传递回 NCache. 您的这段代码在缓存服务器上运行。 这是需要牢记的重要事项。

因此,通读实际上是在缓存服务器本身上运行的。 其实我还有一张图。

通过缓存加载器读取

因此,通读在缓存服务器本身上运行。 因此,缓存服务器必须在 .NET 中开发,您的 .NET 代码才能在其上运行,对吗? 所以,如果你的 .NET 商店,如果你的应用程序是在 .NET 中,那么我说你的整个堆栈应该是 .NET 以获得所有这些好处。

所以,例如,来到 Redis, Redis 是一个基于 Linux 的缓存。 所以,它是一个很棒的缓存,我没有反对他们,但如果你是一个 .NET 商店,你需要做所有这些事情,你的代码必须在缓存服务器上运行,以便缓存能够与自身同步并自动从数据库中重新加载该项目。 因此,如果您希望这样的话,当 SQL 依赖项触发时,您的通读处理程序就会被调用。 如果您不希望这样做,那么它只会从缓存中删除该项目。 而且,当它从缓存中删除时,下次您的应用程序查找它时,它不会找到它,它会去从数据库中获取它。 现在,在某些情况下,例如,它是您缓存的产品目录并且您刚刚更新了价格。 为什么要从缓存中删除产品,您只是更新了价格? 因为,现在应用程序必须具有从数据库中获取它的逻辑。 最好自动重新加载。

因此,如果您认为大量数据将在过期或数据库同步发生时一遍又一遍地需要,最好重新加载它而不是删除它。 因为那时应用程序不必这样做。 缓存越多,应用程序就越容易。 因此,缓存可以与数据库同步的另一种方式。 所以,如果你没有 SQL server 或者 Oracle,比如说你有 MySQL 或者 DB2,那么这两个特性存在,那么你可以使用 DB 依赖,这是另一个特性 NCache 那边 NCache 汇集一个特定的表,然后修改数据库触发器以更新该行的标志。 NCache 拿起它说这个项目有变化。 所以,我需要你删除它或重新加载它。

使用 CLR 存储过程

CLR 过程是将缓存与数据库同步的另一种方法。 你实际写的地方 CLR程序. 您从表触发器中调用它。 因此,假设您有一个添加或更新触发器或删除。 你调用这个 CLR 过程,它调用 NCache 或者它调用缓存。

在 CLR 过程的情况下,您需要确保缓存支持异步方法 NCache 做。 因此,您可以像插入异步调用一样在缓存中进行操作,并且控制权会立即返回给您。 因为,如果您不进行异步调用,您的数据库事务就会开始超时。 因为,您正在更新缓存中的多个服务器,您正在穿越网络,这不是数据库事务的设计方式。 因此,您需要进行异步调用。

因此,这就是您可以同步缓存的三种方式。 因此,您支付的任何缓存都需要确保您可以保持数据最新。 这是两种方式。

将缓存与非关系型同步

同样,如果您有一个非关系数据源,假设您的数据在云中或其他任何地方。 您甚至可以进行 Web 方法调用。 实际上,您可以实现一个自定义依赖项,它也是您在缓存服务器上注册和运行的代码,并且 NCache 调用它并说请检查您的数据源是否已更改。 你检查数据源,如果它改变了你通知 NCache 该数据和数据源已更改。 所以, NCache 可以删除它或重新加载它。

因此,再次使用关系数据库 NCache 为你做这一切。 在非关系的情况下,您必须执行自定义依赖项。

处理关系数据

保持缓存新鲜的最后一个方面是依赖特性。 假设您在客户和订单之间有一对多的关系,并且您正在缓存它们。 如果客户对象从缓存中删除怎么办? 订单是否应该保留在缓存中? 如果您实际上从数据库中删除了客户怎么办。 好吧,当我说一对多时,您通常不会删除客户。 比方说,如果您从缓存中删除的一侧确实意味着您可能已经从数据库中删除了它。 这意味着多方不再有效。 那么,谁应该跟踪这一切呢? 如果缓存可以为您做到这一点,那么它会让您的生活变得更加简单。

例如,ASP.NET 缓存对象具有称为 缓存依赖. NCache 已经实现了它,据我所知,没有其他 .NET 缓存具有此功能。 但是,基本上你注册了项目之间的关系并说这取决于这个项目,如果这个项目被更新或删除,请自动删除这个项目。 因此,缓存会为您进行清理。

缓存为您做的越多,对您来说就越好,因为您可以放心,您的数据将是最新的。 一旦你有了这种信心,你就可以缓存几乎所有的数据。 这只是您将使用哪种策略来保持数据新鲜的问题。

查找数据

因此,既然您确信数据是新鲜的,您将开始缓存大量数据。 好吧,一旦你开始缓存所有数据,接下来的事情就是缓存现在开始看起来越来越像一个数据库。 它不是数据库,它始终是一个临时存储。 但是,尤其是很多参考数据,您几乎会将整个数据集存储到缓存中。 发生这种情况时,您希望能够对其进行搜索。 而不是总是根据键找到它,这非常不方便。 根据密钥查找任何项目并不总是很方便。 您希望能够通过其他方式进行搜索。

一种方法是做一个 SQL 搜索。 再次, SELECT Customers WHERE customers.City = "New York". 就像你会做的那样,或者你会说给我这个类别的所有产品。 因此,您将从缓存中获取这些对象的集合,而不是从数据库中获取。 来自缓存当然意味着数据库不再需要承受这种打击,而且它都在内存中。 同时来自多个服务器的速度要快得多。

所以,所有这些都是并行查询。 而且,为了让您能够做到这一点,缓存必须支持索引,这 NCache 做。 我不确定其他产品是否支持,但请确保缓存支持索引。 否则,这将是非常慢的查询。

数据分组

您不能在缓存中做的一件事是连接多个对象或多个表,这是您可以在关系数据库中做的事情。 因此,有一些方法可以解决它,即您可以对事物进行分组,您可以以某种方式标记它们,然后您可以将它们取回,并且您可以根据缓存中的某些逻辑关联或分组来获取数据。 所以,假设你缓存所有内容,我会给你一个例子。 例如,假设您有一批客户回来了。 所以,我有一组对象标签,我想将每个对象分别缓存到缓存中。 但是,稍后,我希望能够一次调用所有内容。

//Caching Query Results (Collection)
//Cache Collection Objects Separately
static void CacheSupplierProducts(NorthwindEntities context, int supplierId)
{
    Tag[] productTags = new Tag[1];
    productTags[0] = new Tag("SupplierProducts:" + supplierId);
    
    IQueryable<Product> products = context.Products;
    
    var result = from product in products
                  where product.SupplierID == supplierId
                  select product;
                  
    foreach (Product p in result)
    {
        String key = "Products:ProductId:" + p.ProductID;
        _cache.Insert(key, p, productTags);
    }
}
...
ICollection productList = _cache.GetByAnyTag(productTags).Values;

所以,要么我发出相同的查询,SQL 查询,然后我说把所有的东西都还给我。 但是,我从数据库中获取了这些对象,我没有从缓存中获取它们。 而且,它们不是一个完整的数据集。 比方说,当时我想要获取的任何集合。 所以,我已经单独缓存了我可能需要单独访问的每个对象。 但是,我希望能够将它们作为一个集合全部取回。 所以,我使用标签的概念。 我用相同的标签标记了它们。 而且,那个标签可以是任何唯一的字符串,我说给我所有有这个标签的项目。 因此,在一个电话中,我可以再次获得所有的全部收藏。 所以,你可以用标签来做这种事情,这弥补了缓存中缺乏连接的情况。

因此,您可以进行分组。 这 组和子组. 你可以做 标签. 你可以做命名标签。 命名标签 本质上你有一个键,然后是一个值。 例如,如果您正在缓存文本、自由格式文本。 没有办法单独索引该文本,因为它是文本。 因此,您必须自己制作标签,而仅标签本身可能还不够。 您希望能够命名每个标签。

因此,就像一个对象具有属性名称一样。 因此,名称标签就像一个对象的属性。 因此,名称可能是城市,文本中的值可能是纽约或拉斯维加斯。 所以,你可以做所有这些事情,然后再获取这些东西,以防万一 NCache 如果您通过 API 或通过 SQL查询. 因此,当您基于 SQL 获取某些内容时……

第一个问题,太好了! 我们可以使用分层分类法吗? 你实际上可以做的是……组和子组只给你一个层次结构。 因此,在一个组中,您可以有多个子组,但不能超越它。

您可以使用不同类型的标签来表示,因为您可以将多个标签分配给一个项目。 因此,例如,如果您有多个层次结构,您可以为向下的每个级别分配,您可以将每个级别的所有顶部分配为单独的标签。

还有一个问题。 我们可以控制缓存的内部表示吗? 索引是基于哈希表的组合构建的,以防万一 NCache和红黑树。 您可以选择索引,但不能在内部选择要使用的数据结构。 但是,索引是根据使用的性质构建的。 因此,例如,哈希表适用于某些类型的访问,如果您要进行范围类型的搜索,那么红黑树会好得多。 所以 NCache 可以。 我不知道其他产品是做什么的,但是 NCache 索引东西很多。

直读和直写

让我快速浏览一下 通读,通写. 那么,为什么要通读和通写? 通读,我们已经看到了一个例子,那就是你可以自动重新加载东西。

通读的另一个好处当然是您将尽可能多的数据访问合并到缓存本身中。 您访问的越多……进入缓存或缓存层的越多,应用程序中的内容就越少,并且应用程序变得更简单。 该应用程序只是一个 缓存.获取 当然,这样做的另一个好处是您可以自动加载内容。

直写也是同样的方式。 一个好处是您可以合并所有写入。 第二个好处是您实际上可以加快写入速度。 因此,例如,后写是一项功能,您可以同步更新缓存,而缓存异步更新数据库。 因此,如果数据库更新不如缓存那么快,这是真的,那么您的应用程序性能会突然提高,因为您只会更新缓存并且缓存更新数据库。

因此,read-through、write-through 和 write-behind 是非常强大的功能,您应该加以利用。 这也是您编写的在缓存集群上运行的服务器端代码,它简化了您的生活。 因此,您的应用程序可以在缓存中拥有更多数据,并通过缓存对其进行更新。

让我,快点过一遍,谈一谈。 我不打算详细介绍 ASP.NET 会话状态缓存. 只要知道你可以在不更改任何代码的情况下插入它,只需对 web.config 进行更改,它就会自动处理它,同样的事情也会发生 查看状态输出缓存.

高可用性(100% 正常运行时间)

我将快速完成几件事。 首先我想谈谈……你使用的任何缓存,因为它就像一个数据库,它是生产中的内存数据库。 所以,缓存必须是高可用的。 您必须具有高度可用的缓存。

动态缓存集群

NCache 通过它所做的几件事,它是高度可用的。 首先它有一个 动态缓存集群. 您可以在运行时添加或删除服务器,它会自动重新调整集群。 您不必在配置中硬编码服务器名称,客户端会自动进行。 一旦客户端与任何缓存服务器通信,它就会获得集群成员信息。 如果成员资格在运行时发生变化,假设您添加了一个新节点或删除了一个节点,则更新的成员资格将传播到客户端。

动态缓存集群

所以,这是必须存在的第一部分。 以这种方式看待它真的很重要。

缓存拓扑

第二个是缓存拓扑。 所以,有不同的方法 NCache例如,将为您提供四种缓存拓扑。 第一个被称为 镜像缓存. 这是一个 2 节点主动/被动。

镜像缓存

秒被称为 复制缓存,其中缓存中的每个服务器都有整个缓存的副本。 因此,您拥有的服务器越多,您拥有的缓存副本就越多。 它是可扩展的读取,而不是可扩展的更新。 但是,它对案例有自己的用途。

复制缓存

第三种拓扑称为 分区缓存,其中整个缓存被分成多个分区。 每台服务器都是一个分区。 而且,这些分区是自动创建的。

分区缓存

因此,例如,如果 NCache 每个分区里面都有一些桶。 因此,整个集群有 1,000 个桶。 所以,如果你有三个分区,它们就有三分之一,三分之一的桶数。

分区副本 与分区相同,但每个分区都备份到不同的服务器上,所有这些都是自动为您完成的。 因此,当您创建一个两节点集群时,您将拥有两个分区,即两个副本。 当您添加第三台服务器时,会自动创建第三个分区,并且在创建第三个分区时,一些存储桶必须从现有分区中移动。

分区副本

因此,所有这些都是自动为您完成的。 将自动创建一个新副本。 实际上,如果您从这里看到它,让我在这里向您展示。 比方说,如果您有两个服务器分区……但是假设您只有两个服务器集群,那么您就有两个分区。 分区 1 在这里,副本 1。分区 2,副本 2。现在,您添加了第三台服务器,突然您必须拥有第三个分区,这将从这里获取一些存储桶,从这里获取一些其他存储桶,这是一回事。 其次,副本 2 将不再在这里,它将移动到这里,因为现在,分区 3 的副本必须在这里。

所以,所有的调整都是自动为你完成的 NCache. 所以,这是一回事。 其次,有一个功能叫 客户端缓存 有人称它为 Near Cache,它真的很强大,你必须使用它。 基本上它是客户端应用程序中的本地缓存。 但是,有一个特殊的地方是这个本地缓存没有与缓存集群断开连接。 它连接到缓存集群。 它使自己保持同步。

客户端缓存

所以,如果出现 NCache,例如,客户端缓存会在后台自动插入到配置中。 无论您从这些应用程序中获取什么,服务器都会自动保存在客户端缓存中。 因此,下次您需要它时,您会在客户端缓存中找到它。 并且,缓存集群知道哪些数据在哪个客户端缓存中。 因此,如果该数据由另一个客户端更改,那么集群会通知所有具有该数据的客户端缓存并自行更新。 因此,客户端缓存保持连接,但它是本地缓存。 它甚至可以是 In-Proc。 In-Proc 意味着在您的申请过程中。 因此,这确实提高了您的性能,但同时您也是整个连接的分布式缓存的一部分。

广域网复制

我要跳过很多东西,我们没有时间。 所以,还有 广域网复制. 如果您有多个数据中心,您应该能够让缓存在 WAN 上自我复制,并且您不能在 WAN 上构建集群。 那只会死,因为这些套接字中断,延迟非常高。 因此,必须有某种方法可以跨两个数据中心连接缓存。

的情况下 NCache 我们有一个叫做桥接拓扑的东西,它连接多个数据中心。 您现在可以拥有主动-主动、主动-被动甚至两个以上的数据中心。 而且,这一切都是异步完成的,并且还为您完成了冲突解决。 因此,如您所见,缓存不仅仅是一个简单的键值存储。 因此,您应该在幕后记住很多事情。

NCache 与 Redis

我很快想介绍的一件事是 NCache 与 Redis,只是一个高级别的,我想谈谈这个。 NCache 是本机 .NET 缓存。 它在 Windows 上运行,您可以同时进行客户端和服务器端缓存,这是一个好处。 然而, Redis 主要是基于Linux的缓存。 在微软和他们合作之前,你知道,.NET 方面的大多数人甚至都不知道他们的存在,尽管他们在 Unix 和 PHP 等环境中一直是非常流行的缓存。

其次,Linux版本的 NCache 在 Azure 上可用,作为缓存即服务。 因此,缓存即服务的原因是因为对于 .NET 应用程序,您不能真正在基于 Linux 的缓存上运行服务器端代码。 您必须有一个基于 .NET 的缓存才能运行。 因此,这是您将获得的一个巨大限制。

第三,如果你打算在 Azure 之外做任何事情, Redis 仅作为基于 Linux 的缓存提供。 微软所做的窗口支持是他们自己不使用的。 即使在 Azure 上,他们也不选择在 Windows 上使用它。 它不是很稳定。 如果您想查看更详细的信息,还有更多详细信息 之间的比较 NCache 和 Redis,你可以来这里看看对比。 所以,我们有 比较 和每个人在一起,因为我们对自己非常有信心。

因此,如您所见,这是一个全面的比较。 只是通过它。 如果您正在考虑使用分布式缓存,那么您应该满足所有这些可伸缩性需求。 做好功课,确保无论您使用什么,都适合您的需求。 而且,就 NCache 担心,我们会让您轻松比较 NCache 和其他人。 您可以访问我们的网站并 下载 NCache. 它是开源的。 因此,您可以在此处下载企业版或开源版本,也可以转到 GitHub上 你将有 NCache 点击此处。

我的演讲到此结束。 任何问题? 您可以传递存储过程调用。 它必须是所有 SQL,就像将在 SQL 服务器本身内运行的事务 SQL 东西一样。

是的。 因此,缓存可以与应用程序位于同一台服务器上。 我们不推荐它。 这不是一个好的部署策略,但如果您的配置很小,当然可以。

当您进行分区时,所有分区都为您完成。 因此,整个想法是每个分区的权重、数据大小和事务都应该相等。

您不控制分区,您只知道它们存在。

当您更新项目表时,如果您有一个可以直接从数据库调用缓存的 CLR 过程,您可以选择从数据库本身更新缓存。 您还可以编写单独的代码来更新缓存,这取决于您在缓存中保留了多少。

所以,你知道,如果你愿意,我可以谈论更多离线。 但是,我的意思是,有多种方法可以确保您需要访问的所有数据在缓存中保持新鲜。

是的,实际上,我将提供幻灯片。 这就是我们扫描每个人的电子邮件的原因。 所以,我们可以做到这一点,我们还将录制和上传视频。 因此,您可以观看,您的同事也可以观看。

实际上,每个应用程序或……实际上跨多个应用程序。 因此,这一切都取决于事务中的负载。 您至少可以拥有 2 个缓存服务器来拥有集群,然后服务器的数量取决于您拥有多少应用程序服务器或有多少,您基本上有多少活动? 有多少数据要缓存? 您正在执行多少读取和写入操作?

没关系,是的。 是的,一切都会继续工作。 谢谢你们。

接下来做什么?

 

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

联系我们

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