嵌入的 JCache Hazelcast 无法扩展
JCache Hazelcast embedded does not scale
您好,Whosebug 社区。
我有一个 Spring 启动应用程序,它使用带有 Hazelcast 实现的 Jcache 作为缓存框架。
每个 Hazelcast 节点有 5 个缓存,每个缓存大小为 50000 个元素。有 4 个 Hazelcast 实例组成一个集群。
我遇到的问题如下:
我有一个非常繁重的调用,它从所有四个缓存中读取数据。在初始启动时,当所有缓存都为空时,此调用最多需要 600 秒。
当有一个Hazelcast实例运行并且所有5个缓存都充满了数据时,这个调用发生得比较快,平均只需要4秒。
当我启动 2 个 Hazelcast 实例并形成一个集群时,响应时间变得更糟,同一个调用平均需要 25 秒。
我在集群中添加的 Hazelcast 实例越多,响应时间就越长。当然,当数据在集群中的 Hazelcast 节点之间进行分区时,我预计会看到更糟糕的交付时间。但是没想到仅仅多加一个hazelcast实例,响应时间就慢了6-7倍...
请注意,出于简单原因和测试目的,我只在 一台 机器上启动了四个 Spring 启动实例,每个 Hazelcast 嵌入式节点都嵌入其中。因此,这种糟糕的性能不能用网络延迟来解释。我认为即使使用 Hazelcast,这个 API 调用也很慢,因为在 Hazelcast 集群节点之间发送时需要 serialized/deserialized 很多数据。如有不妥请指正
缓存数据在所有节点之间平均分配。我正在考虑添加近缓存以减少延迟,但是,根据 Hazelcast 文档,Jcache 成员无法使用近缓存。就我而言,由于某些项目要求,我无法切换到 Jcache 客户端来使用 Near Cache。在这种情况下,是否有一些关于如何减少延迟的建议?
提前致谢。
演示问题的虚拟代码示例:
- Hazelcast 配置:保持默认,没有任何改变
- 缓存:
private void createCaches() {
CacheConfiguration<?, ?> cacheConfig = new CacheConfig<>()
.setEvictionConfig(
new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(150000)
.setMaxSizePolicy(MaxSizePolicy.ENTRY_COUNT)
)
.setBackupCount(5)
.setInMemoryFormat(InMemoryFormat.OBJECT)
.setManagementEnabled(true)
.setStatisticsEnabled(true);
cacheManager.createCache("books", cacheConfig);
cacheManager.createCache("bottles", cacheConfig);
cacheManager.createCache("chairs", cacheConfig);
cacheManager.createCache("tables", cacheConfig);
cacheManager.createCache("windows", cacheConfig);
}
- 虚拟控制器:
@GetMapping("/dummy_call")
public String getExampleObjects() { // simulates a situatation where one call needs to fetch data from multiple cached sources.
Instant start = Instant.now();
int i = 0;
while (i != 50000) {
exampleService.getBook(i);
exampleService.getBottle(i);
exampleService.getChair(i);
exampleService.getTable(i);
exampleService.getWindow(i);
i++;
}
Instant end = Instant.now();
return String.format("The heavy call took: %o seconds", Duration.between(start, end).getSeconds());
}
- 虚拟服务:
@Service
public class ExampleService {
@CacheResult(cacheName = "books")
public ExampleBooks getBook(int i) {
try {
Thread.sleep(1); // just to simulate slow service here!
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Book(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "bottles")
public ExampleMooks getBottle(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Bottle(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "chairs")
public ExamplePooks getChair(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Chair(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "tables")
public ExampleRooks getTable(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Table(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "windows")
public ExampleTooks getWindow(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Window(Integer.toString(i), Integer.toString(i));
}
}
如果你算一下:
4s / 250 000 次查找是每次本地查找 0.016 毫秒。这似乎相当高,但让我们接受它。
当您添加单个节点时,数据将被分区,并且一半的请求将从另一个节点提供服务。如果您再添加 2 个节点(总共 4 个),那么 25% 的请求将在本地提供服务,而 75% 的请求将通过网络提供服务。这应该可以解释为什么当您添加更多节点时响应时间会增加。
即使是在本地主机上进行简单的 ping 操作也需要两倍或更多的时间。在真实网络上,我们在基准测试中看到的读取延迟是每次读取调用 0.3-0.4 毫秒。这使得:
0.25 * 250k *0.016 + 0.75 * 250k * 0.3 = ~57 秒
您根本无法通过网络(即使是本地电话)连续拨打这么多电话,您需要
- 并行调用 - 使用
javax.cache.Cache#getAll
减少调用次数
- 您可以尝试通过
com.hazelcast.config.MapConfig#setReadBackupData
启用 reading local backups 以减少网络请求。
读取备份数据功能仅适用于 IMap,因此您需要使用 Spring 缓存与 hazelcast-spring
模块及其 com.hazelcast.spring.cache.HazelcastCacheManager
:
@Bean
HazelcastCacheManager cacheManager(HazelcastInstance hazelcastInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
有关详细信息,请参阅 documentation。
您好,Whosebug 社区。
我有一个 Spring 启动应用程序,它使用带有 Hazelcast 实现的 Jcache 作为缓存框架。
每个 Hazelcast 节点有 5 个缓存,每个缓存大小为 50000 个元素。有 4 个 Hazelcast 实例组成一个集群。
我遇到的问题如下:
我有一个非常繁重的调用,它从所有四个缓存中读取数据。在初始启动时,当所有缓存都为空时,此调用最多需要 600 秒。
当有一个Hazelcast实例运行并且所有5个缓存都充满了数据时,这个调用发生得比较快,平均只需要4秒。
当我启动 2 个 Hazelcast 实例并形成一个集群时,响应时间变得更糟,同一个调用平均需要 25 秒。
我在集群中添加的 Hazelcast 实例越多,响应时间就越长。当然,当数据在集群中的 Hazelcast 节点之间进行分区时,我预计会看到更糟糕的交付时间。但是没想到仅仅多加一个hazelcast实例,响应时间就慢了6-7倍...
请注意,出于简单原因和测试目的,我只在 一台 机器上启动了四个 Spring 启动实例,每个 Hazelcast 嵌入式节点都嵌入其中。因此,这种糟糕的性能不能用网络延迟来解释。我认为即使使用 Hazelcast,这个 API 调用也很慢,因为在 Hazelcast 集群节点之间发送时需要 serialized/deserialized 很多数据。如有不妥请指正
缓存数据在所有节点之间平均分配。我正在考虑添加近缓存以减少延迟,但是,根据 Hazelcast 文档,Jcache 成员无法使用近缓存。就我而言,由于某些项目要求,我无法切换到 Jcache 客户端来使用 Near Cache。在这种情况下,是否有一些关于如何减少延迟的建议?
提前致谢。
演示问题的虚拟代码示例:
- Hazelcast 配置:保持默认,没有任何改变
- 缓存:
private void createCaches() {
CacheConfiguration<?, ?> cacheConfig = new CacheConfig<>()
.setEvictionConfig(
new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(150000)
.setMaxSizePolicy(MaxSizePolicy.ENTRY_COUNT)
)
.setBackupCount(5)
.setInMemoryFormat(InMemoryFormat.OBJECT)
.setManagementEnabled(true)
.setStatisticsEnabled(true);
cacheManager.createCache("books", cacheConfig);
cacheManager.createCache("bottles", cacheConfig);
cacheManager.createCache("chairs", cacheConfig);
cacheManager.createCache("tables", cacheConfig);
cacheManager.createCache("windows", cacheConfig);
}
- 虚拟控制器:
@GetMapping("/dummy_call")
public String getExampleObjects() { // simulates a situatation where one call needs to fetch data from multiple cached sources.
Instant start = Instant.now();
int i = 0;
while (i != 50000) {
exampleService.getBook(i);
exampleService.getBottle(i);
exampleService.getChair(i);
exampleService.getTable(i);
exampleService.getWindow(i);
i++;
}
Instant end = Instant.now();
return String.format("The heavy call took: %o seconds", Duration.between(start, end).getSeconds());
}
- 虚拟服务:
@Service
public class ExampleService {
@CacheResult(cacheName = "books")
public ExampleBooks getBook(int i) {
try {
Thread.sleep(1); // just to simulate slow service here!
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Book(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "bottles")
public ExampleMooks getBottle(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Bottle(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "chairs")
public ExamplePooks getChair(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Chair(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "tables")
public ExampleRooks getTable(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Table(Integer.toString(i), Integer.toString(i));
}
@CacheResult(cacheName = "windows")
public ExampleTooks getWindow(int i) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Window(Integer.toString(i), Integer.toString(i));
}
}
如果你算一下:
4s / 250 000 次查找是每次本地查找 0.016 毫秒。这似乎相当高,但让我们接受它。
当您添加单个节点时,数据将被分区,并且一半的请求将从另一个节点提供服务。如果您再添加 2 个节点(总共 4 个),那么 25% 的请求将在本地提供服务,而 75% 的请求将通过网络提供服务。这应该可以解释为什么当您添加更多节点时响应时间会增加。
即使是在本地主机上进行简单的 ping 操作也需要两倍或更多的时间。在真实网络上,我们在基准测试中看到的读取延迟是每次读取调用 0.3-0.4 毫秒。这使得:
0.25 * 250k *0.016 + 0.75 * 250k * 0.3 = ~57 秒
您根本无法通过网络(即使是本地电话)连续拨打这么多电话,您需要
- 并行调用 - 使用
javax.cache.Cache#getAll
减少调用次数 - 您可以尝试通过
com.hazelcast.config.MapConfig#setReadBackupData
启用 reading local backups 以减少网络请求。
读取备份数据功能仅适用于 IMap,因此您需要使用 Spring 缓存与 hazelcast-spring
模块及其 com.hazelcast.spring.cache.HazelcastCacheManager
:
@Bean
HazelcastCacheManager cacheManager(HazelcastInstance hazelcastInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
有关详细信息,请参阅 documentation。