如何将 EF Core 应用程序扩展到极致性能?

录制的网络研讨会
作者:罗恩·侯赛因和扎克·汗

EF Core 越来越多地用于高事务 .NET 服务器应用程序(ASP.NET、Web 服务、微服务和其他服务器应用程序)。 而且,这些应用程序面临来自数据库的可扩展性瓶颈,它们可以通过在 EF Core 中使用分布式缓存来消除这些瓶颈。

  • EF Core 新功能介绍(模型、查询、保存数据)
  • 分布式缓存如何解决 EF Core 数据库瓶颈
  • 在 EF Core 应用程序中使用分布式缓存的各种方式
  • 有关使用 EF Core 扩展方法进行缓存的详细信息
  • 处理集合和数据关系的缓存

Entity Framework Core 或 EF Core 是 Microsoft 为 .NET 推出的新的跨平台轻量级对象关系映射引擎,无需开发人员编写的大部分数据访问代码。 如同 .NET Core, EF Core 也是跨平台的,这意味着您可以在 Windows、Linux、Mac 上使用它,并且在开发人员社区中变得非常流行。

如何扩展您的应用程序以实现极致性能以及请求处理能力的线性增长? 因此,您可以扩展您的应用程序,Entity Framework Core 应用程序使用 NCache 扩展方法。 因此,我们列出了一些您可以使用的内容。 我将绘制不同的场景,然后基于这些场景,我将介绍 NCache 您可以在环境中的典型 Entity Framework Core 应用程序中使用的扩展方法以及对象缓存功能。

什么是实体框架/EF Core?

首先,我将笼统地谈论 Entity Framework 和 Entity Framework Core。 我很确定每个人都知道 EF 和 EF Core,但只是为了完整起见,构建一些介绍性细节。

什么是实体框架核心

Entity Framework 是一个可用于 .NET 的 ORM,通过 EF Core,您还可以将其用于 .NET 和 .NET Core. Entity Framework 简化了您的数据库编程。 您可以处理概念对象模型。 因此,您不必直接使用数据模型,它简化了整体数据访问编程。 您不需要自己编写持久代码。 它可以在应用程序中的对象层与您拥有的数据模型之间生成大量交互,并使用 Entity Framework Core 生成的数据库访问。

它对您的 .NET 和 .NET Core 应用程序。 您可以在任何类型的应用程序中使用它,它可以是 Web 应用程序,也可以是典型的服务器应用程序、.NET 或 .NET Core Web 服务、物联网用例或任何其他服务器应用程序都可以利用它,因为它很受欢迎,也因为它易于使用。 它很容易设置,您可以开始使用它。

Entity Framework Core 是一个新的变体。 这是微软正在采取的新方向,它首先是开源的,它是跨平台的。 因此,您可以运行正在使用的应用程序 .NET Core 或者你知道.NET。 它可以在带有 .NET 和 .NET Core 它可以在 Windows 和 Linux 环境上运行,类似地 实体框架核心 跟风,对吧? 因此,如果需要,它允许您在 Windows 和 Linux 环境上运行,并且它本质上是非常轻量级和模块化的。 这实质上意味着您不必遍历您曾经拥有的实体框架中的所有组件。 您可以使用所需的组件使用 EF Core。 您只能为此获得 NuGet 包,并且基于此,您可以在应用程序中逐步构建复杂性。

因此,如果您需要,您知道,在您的应用程序中管理复杂的场景。 而且,最重要的是,它的速度非常快,因为 .NET Core 还有一个特定的重点或性能方面。 因此,Entity Framework Core 的设计遵循相同的准则,其中性能是您能够实现的关键因素之一。

Entity Framework 架构图

因此,这是 Entity Framework Core 的架构图。

架构图

我发现了一个仍然适用的实体框架图,很多东西仍然适用于实体框架核心架构。 这是来自 MSDN。 使用 EF Core,您将拥有 ADO.NET 数据提供程序。 我将使用 SQL Server 作为数据源示例,然后你有 Entity Client 数据阅读器,它构建命令树、数据访问场景,然后你有对象服务,在应用程序端你可以使用 Entity SQL 查询或 LINQ 到实体。 因此,您再次使用对象模型并在幕后由 Entity Framework Core 处理所有其他细节。

您可以运行一个命令来在运行时生成映射和模型,因此,概念模型、EDMX、所有这些映射文件,它们已经摆脱了 Entity Framework Core 中的那些。 因此,重新设置非常容易。 同样,它是模块化的。 因此,您可以引入 NuGet 包,并且可以在几分钟内构建一个非常简单的 Entity Framework Core 应用程序。

什么是可扩展性?

接下来,我将讨论可扩展性,这是我想强调的另一个概念。 为什么需要在使用 Entity Framework Core 的应用程序中进行缓存? 首先,有一个可扩展性的概念。 这是应用程序中的一个非常好的功能,您可以在其中实现高应用程序性能,并且在峰值负载下也是如此,对吧? 因此,如果您能够拥有低延迟和尽可能低的延迟,并且在低用户负载下保持低延迟,假设有 XNUMX 到 XNUMX 个用户,并且如果您的应用程序架构设计可以管理相同的在大量用户请求负载下的低延迟,那么该应用程序将被归类为非常可扩展的应用程序。

而且,线性可扩展性是一个进一步相关的概念,您可以添加越来越多的服务器,并且可以分配负载,并且通过在多个服务器上分配负载,更多的服务器实际上会增加请求处理能力,并且这种增加应该以线性方式增长。 因此,如果您有一个应用程序随着您添加更多服务器而增加其容量,并且该应用程序执行线性增加横幅,该横幅将被归类为线性可扩展应用程序。

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

因此,通常具有高事务性质的应用程序,例如 ASP.NET 或 ASP.NET Core Web 应用程序、Web 服务、IOT、大数据应用程序或任何其他通用 .NET 或 .NET Core 使用 Entity Framework 核心的应用程序,它们需要高可扩展性,对吗? 因此,他们需要在其架构中处理高事务负载。

什么应用程序需要可扩展性

可扩展性问题究竟出在哪里?

那么,问题究竟出在哪里? 为什么你需要一个缓存系统来解决这个问题? 您始终可以创建 Web Farm 和 App Farm,其中 Entity Framework Core 应用程序可以通过创建 Web Farm 或 App Farm 来线性扩展自身。 您可以在前面放置一个负载均衡器。 所以,这不是主要关注点。 您的应用程序层始终可以横向扩展。 这就是 Entity Framework Core 和任何典型的 .NET 或 .NET Core 应用程序被设计。 您可以将请求路由到多个服务器,并且仍然可以将它们组合使用。

主要问题是您的数据存储瓶颈。 通常,如果它是关系数据库,这就是我们正在使用的示例。 数据库通常是所有数据存储的单一来源。 它们非常适合存储。 这是他们最擅长的事情。 但是,当涉及到处理大量请求负载时,例如,您有大量来自应用程序的事务和参考数据负载,数据库并非旨在处理增加的请求负载量。 它可以在低用户负载下正常工作,但是当它开始消耗 CPU 端、内存端的容量时,就会有一个 DBMS 隧道,所有请求都在其中被路由。 您的数据存储有可能成为可扩展性瓶颈。 它会给你带来性能下降,它会放置在运行时生成的队列请求。 因此,您无法添加另一个数据库服务器。

首先,一开始的成本很高,然后就不可能让两台服务器相互结合使用。 因此,它不会增加性能值,它会降低您的性能,也会影响您的最终用户体验,进而影响您的业务。 因为,如果你的应用程序性能很慢,很多用户不会喜欢你的应用程序的缓慢或缓慢的行为。 因此,应用程序始终需要低延迟和高吞吐量,而保持高吞吐量会导致性能损失,这就是数据库倾向于看到它们在哪里简单地降低应用程序的速度,或者在大多数情况下它们也倾向于完全阻塞。

解决方案

在本节中,我们将讨论如何解决这个问题。 解决方案非常简单。 您可以使用 分布式缓存系统 NCache. 它非常快,因为它在内存中。 它具有很强的可扩展性,因为它不仅仅是单一来源。 您可以根据需要将任意数量的服务器添加到缓存集群中,并且缓存集群中的这些服务器可以相互结合工作。 您可以分发您的数据,然后您可以分发请求处理负载,并且通过添加更多服务器,您还可以从系统中获得更多的可伸缩性。 它不能替代您的传统关系数据库。 您将 EF Core 应用程序中的分布式缓存与数据库结合使用。 因此,您要么缓存最容易访问的数据、参考数据,要么也缓存一些事务数据。

而且,我将讨论如何处理不同的缓存场景,以及您可以在应用程序中使用的缓存策略。

部署架构

下面的部署架构将进一步阐明为什么它是比关系数据库更好的选择。

部署架构

首先,分布式缓存位于应用程序和数据库层之间。 而且,通过在两者之间说它本质上是您的应用程序将首先使用缓存,如果它在这里找到数据它应该返回,如果它在这里没有找到数据它应该只去后端数据库,对吗? 因此,在逻辑上,它位于您的应用程序和数据库之间。

就部署而言,您可以将应用程序部署在虚拟机、物理盒、本地或云中。 NCache 在 Microsoft Azure 以及 AWS 上以预配置映像或任何其他云的形式支持可以使用我们的安装程序,可以从我们的网站下载,然后您可以开始安装,这非常容易设置。 NCache 可以在自己的相应层上,单独的专用服务器 NCache 或者它可以在同一个盒子上运行。 如果您有部署应用程序的盒子,则可以部署 NCache 也在同一层。 我们建议使用单独的盒子,您可以添加两到三个服务器开始,然后您可以根据您的请求处理要求添加越来越多的服务器。

如果您需要处理 10,000 个用户及其相关的请求负载,您可以运行一些测试,您可以查看需要多少台服务器。 一般来说,根据最近的测试,我们能够实现每秒 4 万个请求,只需 5 到 XNUMX 个 NCache 服务器,对吧? 因此,您可以查看需要多少台服务器来处理您的应用程序负载要求。 只有先决条件 NCache 是 .NET 或 .NET Core. 它可以在 Windows、Windows Nano 以及 Linux 服务器上运行。 同样,您的应用程序可以在 Windows 平台或 Linux 平台上。

而且,还有一些强大的数据库同步功能。 就像我之前说的,它不能替代您的传统数据库。 它使用您的数据库,它与您的数据库一起使用,对吗? 因此,一些数据将被缓存,但您的所有数据将始终存在于您的永久存储中,即您的关系数据库。 而且,稍后我将讨论如何进行缓存。

3个常用的分布式缓存用例

以下是一些常见的用例。 分布式缓存的一般用例,最突出的是您的应用数据缓存。 我们这里有实体框架核心。

应用程序数据缓存

因此,我们最常见的第一个用例是我们的 App 数据缓存用例。 在这种情况下,您通常会缓存属于数据库的数据。 这里的动机是您尽可能节省昂贵的数据库访问,并且您获得的好处是数据检索的高性能。 因为与内存访问相比,数据库速度较慢,所以它是在内存中的,所以它非常快,然后您有多个服务器托管并服务于您的应用程序的请求。 因此,您可以从系统中获得更高的可扩展性和更高的吞吐量,同时保持为分布式缓存提供的低延迟,并且在大多数情况下,它具有高可用性和可靠性,并且基于您选择的拓扑,这与可能无法复制的数据库不同跨其他服务器。

使用 App 数据缓存,您几乎可以缓存任何内容。 您可以使用直接 API。 您可以缓存您的域对象、集合、数据集、单个项目、结果,以及您想要缓存的任何数据,并且您再次需要该数据您应该考虑使用我们的对象缓存和 Entity Framework Core,您可以再次使用直接 API 或扩展我将向您展示的方法。

ASP.NET 会话缓存和 SignalR Backplane

然后是另一个用例 NCache 是 ASP.NET 和 ASP.NET Core 特定的缓存。 我们有我们的会话缓存。 ASP 中的会话缓存.NET Core 是通过 IDistributedCache 以及我们的 NCache 会话提供者也是如此。 所以,这是第二个选项,然后我们有 SignalR Backplane. 这是您可以利用的另一种选择。 如果您有 SignalR 应用程序,您可以使用 NCache 作为背板。 这又是一个无代码更改选项,然后我们有响应缓存、视图状态和输出缓存。 因此,这些都是 ASP.NET 特有的功能,除此之外,如果需要,您还可以在 Web 应用程序中使用 App 数据缓存。

Pub/Sub 消息传递和连续查询

然后,我们有强大的 Pub/Sub 消息传递和连续查询事件,或者我会说 Pub/Sub 消息传递和事件。 您可以使用 NCache 作为您的消息传递平台,连接多个应用程序并且这些应用程序可以使用我们的 Pub/Sub 消息传递相互通信。 应用程序可以将消息发布到 NCache,它充当沟通渠道。 它有一个主题的概念,然后这些订阅者应用程序可以在连接到该主题时接收该消息。 因此,如果需要,他们可以相互协调共享数据以及自定义消息。 我们可以建立社交媒体系统。 你可以有聊天系统。 你可以有排行榜。 您可以根据需要在任何类型的行业中使用它,在这些行业中,您的应用程序需要使不同的组件相互协调。

微服务之间的通信

微服务是另一个用例,您需要拥有无服务器的微服务应用程序,相互交互并且它们可以使用 NCache 满足他们的通信需求,然后我们还有连续查询事件。 添加、更新或删除您的数据的常规活动。 而不是应用程序发送事件, NCache 可以根据数据更改将事件发送到您的应用程序,这可以针对所有项目或选择性项目或基于您在其中映射数据集的连续查询 NCache 和 NCache 仅发送该指定数据集的事件。

还有第四个用例。 我们有全文搜索功能。 这在电子商务应用程序中很常见。 和 NCache 我们已经实现了 Lucene .NET API。 因此,如果您对全文搜索功能感兴趣, NCache 配备齐全。

EF Core 中的应用程序数据缓存

让我们谈谈如何在典型的 Entity Framework Core 应用程序中使用缓存。

设置示例应用程序

所以,首先,我有这个应用程序。 示例应用程序就在这里排列。 它是随附安装的样本之一 NCache 也是。 所以,如果你去 C:\Program Files\NCache\,这些示例可在此处获得。

.NET 和 .NET Core 样品分开放置。 所以,我会继续 .NET Core 和入口框架,我们在这里有 EF Core 示例。 所以,这就是我正在使用的样本。

分布式缓存用例

我对其进行了一些更改,以演示我计划在本次网络研讨会中展示的其中一些场景。 否则,它将为您的 POC 完成基本工作。 对,所以,这就是样本。 接下来我会登录到我们的演示环境,然后开始为您创建一个缓存集群,以便您实际使用缓存集群并从那里获取它。

创建集群缓存

因此,对于缓存创建,我们已经启动并运行了我们的 Web 管理工具。 我将快速创建一个。 所以,让我们继续添加一个集群缓存。 我们将其命名为 democache。 我要附加几个整数,democache111。 顺便说一句,就序列化而言,您可以保留 JSON 或二进制格式。 你去吧。 因此,您可以使用二进制或 JSON 序列化格式。 我将继续使用二进制,因为这是默认选项。 有四种缓存拓扑可供您选择,我们还有其他网络研讨会,例如 NCache 专门针对的架构,你知道,解释 NCache 建筑学。 因此,我将继续使用分区副本缓存,因为这对于读取非常有效,对于写入非常有效,并且它对于读取和写入请求容量具有超级可扩展性,如果您不断添加更多服务器并且它也配备了副本,因此,您的应用程序也不会出现停机或数据丢失。

所以,我会保持一切简单,默认。 活动分区与其备份之间的异步复制。 所以,我会选择那个,它更快。 缓存集群的大小,在这里我指定了将托管我的缓存集群的服务器。 就像我说的那样,我将快速创建缓存,因为我们的主要关注点是 Entity Framework Core。 否则,在我们的定期网络研讨会中 NCache 架构或扩展 Azure 应用程序 NCache,这些网络研讨会非常详细地讨论了所有这些功能。 因此,在这个特定的网络研讨会中,我将继续快速创建缓存。 对,所以,我会指定默认端口 TCP/IP,然后我会启动并自动启动此缓存,以便在您的服务器重新启动时它会自动启动。 所以,差不多就是这样。

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

就缓存配置而言,您只需要遵循此向导即可在多台服务器上创建缓存。 我想它也开始了。 我会打开统计窗口,然后我会打开 NCache 监控窗口,这是另一个安装的工具 NCache. 目前没有任何活动,但我可以继续运行压力测试工具应用程序。 它被称为测试压力工具,它将模拟一些虚拟活动,我的缓存集群上的虚拟负载。 只是为了验证一切,它已被正确配置。 对,所以,你可以看到,你知道,对,服务器一和服务器二每秒有一千到一千五百个请求。 因此,它每秒总共处理大约三千个请求,您可以查看延迟。 我们每个缓存操作延迟的平均微秒非常小。 我认为它介于 XNUMX 到 XNUMX 微秒之间。

所以,这个图表会更新,单位会下降。 一旦我们,你知道,增加一些负载。 让我们实际上继续这样做。 正确的。 所以,我有另一个实例正在运行。 只是为了告诉你,现在它应该在这里显示增加的价值,你去吧。 所以,我们有大约,以前每个服务器每秒处理大约一千五百个任务。 现在每台服务器每秒的请求在 XNUMX 到 XNUMX 之间,您可以看到每次缓存操作的平均微秒大约是每次操作大约 XNUMX 到 XNUMX 微秒。 这是一个巨大的性能提升。 考虑到您的应用程序没有被最大化或服务器根本没有被最大化。 因此,您可以在使用时从应用程序中获得亚毫秒或微秒的响应 NCache. 这样,我们的环境就配置好了。 一切都设置好了。 我想我们可以走了。 让我停止这些测试,让我们继续创建、查看我们讨论缓存场景的示例应用程序。

要缓存哪些 EF Core 实体?

让我们首先谈谈如何在 Entity Framework Core 中进行缓存。 要缓存哪些 EF Core 实体? 通常,您有两个选择。 您可能有一个单一实体,或者您有一个查询结果,它是一个实体集合,对吧? 返回单个实体,或者它可能是一个集合。 单个实体按原样存储。 集合可以存储为单个项目,也可以将每个集合项目单独添加到缓存中,让我们谈谈如何去做。

EF Core 实体缓存选项

直接 NCache 蜜蜂。 有两种方法。 首先,您可以使用 NCache 直接API,对吧? 所以,这也是你可以在开源、企业或专业中使用的东西。 然后,我们有实体框架核心扩展方法,我将花一些时间来研究。

缓存 EF Core 单一实体: NCache 直接 API

直接 API。 这是 Direct API 的详细信息。 让我从这里给你看,对吧?

Customers GetCustomer (string CustomerId)
{
	string key = "Customer:CustomerId:" + CustomerId;
	Customers customer = (Customers)_cache.Get(key);
	
	if (customer != null)
	{
		return customer;
	}
	else
	{
	
		customer = (from cust in database.Customers
					where cust.CustomerId == CustomerId
					select cust).FirstOrDefault();
		_cache.Insert(key, customer);
		return customer;
}
}

因此,通常这是您获取客户的方法。 如果您使用直接 API,您可能有一个客户 ID。 所以,首先,你需要构造一个缓存键,对吧。 所以,这是你必须要做的事情。 因为,内 NCache 一切都存储在一个键值对中。 Key 是你的字符串键,用来识别一个对象。 对象部分是您要在缓存中添加的实际值、实际属性。 您要为应用程序缓存的实际数据。

所以,我想出了这个关键组织,我将客户作为关键字,然后是客户 ID,然后我提供了一个传递给我的运行时参数,对吗? 所以,这将在这里识别这个特定的客户,客户 ID 是唯一的,对吧? 因此,它将唯一地允许您识别这些客户,然后您调用 cache.Get 从缓存中检索项目。 正确的? 所以,首先,如果你知道,使用缓存,你需要确保你已经构造了键,然后你首先通过调用 cache.Get 检查缓存中的数据,这个缓存句柄是什么初始化缓存时返回的,对吗?

所以,我在这里使用这个缓存, NCache.初始化缓存。 它允许您初始化缓存并连接到它。 如果您在缓存中找到项目,这意味着客户不为空,您只需从这里返回。 您节省了访问数据库的昂贵费用,这是使用缓存系统的主要动机,其中 NCache 会有你的数据。 因此,您可以通过后端数据库节省昂贵的旅行费用。 你不必去数据库。 但是,因为这是第一次启动这个应用程序。 因此,在这种情况下,缓存将没有数据。

因此,在这种情况下,您将执行 Entity Framework Core,链接到 Entity Framework Core。 它将返回单个项目或集合。 这是一个单项场景,然后您调用 cache.Insert 以实际添加它以供下次使用并返回客户。 所以,下一次你总是会在缓存中找到这些数据,只要它没有被更改或没有与数据同步。 所以,这就是我们对实体、直接 API 的单一使用。

缓存 EF Core 实体集合: NCache 直接 API

在集合的情况下,直接 API 非常相似。

List GetCustomersByCity (string CustomerCity)
{
	string key = "Customers:City = " + CustomerCity;
	List custList;
	custList = (List)_cache.Get(key);

	if (custList != null)
	{
		return custList;
	}
	else
	{
		custList = (from cust in database.Customers
                    where cust.City == CustomerCity
                    select cust).ToList();

		_cache.Insert(key, custList);
		return custList;
	}
}     

我们有不同城市的客户。 客户城市是运行时参数。 我们将构建一个客户列表并尝试从 NCache cache.Get,获取客户列表。 如果这不是从这里返回的空值,如果它是空值,我们需要从后端数据库中检索它并将其添加到缓存中以供下次使用。 所以,就是这样,如果你想单独存储这些客户项目,你也可以迭代到这个 custList 并单独调用 cache.Insert 通过为每个集合项目提供唯一的键。 因此,这是您可以利用的另一种选择。

但是你看,你必须首先构造一个键,从缓存中获取项目。 如果存在,则进行 null 处理,如果不存在,则从数据库中获取它,执行数据逻辑,然后添加它。 所以,这就是你必须使用 Direct 做的事情 NCache 蜜蜂。 这是任何典型数据库缓存的最常见用例。 对于应用程序数据缓存,这是您通常会做的事情。

缓存 EF Core 单个实体 - EF Core 扩展方法

但还有另一种方法,即通过我们的扩展方法,这是主要亮点。 您可以将整个集合缓存为一个项目,也可以通过扩展方法单独缓存每个集合项目。

Customers GetCustomer (string CustomerId)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities
	};
	
    Customers customer  = (from cust in database.Customers
                           where cust.CustomerId == CustomerId
                           select cust).FromCache(out string cacheKey,
                           options).FirstOrDefault();
	return customer;
}	

这是我想向您展示的第一个扩展方法。 好吧,所以,它被称为 From Cache 但它为您做了很多自动化,对吧。 它的工作方式首先允许您构建一些缓存选项,对吗? 因此,您首先要创建缓存选项,我现在要介绍的属性之一是 Store As。 所以, NCache 将允许您选择是否需要将集合存储为单个项目、单个项目、集合项目。 假设集合中有 10 个项目。 因此,这些将被添加为 10 个单独的项目 NCache 或者你想把它存储为一个集合,就在这里,对吧? 所以,这是第二种选择。 在这种情况下,它将作为一个项目存储在缓存中。

所以,我使用单独的实体。 所以,如果我在这里运行这段代码,我有一个扩展方法,它说 From Cache,如果我向你展示它的定义,它来自 NCache Alachisoft .NCache.EntityFrameworkCore。 那是主要的命名空间,如果我来这里向您展示 NuGet 包。 在已安装下,我们有 Alachisoft .NCache.EFCore NuGet 包。 因此,这就是您需要引入的 NuGet 包,然后您就可以开始使用这些扩展方法。 首先,它是一个外引用缓存键,因此,缓存键将由 NCache 并给了你。 所以,这就是灵活性,然后它只是将选项作为参数,对。 所以,它做了很多自动化,在幕后它为你做了很多工作。 FromCache 以这样的方式工作,它会首先自动检查缓存中的数据,这就是行为,如果找到,它不会进入数据库。

但是,如果它不在缓存中,那么它会按照规则进入数据库并执行查询,检索结果集,然后使用填充的缓存键将其添加到缓存中,然后将这些缓存选项设置为该项目. 因此,如果将其与此进行比较,则不必构造缓存键。 您不必自己检查 null 处理或从缓存中获取。 您不必将其插入缓存中。 您只需要使用此扩展方法。 因此,这使得很多事情自动化,并且它也为您的应用程序提供了非常简化的编程模型。

缓存 EF Core 实体集合:EF Core 扩展方法

而且,在您希望将其存储为集合的相同情况下,您只需提供缓存选项作为集合,在这种情况下,您有一个您将返回的客户列表。 因此,您只需运行查询,然后再次调用 FromCache。 至此,我们的第一个扩展方法和对它的介绍就完成了。

List GetCustomersByCity (string CustomerCity)
{
List custList; 
	CachingOptions options = new CachingOptions
	{	
		StoreAs = StoreAs.Collection
	};
    
	custList = (from cust in database.Customers
				where cust.City == CustomerCity
				select cust).FromCache(out string cacheKey,
                options).ToList();

	return custList;	
}       

在 EF Core 中缓存哪些数据?

接下来,我将讨论引用和事务数据。 这是主要部分。

在 efcore 中缓存什么数据

您将处理具有大量读取的应用程序,具有大量读取而不是写入的数据,然后可能存在读取和写入的应用程序场景,对吗? 因此,例如,查找数据产品、员工,它们不会频繁更改但它不是静态数据,它会发生变化但更改频率不是很大,并且整个数据应该存在于缓存中。 在这种特殊情况下,我将解释原因,您应该在几小时或几天后过期。 这就是如何保持缓存新鲜,这是另一个部分。

然后我们有交易数据。 我将讨论如何处理事务数据缓存。 它是动态创建的。 订单、账户,这些都是例子。 它的变化非常频繁,通常,历史事务数据不适合缓存。 因为,它应该改变并且应该在您的用户处于活动状态时离开您的应用程序,所以只需要在那个时候。 但根据我们的经验,我们强烈建议您也为事务数据启用缓存。 因为,虽然您没有更改数据仍然处于活动状态,但您可能会多次阅读它,如果您有数百万用户登录,这将导致数百万次请求该数据返回数据库,如果它被缓存了,即使它可能是两到三个请求,但它会发生变化,从性能的角度来看,它仍然是有益的。 因此,我们强烈建议您考虑缓存一些事务(如果不是全部)。

在 EF Core 中缓存引用数据

好的,如何处理 EF Core 中的引用数据缓存?

ef-core 中的缓存参考数据

为此,我有两个步骤。 您可以将整个数据加载到缓存中,这是必须的。 因此,您的所有参考数据都应加载到缓存中,这是我们建议的必须。 为什么? 因为你根本不想去数据库,对吧? 您应该继续并开始从数据库中加载所有数据 NCache 我们有扩展方法可以立即解决这个问题,然后只使用缓存并避免数据库访问。

第二步始终缓存为单独的实体。 这是我给你的另一个提示,你不应该将所有产品或任何其他产品缓存为一个集合,对吗? 它可能是数千种产品或数百万种产品。 但是单独存储它们可以让您在稍后阶段获取数据子集,对吧。 因此,例如,您加载所有产品,但稍后从缓存中您只需要已停产的产品。 因此,如果您已将它们单独存储在缓存中,例如,六万种产品,这就是我将向您展示的示例。 您只能找到当时需要的那些。 因此,您无需处理整个产品数据集并再次节省昂贵的数据库访问。

在 EF Core 中缓存引用数据:预加载缓存

因此,我们有一个名为 LoadIntoCache 的扩展方法,接下来我将向您展示,我还将向您展示一个工作示例。

void LoadAllProducts (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities,
	};	
	
	// Loads all products into cache as individual entities
	var res = (from products in database.Products select
    products).LoadIntoCache(options).ToList();

}

现在,LoadIntoCache,首先,缓存选项应该设置为“存储为单独的实体”,然后你应该运行一个应该加载所有产品的查询,然后你应该调用 LoadIntoCache,然后提供选项,它会再次创建缓存键对于所有这些单独的,自动的。 而且,它会继续加载缓存中的所有这些项目,然后您可以像这样运行 LINQ 查询,而这个 LINQ 查询是针对 NCache. 它没有使用任何扩展方法。 它在 product.Discontinued 并且我们只有 FromCache 的数据库产品中调用产品。 它根本不会进入数据库。 它从 NCache 直。

在 EF Core 中缓存引用数据:仅从缓存中搜索引用数据

如果您有参考数据,则应首先加载单独的实体。 使用 load into cache 将所有产品加载到 NCache. 完成后,您不必访问数据库。 然后使用 FromCacheOnly 而不是 FromCache。 第一个扩展方法是 FromCache,它检查缓存,如果它不在缓存中,则转到数据库。 但是,LoadIntoCache 将加载所有产品,然后 FromCacheOnly 将确保它只与缓存对话,假设所有数据都加载到缓存中。

List<Products> FindDiscontinuedProducts (NorthwindContext database)
{
	//Fetch discontinued products only out of all products 
	List<Products> discontinuedProducts;
	
	discontinuedProducts = (from product in database.Products 
   	 where product.Discontinued == true
   	 select product).FromCacheOnly().ToList();
	
	return discontinuedProducts;

}

让我运行这段代码,以便您看到它的实际效果。 我有一个为这个测试配置的缓存,我将向你展示统计数据。 我一直在玩它。 已加载 60,000 个产品。 所以,让我继续清除内容。 对,所以,让我们回顾一下统计数据。 我的缓存在哪里? 你去吧。 对,所以,它是零项,然后我将继续运行它。 它只会缓存单个项目,然后收集我向您展示的所有代码示例,但我想向您展示 LoadIntoCache 的工作原理,并在此基础上,我也会在那里放置一个断点,然后就可以了。

因此,前两个示例是加载单个项目,然后加载一个集合,我向您展示的初始代码,然后这里实际上是加载所有产品以向您展示参考数据场景。 首先,它存储为单独的实体,它设置了一些优先级,一些依赖关系,它对此进行了详细说明,然后它将继续加载所有产品,从数据库中获取它,我强烈建议你继续运行加载产品,在一段时间后加载到缓存中,因此,您从数据库中检索数据并加载到缓存中总是对数据库起作用。 它总是会进入数据库,无论你在数据库中拥有什么,它都会对数据库执行并将该数据取回 NCache 然后使用 FromCacheOnly 之后。

所以,这就是您处理参考数据的方式。 首先,您将它们分别单独存储。 LoadIntoCache,使用这个扩展方法 LoadIntoCache,它总是针对数据库执行。 它没有优先缓存。 它总是会针对数据库执行,然后将所有内容取回 NCache 然后使用 FromCacheOnly。 就是这么简单。

在 EF Core 中缓存事务数据

交易数据。 您只能加载工作集。 它用于结果集缓存,对吗?

在 efcore 中缓存事务性数据

我个人建议,如果您对城市的客户感兴趣,基于产品的订单,您应该有某种结果集缓存,这就是您应该做的。 您应该根据结果的大小将它们存储为一个集合或单独的实体,如果一个集合的大小不是那么大,假设它最多处理 100 或 200 个项目,将它们存储为一个集合,但是如果有是多个产品、多个订单或客户信息,可归类为交易数据。 将它们存储为单独的实体。 因此,您可以从中获得子集,并且可以最大限度地利用缓存。

在 EF Core 中缓存事务数据 - 获取并缓存为集合

用例再次非常简单。 您只需将其存储为集合或使用 FromCache,您不使用 FromCacheOnly,因为如果它不在缓存中,您想访问数据库。

List<Orders> GetCustomerOrders (string CustomerID)
{
	CachingOptions options = new CachingOptions	
	{
		StoreAs = StoreAs = StoreAs.Collection,
	};

	//Fetch from cache. If not found then fetch from DB.
	orderList = (from customerOrder in database.Orders 
				where customerOrder.Customer.CustomerId==CustomerID 
				select customerOrder)
				.FromCache(out string cacheKey, options).ToList();
	
	return orderList;
}
在 EF Core 中缓存事务数据 - 作为单独的实体获取和缓存
List<Orders> GetCustomerOrders (string CustomerID)
{
	CachingOptions options = new CachingOptions	
	{
		StoreAs = StoreAs.SeperateEntities
	};

	//Fetch from cache. If not found then fetch from DB.
	orderList = (from customerOrder in database.Orders 
				where customerOrder.Customer.CustomerId==CustomerID 
				select customerOrder)
				.FromCache(out string cacheKey, options).ToList();
	return orderList;
}

到目前为止,我们已经介绍了三种扩展方法。 FromCache 自动与缓存和数据库一起使用,而不是在缓存中,您可以从数据库中获取它。 LoadIntoCache 将始终针对数据库执行。 获取对象并将其带到缓存中,FromCacheOnly 始终针对缓存执行,这是唯一真正的数据源。 它不会进入数据库。 所以,我希望这能澄清很多事情。

保持缓存新鲜

下一部分基于如何在 Entity Framework Core 中保持缓存新鲜,这是你们在处理两个不同来源时需要了解的另一个重要概念。

您已启用缓存。 你有后端数据库,它是你的主要数据源,持久数据存储,然后你有缓存,它也有数据的副本。 那么,与数据库相比,如何确保缓存是新鲜的。 所以,让我们花一些时间在这里。

保持缓存新鲜:参考数据

首先,你应该,因为你有整个数据集在缓存中,如果数据库发生变化怎么办,对吧?

保持缓存新鲜参考数据

因此,您需要使缓存中的数据过期,为此我们建议您使用过期,然后自动重新加载数据,这可能是一种选择。 因此,策略一是您使用过期但没有重新加载。 因此,每次数据过期时,它也会自动重新加载到缓存中,为此我们在此处进行了此设置,加载所有产品。

策略 1:使用过期但自动重新加载
void LoadAllProducts (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities,
	};
	
	options.SetAbsoluteExpiration(DateTime.Now.AddHours(10)); 	
    options.SetResyncProviderName("MyEFCoreResyncProvider");
	
	// Load all products into cache with Expiration and Auto-Reload
	var res = (from products in database.Products select
    products).LoadIntoCache(options).ToList();

}

让我从这里给你看,对吧。 因此,首先,您将它们存储为单独的实体,因为这是参考数据用例。 您将它们作为所有产品加载,然后设置某种到期时间,然后设置 options.SetResyncProvider,应该有一个重新加载提供程序,然后将 options.IsSyncEnabled 设置为 true。 所以,自动重新加载,这样它就会在缓存中自动重新加载,以防过期,对吧。 因此,如果您使用 SetResyncProviderName 将自动将自动重新加载标志设置为 true,则这两个属性是一起的。

namespace Alachisoft.NCache.EFSampleResyncProvider
{
    public abstract class EFDefaultResyncProvider : IReadThruProvider
    {
        public virtual void Init(IDictionary parameters, string cacheId)
        {
            db = InitializedDbContext();
        }
        public virtual void LoadFromSource(string key, out 					
        ProviderCacheItem cacheItem)
        {
            cacheItem = new ProviderCacheItem(FetchItemFromDb(key));
            cacheItem.AbsoluteExpiration = DateTime.Now.AddHours(10);
            cacheItem.ResyncItemOnExpiration = true;
        }
        public virtual void Dispose()
        {
            db.Dispose();
        }
    }
}

然后,您需要 ResyncProvider 就在这里,也就是说,我们的示例实现就在这里,我正在展示。

namespace Alachisoft.NCache.EFSampleResyncProvider.Provider
{
    public class EFResncProvider : EFDefaultResyncProvider, 	
    IReadThruProvider
    {
        public override DbContext InitializedDbContext()
        {
            return new NorthwindContext();
        }
    }
}

你去吧。 您需要实现我们的 IReadThruProvider。 初始化您的数据源,在最后处理它,然后您 LoadFromSource 允许您获取密钥并基于该密钥您构造一个 SQL 命令并从数据库中获取项目,我在这里给出了一个示例实现,我们在其中构造了一个从我们拥有的缓存键中进行 SQL 查询。

对,所以,这个示例实现中的那个键也可以在 GitHub 上找到。 因此,它将以这样一种方式工作,即您在缓存中加载的项目,如果我再次运行加载到缓存中,它们将附加到期。 因此,它们将在 XNUMX 到 XNUMX 小时后过期,以过期时间为准,之后此提供程序将启动,它会自动使用 read thru 处理程序调用 LoadFromSource,并将更新的数据引入 NCache. 是的,所以你的缓存会在项目过期后自动更新。

策略2:不要使用过期,手动重新加载

我个人推荐的第二种方法是不要使用表达式,通过调用 LoadIntoCache 手动重新加载。 而且,如果我再向您展示一次,您应该想出这个 LoadIntoCache 方法,这非常简单。 间隔一段时间后继续调用此方法,不要使用任何表达式。 让我们摆脱这些。

void LoadAllProducts (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities,
	};
		
	var res = (from products in database.Products select
    products).LoadIntoCache(options).ToList();

}

因此,您知道这将是参考数据仅对假设一小时、两小时、五天、一周、一个月有效。 基于此,不断重复加载所有产品。 这应该在一段时间后调用,对吧?

所以,这就是您应该根据对到期间隔所做的智能猜测手动重新加载数据的想法,对吧? 所以,你应该想出工作时间设置,之后你应该自动调用加载所有产品。 因此,它会自动从后端数据库获取数据,并且您的参考数据会保持最新。

所以,我要重申这些。 所以,有两种选择。 如果您使用过期数据将从缓存中删除。 所以,你最终会得到部分集,所以,你需要自动重新加载。 但是,如果您不使用过期,您将始终将所有数据都放在缓存中作为参考数据,然后您可以在特定时间间隔后手动重新加载该数据。 我希望澄清。

保持缓存新鲜:事务数据

接下来,我将讨论为事务数据保持缓存新鲜,这非常简单。 您应该始终使用短期到期而不自动重新加载。 因为,这又是一个短暂的数据,可能仅在五到十分钟内有效,您应该使用 FromCache,这样如果它不在缓存中,您应该始终从后端数据库中获取它。

而且,这是一个例子,我们有客户订单,将它们存储为集合或单个项目,这完全取决于您。 如果需要,短期到期或不使用到期,或使用到期然后不使用任何自动重新加载。 这样它就会在过期后立即从数据库中检索。 我个人建议使用可配置的到期时间,或者提出您确定的到期时间,这是该数据集的工作时间。 所以在那之后就不需要了。 因此,它应该自动过期,然后您将强制执行数据库访问,并自动从后端数据库获取它。

有效期短,无自动重新加载
 private List<Orders> GetCustomerOrders (string CustomerID)
{
	CachingOptions options = new CachingOptions	
	{
		StoreAs = StoreAs = StoreAs.Collection
	};
	
	options.SetAbsoluteExpiration(DateTime.Now.AddSeconds(60));

    	List<Orders> orderList = (from customerOrder in database.Orders 					
        where customerOrder.Customer.CustomerId==CustomerID 
        select customerOrder).FromCache(out string cacheKey,
        options).ToList();

	return orderList;
 }

因此,我们已经介绍了如何处理参考数据以及事务数据。 然后我们还介绍了如何保持缓存的最新状态以供参考,以及事务数据。

处理缓存中的关系

一对多

您可能会在缓存中遇到其他一些事情。 处理 EF Core 缓存中的关系。 同样,它非常简单。 支持包含,所以如果您有带有数据库区域的区域,然后您会在它们旁边获得 region.Territories,对吗? 因此,您可以调用 FromCache,它将分别存储区域和区域,它们将制定区域和区域之间的关系。 因此,如果我向您展示具有领土的区域。

List<Region> GetRegionWithTerritories(NorthwindContext database)
{
	List<Region> regionDetails;
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.SeperateEntities
	};

	regionDetails = (from region in database.Region select region)
					.Include(region => region.Territories)
					.FromCache(options).ToList();

	return regionDetails;
}

这是一个例子。 正确的。 因此,此区域详细信息包括,然后我们使用 FromCache。 因此,我们将分别存储区域和区域领土,单独的项目,然后我们将构建一个基于键的依赖项。 如果地区发生变化,地区也将失效,反之亦然。 所以,这就是你处理一对一或一对多关系的方式。

缓存聚合操作

还支持聚合操作。 因此,您可以使用 Deferred First 或 Default FromCache 来运行这些扩展方法。 它可以基于延迟计数,FromCache。 所以,它会将它们存储为结果集,对吧? 因此,将它们存储为集合或单个项目并不重要,因为这只是聚合操作的结果。 所以,这是我们的实体框架的另一种可能性。

缓存聚合操作 - 返回实体

Shippers GetFirstShipperInstance (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{ 
		StoreAs = StoreAs.Collection
	};

	Shippers shipper = database.Shippers.DeferredFirstOrDefault()
						.FromCache(out string cacheKey, options);

	return shipper;

}

缓存聚合操作 - 返回值

int GetTotalShippersCount (NorthwindContext database)
{
	CachingOptions options = new CachingOptions
	{
		StoreAs = StoreAs.Collection 
	};

	int count = database.Shippers.DeferredCount()
				.FromCache(out 	string cacheKey, options);
	
	return count;

}

分布式缓存架构

所以,到最后,我想谈谈关于分布式缓存的一些架构细节。 为什么你应该考虑它。 它高度可用,超级可靠,具有复制功能。 这是一个点对点架构的缓存。 没有单点故障。 您可以在运行时添加或删除任何服务器,并且客户端可以为其构建连接故障转移支持。 因此,它具有 100% 的正常运行时间,高度可用且超级可靠。 它带有许多缓存拓扑、客户端缓存、WAN 复制、分区和分区副本,如果有任何问题,我可以专门讨论架构细节,否则到此结束我们的演示。

高可用性

高可用性

缓存拓扑

缓存拓扑

客户端缓存(靠近缓存)

客户端缓存

缓存的广域网复制

万复制

结论

我想快速回顾一下。 在本次网络研讨会中,我们讨论了缓存选项、直接 API、实体框架核心扩展方法。 因此,我们可以在这些之间进行选择。 我们更专注于实体框架核心扩展方法,因为这是我们想要的项目。 它更容易使用。 如何处理参考和交易数据。 因此,我们讨论了加载整个数据以获取参考数据的方法,然后使用单独的实体方法,然后仅对缓存中的所有数据使用缓存。 对于事务性数据,我们建议只缓存到结果集,然后使用 FromCache 扩展方法,这样如果数据库不在缓存中,您就可以转到数据库。 然后,为了保持缓存新鲜,我们谈到您应该使用过期自动重新加载参考数据,或者不使用过期但在特定间隔后手动重新加载,对于事务数据确保使用过期,对吗? 那应该是短暂的过期但没有自动重新加载,因此,您可以返回数据库并在下次使用时刷新缓存。

您可以随时联系我们 support@alachisoft.com. 如果您有任何技术问题,也可以通过以下方式联系我们 sales@alachisoft.com. 如果你想看看产品,你可以下载 NCache Enterprise 30 天免费试用。

接下来做什么?

 

联系我们

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