NHibernate 二级缓存性能
NHibernate SecondLevel Cache Performance
在研究 nhibernate 中二级缓存提供的可能性时,我测试了一些实现。目前的结果很出乎意料,我怀疑我的预期是否错误。
- 场景(大量阅读)
首先将 4 * 20000 个带有一个字符串 属性 的简单对象插入到数据库中,然后四个线程通过对象 ID (session.Get<SimpleObj>(id)
) 获取它们。每个线程只访问它创建的 ID。访问模式是随机的,获取 1000 个对象,然后重新创建会话。
while (true)
{
using (var session = sf.OpenSession())
using (var tx = session.BeginTransaction())
{
for (int i3 = 0; i3 < 1000; i3++)
{
long id = (long)ids[rnd.Next(19999)];
var t = session.Get<SimpleObject>(id);
var no = t.StringProperty;
}
Interlocked.Add(ref ops, 1000);
tx.Commit();
}
}
结果
- Redis 每秒 5000 次读取
- MemCached 每秒 8000 次读取 (EnyimMemcached)
- 无缓存每秒 15000 次读取(同一台机器,TCP-IP)
- 无缓存每秒 25000 次读取(同一台机器,共享内存)
- SysCache2 每秒 200000 次读取
- HashtableCacheProvider 每秒 380000 次读取
使用的版本
- NHibernate 4.0.0.4000 和当前版本https://github.com/nhibernate/NHibernate-Caches
- 来自 https://msopentech.com/opentech-projects/redis/
的 Redis 服务器 3.0.501
- MemCached 1.4.5_4_gaa7839e 来自 Couchbase (http://www.couchbase.com/)
这些提供程序与 SysCache2 实现之间的性能差异是否在意料之中?
更新 1
正如弗雷德里克指出的那样,测试场景没有太大意义,因为比较了具有两个不同用例的两个不同 classes 缓存架构。第一个 class (5 & 6) 不能水平缩放,而 1 和 2 可以。当前 SQL-服务器在同一台机器上 运行,因此使用共享内存作为 IPC 机制,因此我禁用了所有 IPC 连接可能性并将其配置为使用 TCP/IP 连接到数据库。结果,无缓存方案的性能下降了大约 10000 op/s,这使最快的分布式缓存提供程序处于合理的距离。
通过对缓存提供程序的比较,我想测试这些提供程序是否可以在每个请求的会话设置中用于缓存参考数据,例如国家、货币或余额-sheet 结构。由于性能最好的分布式缓存的性能仍然只有普通 NHibernate 版本 op/s 的一半,我不确定这是否可行。
SysCache(2)
(或 RtMemoryCache
,如果您也尝试另一个)表现比 Redis
或 MemCached
好得多也就不足为奇了。前者是进程内内存缓存,它们的使用不会引起任何网络 IO 或任何进程间通信。后者是分布式缓存,它们至少意味着进程间通信,并且在大多数情况下是网络IO,这会产生相当高的使用成本。
将分布式缓存性能与非分布式缓存性能进行比较没有任何意义。仅当需要在多个进程之间共享缓存时才应使用分布式缓存。如果您有此要求,您将无法使用非分布式缓存。
令人惊讶的是“无缓存”场景(因此,SQL 查询)比分布式缓存执行得更好。
我认为您的测试描述中缺少一个关键点:在您的场景中是什么导致了 SQL 查询的性能成本?如果数据库服务器托管在与 Web 服务器相同的物理主机上,则与更常见的设置相比,此成本相当低。某些数据库解决方案,如 SQL 服务器甚至可以跳过任何网络 IO,并在与 Web 服务器托管在同一主机上时进行内存通信(通过命名管道)中的进程间通信。
旁注:如果您不考虑配置 SysCache2
通过 SQL 服务器数据更改通知失效,您应该测试 SysCache
,它更轻。 (虽然这可能不会真正改变您的测试场景中的结果。)
寻址更新 1:
首先要确保您确实需要分发缓存。如果满足以下条件,每个进程都可以安全地拥有自己的缓存:
- 缓存数据是只读的(或者最终,如果有一些进程提供陈旧的缓存数据不会在您的应用程序案例中造成问题)。
- 这不会导致太多内存消耗。 (缓存的数据并不大,在多个进程之间复制它不是问题。)
当然,非分布式缓存的数据缓存预热会比较慢,但如果分布式缓存性能太差,这不是重点。
如果您需要分发它,那么您应该评估您的 SQL 服务器是否在负载下成为瓶颈。如果在负载下,您的 SQL 服务器可能成为您应用程序的瓶颈,那么分布式缓存将有助于卸载它。否则,如果在您的设置中,分布式缓存的性能一直比您的数据库服务器差,最好不要使用它们。
旁注:如果仅满足条件 (2.),您还可以尝试使用 SQL 服务器通知失效的非分布式缓存,从而避免陈旧数据。请在此处查看 how to configure SysCache2
以了解 SQL 服务器的通知。
Frederic 已经写过您测试了两种完全不同类型的缓存,所以我不打算再重复一遍。但我会添加一些关于 NH 缓存如何工作的信息并回答 Frederic 的评论:
What is surprising is to have the "no cache" scenario (so, SQL
queries) to perform highly better than distributed caches.
其实没什么惊喜:)你要记住:
- NHibernate 尝试安全地使用缓存并在每个缓存实体上模拟 "transactions"。根据其实现,分布式 (NHibernate) 缓存可以向缓存服务器(锁定和释放实体)发送两个额外的请求,导致性能严重下降
- 不同的缓存使用不同的序列化器。例如,Redis 缓存使用内置的 .NET XML 序列化程序,但速度并不出名 ;)
- 根据配置的过期策略,如果您使用分布式缓存,您可能还会有额外的性能下降
- 某些缓存服务器(如 Redis)可能会由于自身资源压力而删除缓存条目,从而导致严重的性能问题(在这种情况下,NH 将进入缓存服务器,获取缓存未命中,然后才尝试检索实体来自数据库 => 2 次网络调用)
另外我必须说你的测试不是很有代表性。在 运行 进行这些性能测试之前,您需要
- 检查缓存中将存储多少实体
- 这些实体有多少 "puts" 与 "gets"
- 您在缓存实体中有多少链接集合
- 链接集合中平均存储了多少项目
- 你有什么样的实体(只读的,主要是不可变的还是所有的都是可更新的?)
- 等等
当然,运行 测试您的数据而不是虚拟数据集 ;)
在研究 nhibernate 中二级缓存提供的可能性时,我测试了一些实现。目前的结果很出乎意料,我怀疑我的预期是否错误。
- 场景(大量阅读)
首先将 4 * 20000 个带有一个字符串 属性 的简单对象插入到数据库中,然后四个线程通过对象 ID (session.Get<SimpleObj>(id)
) 获取它们。每个线程只访问它创建的 ID。访问模式是随机的,获取 1000 个对象,然后重新创建会话。
while (true)
{
using (var session = sf.OpenSession())
using (var tx = session.BeginTransaction())
{
for (int i3 = 0; i3 < 1000; i3++)
{
long id = (long)ids[rnd.Next(19999)];
var t = session.Get<SimpleObject>(id);
var no = t.StringProperty;
}
Interlocked.Add(ref ops, 1000);
tx.Commit();
}
}
结果
- Redis 每秒 5000 次读取
- MemCached 每秒 8000 次读取 (EnyimMemcached)
- 无缓存每秒 15000 次读取(同一台机器,TCP-IP)
- 无缓存每秒 25000 次读取(同一台机器,共享内存)
- SysCache2 每秒 200000 次读取
- HashtableCacheProvider 每秒 380000 次读取
使用的版本
- NHibernate 4.0.0.4000 和当前版本https://github.com/nhibernate/NHibernate-Caches
- 来自 https://msopentech.com/opentech-projects/redis/ 的 Redis 服务器 3.0.501
- MemCached 1.4.5_4_gaa7839e 来自 Couchbase (http://www.couchbase.com/)
这些提供程序与 SysCache2 实现之间的性能差异是否在意料之中?
更新 1
正如弗雷德里克指出的那样,测试场景没有太大意义,因为比较了具有两个不同用例的两个不同 classes 缓存架构。第一个 class (5 & 6) 不能水平缩放,而 1 和 2 可以。当前 SQL-服务器在同一台机器上 运行,因此使用共享内存作为 IPC 机制,因此我禁用了所有 IPC 连接可能性并将其配置为使用 TCP/IP 连接到数据库。结果,无缓存方案的性能下降了大约 10000 op/s,这使最快的分布式缓存提供程序处于合理的距离。
通过对缓存提供程序的比较,我想测试这些提供程序是否可以在每个请求的会话设置中用于缓存参考数据,例如国家、货币或余额-sheet 结构。由于性能最好的分布式缓存的性能仍然只有普通 NHibernate 版本 op/s 的一半,我不确定这是否可行。
SysCache(2)
(或 RtMemoryCache
,如果您也尝试另一个)表现比 Redis
或 MemCached
好得多也就不足为奇了。前者是进程内内存缓存,它们的使用不会引起任何网络 IO 或任何进程间通信。后者是分布式缓存,它们至少意味着进程间通信,并且在大多数情况下是网络IO,这会产生相当高的使用成本。
将分布式缓存性能与非分布式缓存性能进行比较没有任何意义。仅当需要在多个进程之间共享缓存时才应使用分布式缓存。如果您有此要求,您将无法使用非分布式缓存。
令人惊讶的是“无缓存”场景(因此,SQL 查询)比分布式缓存执行得更好。
我认为您的测试描述中缺少一个关键点:在您的场景中是什么导致了 SQL 查询的性能成本?如果数据库服务器托管在与 Web 服务器相同的物理主机上,则与更常见的设置相比,此成本相当低。某些数据库解决方案,如 SQL 服务器甚至可以跳过任何网络 IO,并在与 Web 服务器托管在同一主机上时进行内存通信(通过命名管道)中的进程间通信。
旁注:如果您不考虑配置 SysCache2
通过 SQL 服务器数据更改通知失效,您应该测试 SysCache
,它更轻。 (虽然这可能不会真正改变您的测试场景中的结果。)
寻址更新 1:
首先要确保您确实需要分发缓存。如果满足以下条件,每个进程都可以安全地拥有自己的缓存:
- 缓存数据是只读的(或者最终,如果有一些进程提供陈旧的缓存数据不会在您的应用程序案例中造成问题)。
- 这不会导致太多内存消耗。 (缓存的数据并不大,在多个进程之间复制它不是问题。)
当然,非分布式缓存的数据缓存预热会比较慢,但如果分布式缓存性能太差,这不是重点。
如果您需要分发它,那么您应该评估您的 SQL 服务器是否在负载下成为瓶颈。如果在负载下,您的 SQL 服务器可能成为您应用程序的瓶颈,那么分布式缓存将有助于卸载它。否则,如果在您的设置中,分布式缓存的性能一直比您的数据库服务器差,最好不要使用它们。
旁注:如果仅满足条件 (2.),您还可以尝试使用 SQL 服务器通知失效的非分布式缓存,从而避免陈旧数据。请在此处查看 how to configure SysCache2
以了解 SQL 服务器的通知。
Frederic 已经写过您测试了两种完全不同类型的缓存,所以我不打算再重复一遍。但我会添加一些关于 NH 缓存如何工作的信息并回答 Frederic 的评论:
What is surprising is to have the "no cache" scenario (so, SQL queries) to perform highly better than distributed caches.
其实没什么惊喜:)你要记住:
- NHibernate 尝试安全地使用缓存并在每个缓存实体上模拟 "transactions"。根据其实现,分布式 (NHibernate) 缓存可以向缓存服务器(锁定和释放实体)发送两个额外的请求,导致性能严重下降
- 不同的缓存使用不同的序列化器。例如,Redis 缓存使用内置的 .NET XML 序列化程序,但速度并不出名 ;)
- 根据配置的过期策略,如果您使用分布式缓存,您可能还会有额外的性能下降
- 某些缓存服务器(如 Redis)可能会由于自身资源压力而删除缓存条目,从而导致严重的性能问题(在这种情况下,NH 将进入缓存服务器,获取缓存未命中,然后才尝试检索实体来自数据库 => 2 次网络调用)
另外我必须说你的测试不是很有代表性。在 运行 进行这些性能测试之前,您需要
- 检查缓存中将存储多少实体
- 这些实体有多少 "puts" 与 "gets"
- 您在缓存实体中有多少链接集合
- 链接集合中平均存储了多少项目
- 你有什么样的实体(只读的,主要是不可变的还是所有的都是可更新的?)
- 等等
当然,运行 测试您的数据而不是虚拟数据集 ;)