27

现代数据库提供缓存支持。大多数 ORM 框架也会缓存检索到的数据。为什么这种复制是必要的?

4

9 回答 9

44

因为要从数据库的缓存中获取数据,您仍然必须:

  1. 从 ORM 的“本机”查询格式生成 SQL
  2. 对数据库服务器进行网络往返
  3. 解析 SQL
  4. 从缓存中获取数据
  5. 将数据序列化为数据库的无线格式
  6. 将数据反序列化为数据库客户端库的格式
  7. 将数据库客户端库的格式转换为语言级别的对象(即任何东西的集合)

通过在应用程序级别进行缓存,您无需执行任何操作。通常,这是对内存中哈希表的简单查找。有时(如果使用 memcache 缓存)仍然存在网络往返,但所有其他事情都不再发生。

于 2010-06-03T06:55:37.443 回答
12

以下是您可能想要这个的几个原因:

  • 应用程序只缓存它需要的内容,因此您应该获得更好的缓存命中率
  • 由于网络延迟,访问本地缓存可能比访问数据库快几个数量级——即使网络速度很快
于 2010-06-03T06:54:56.163 回答
12

使用强一致性缓存扩展读写事务

通过添加更多的Replica 节点,可以相当容易地扩展只读事务

但是,这不适用于主节点,因为它只能垂直缩放:

这就是缓存发挥作用的地方。对于需要在Primary节点上执行的读写数据库事务,缓存可以通过将其定向到强一致性缓存来帮助您减少查询负载,例如Hibernate二级缓存:

扩展读写事务

使用分布式缓存

由于几个原因,在应用程序的内存中存储应用程序级缓存是有问题的。

首先,应用程序内存是有限的,因此可以缓存的数据量也是有限的。

其次,当流量增加并且我们想要启动新的应用程序节点来处理额外的流量时,新节点会从冷缓存开始,这会使问题变得更糟,因为它们会导致数据库负载达到峰值,直到缓存中填充数据:

内存应用程序缓存

为了解决这个问题,最好让缓存作为分布式系统运行,比如 Redis。这样,缓存的数据量不受单个节点上的内存大小的限制,因为可以使用分片在多个节点之间拆分数据。

分布式缓存

并且,当自动缩放器添加新的应用程序节点时,新节点将从同一个分布式缓存中加载数据。因此,不再有冷缓存问题。

于 2021-10-12T14:00:27.163 回答
7

即使数据库引擎缓存了数据、索引或查询结果集,您的应用程序仍需要往返数据库才能从该缓存中受益。

ORM 框架与您的应用程序在同一空间中运行。所以没有往返。这只是一个内存访问,通常要快得多。

该框架还可以决定在需要时将数据保留在缓存中。当其他并发客户端发出使用缓存的请求时,数据库可能会决定在不可预测的时间使缓存数据过期。

您的应用程序端 ORM 框架还可能以数据库无法返回的形式缓存数据。例如,以 java 对象集合的形式而不是原始数据流的形式。如果您依赖数据库缓存,您的 ORM 必须重复该转换为对象,这会增加开销并降低缓存的好处。

于 2010-06-03T06:59:37.677 回答
6

此外,数据库的缓存可能不像人们想象的那么实用。我从http://highscalability.com/bunch-great-strategies-using-memcached-and-mysql-better-together复制了这个——它是 MySQL 特有的。

鉴于 MySQL 有缓存,为什么还需要 memcached?

MySQL 缓存只与一个实例相关联。这将缓存限制为一台服务器的最大地址。如果您的系统大于一台服务器的内存,那么使用 MySQL 缓存将不起作用。如果从另一个实例中读取相同的对象,则它不会被缓存。

查询缓存在写入时失效。你建立了所有的缓存,当有人写它时它就会消失。根据使用模式,您的缓存可能根本不是缓存。

查询缓存是基于行的。Memcached 可以缓存您想要的任何类型的数据,并且不仅限于缓存数据库行。Memcached 可以缓存复杂的复杂对象,无需连接即可直接使用。

于 2010-06-03T07:06:26.337 回答
6

已正确指出与网络往返有关的性能注意事项。

为此,必须补充一点,将数据缓存在 dbms(不是“数据库”)中的任何其他位置会产生一个问题,即可能已过时的数据仍被呈现为“最新”的数据。

屈服于性能改进的诱惑是以失去绝对可靠和有保证的正确和一致数据的保证(无懈可击或至少接近)为代价的。

每次准确性和一致性至关重要时都要考虑这一点。

于 2010-06-03T08:43:12.793 回答
6

这里有很多很好的答案。我还要补充一点:我知道我的访问模式,而数据库不知道。

根据我正在做的事情,我知道如果数据最终过时,那并不是真正的问题。数据库没有,并且必须用新数据重新加载缓存。

我知道我会在接下来的一段时间内多次返回某个数据,因此保留它很重要。数据库必须猜测要在缓存中保留什么,它没有我所做的信息。因此,如果我一遍又一遍地从数据库中获取它,如果服务器很忙,它可能不在缓存中。我可能会出现缓存未命中。有了我的缓存,我可以确定我会成功。对于获取非平凡数据(即一些连接、一些组函数)而不是仅获取一行的数据尤其如此。获取主键为 7 的行对数据库来说很容易,但如果必须做一些实际工作,缓存未命中的成本要高得多。

于 2010-06-05T13:40:28.280 回答
4

毫无疑问,现代数据库正在提供缓存设施,但是当您的站点上有更多流量并且您需要执行许多数据库事务时,您将无法获得高性能。因此,在这种情况下,为了提高性能,休眠缓存将帮助您,通过优化数据库应用程序。缓存实际上存储了已经从数据库加载的数据,这样当应用程序想要再次访问该数据时,我们的应用程序和数据库之间的流量就会减少。应用程序和数据库之间的访问时间和流量也会减少。

于 2010-06-05T13:16:55.870 回答
3

也就是说 - 缓存有时会成为一种负担,实际上会减慢服务器的速度。当你有高负载时,缓存的算法和不适合的算法可能不适合传入的请求......你得到的是一个缓存,它开始像 FIFO 一样超时运行......这开始让自己知道当位于缓存后面的表的记录比内存中缓存的要多得多时...

一个很好的折衷办法是将数据聚类为您想要缓存的内容。有一个将更新泵送至集群的主服务器,发送/泵送更新的时间应该能够根据 TTL(生存时间)设置为每个表定制。

然后,您在用户节点上的逻辑和数据可以位于在内存数据库中打开的同一台服务器上,或者如果它确实必须获取数据,那么您可以将其设置为使用管道而不是网络调用......

这需要考虑一下您希望如何使用数据以及何时/如果您进行集群,那么您必须了解分布式事务(多个数据库上的事务)......但是如果缓存的数据将被更新本身没有链接到其他数据库空间,那么你可以摆脱这个....

ORM 缓存的问题在于,如果数据库是通过另一个应用程序独立更新的,那么 ORM 缓存可能会过时……此外,如果您对集合进行更新,它可能会变得很棘手……更新可能会更新一些在你的缓存中,它需要某种算法来识别哪些记录需要在内存中删除/更新(减慢更新速度!?) - 然后这个算法变得非常棘手并且容易出错!

如果使用 ORM 缓存,那么请遵循一个简单的规则...缓存几乎不会更改的简单对象(例如用户/角色详细信息)并且尺寸很小并且在请求中被多次命中...如果它不在此范围内然后我建议对数据进行聚类以提高性能。

于 2011-06-08T09:59:40.130 回答