问题 Hazelcast与Ignite基准测试


我使用数据网格作为我的主要“数据库”。我注意到Hazelcast和Ignite查询性能之间存在巨大差异。我通过适当的自定义序列化和索引优化了我的数据网格使用,但差异仍然是明显的IMO。

由于没有人在这里问过,我将回答我自己的问题以供将来参考。这不是一个抽象(学习)练习,而是一个真实的基准测试,用于模拟大型SaaS系统中的数据网格使用 - 主要用于显示已排序和过滤的分页列表。我主要想知道,与原始无框架Hazelcast和Ignite使用相比,我的通用JDBC-ish数据网格访问层增加了多少开销。但是因为我比较苹果和苹果,所以这里是基准。


4004
2017-08-11 03:44


起源



答案:


我已经在GitHub上查看了提供的代码并且有很多评论:

索引和连接

  1. 可能最重要的一点是Apache Ignite索引要比Hazelcast复杂得多。与Hazelcast不同,Ignite支持ANSI 99 SQL,因此您可以随意编写查询。
  2. 最重要的是,与Hazelcast不同,Ignite支持跨不同缓存或数据类型的组索引和SQL JOIN。想象一下,您有人员和组织表,并且您需要选择为同一组织工作的所有人员。这在Hazelcast的一步中是不可能的(如果我错了,请纠正我),但在Ignite中它是一个简单的SQL JOIN查询。

鉴于上述情况,Ignite索引需要更长时间才能创建,尤其是在您的测试中,其中有7个。

TestEntity类中的修复

在您的代码中,您存储在缓存中的实体, TestEntity,重新计算值 IDSORTcreatedAtSort,和 modifiedAtSort 每次呼叫吸气器。当实体存储在索引树中时,Ignite会多次调用这些getter。对TestEntity类的简单修复提供了4倍的性能提升: https://gist.github.com/dsetrakyan/6bfe089d53f888448503

堆测量不准确

测量堆的方式不正确。你应该至少打电话 System.gc()的 在进行堆测量之前,即使这样也不准确。例如,在下面的结果中,我使用您的方法获得负堆大小。

暖身

每个基准都需要热身。例如,当我申请时 TestEntity 修复,如上所述,并进行2次缓存填充和查询,我得到更好的结果。

MySQL比较

我不认为将单节点数据网格测试与MySQL相比是公平的,无论是Ignite还是Hazelcast。数据库有自己的缓存,每当使用如此小的内存大小时,您通常会测试数据库内存缓存与数据网格内存缓存。

每当在分区缓存上进行分布式测试时,通常都会带来性能优势。这样,数据网格将并行执行每个群集节点上的查询,结果应该更快地返回。

结果

以下是我为Apache Ignite获得的结果。在我做了上述修复后,它们看起来好多了。

请注意,第二次执行缓存填充和缓存查询时,我们会得到更好的结果,因为HotSpot JVM已经预热。

值得一提的是 Ignite不会缓存查询结果。每次运行查询时,都是从头开始执行。

[00:45:15] Ignite node started OK (id=0960e091, grid=Benchmark)
[00:45:15] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=8.0GB]
Starting - used heap: 225847216 bytes
Inserting 100000 records: ....................................................................................................
Inserted all records - used heap: 1001824120 bytes
Cache: 100000 entries, heap size: 775976904 bytes, inserts took 14819 ms
------------------------------------
Starting - used heap: 1139467848 bytes
Inserting 100000 records: ....................................................................................................
Inserted all records - used heap: 978473664 bytes
Cache: 100000 entries, heap size: **-160994184** bytes, inserts took 11082 ms
------------------------------------
Query 1 count: 100, time: 110 ms, heap size: 1037116472 bytes
Query 2 count: 100, time: 285 ms, heap size: 1037116472 bytes
Query 3 count: 100, time: 19 ms, heap size: 1037116472 bytes
Query 4 count: 100, time: 123 ms, heap size: 1037116472 bytes
------------------------------------
Query 1 count: 100, time: 10 ms, heap size: 1037116472 bytes
Query 2 count: 100, time: 116 ms, heap size: 1056692952 bytes
Query 3 count: 100, time: 6 ms, heap size: 1056692952 bytes
Query 4 count: 100, time: 119 ms, heap size: 1056692952 bytes
------------------------------------
[00:45:52] Ignite node stopped OK [uptime=00:00:36:515]

我将使用更正后的代码创建另一个GitHub仓库,并在我醒来时将其发布在此处(咖啡不再帮助了)。


10
2017-08-11 08:36



谢谢,德米特里。只是我项目的简史。我会私下给你们写一封信。我对Ignite有很大的计划。 - Alex Rogachevsky
谢谢,德米特里。只是我项目的简史。我用Hazelcast启动了Px100(如果你想知道的话,它在之前的版本中使用了标准的SQL / JPA)。我并不天真地认为它只是“在内存中”胜过MySQL,但我预计至少会有类似的表现。像其他人一样,我使用Serializable而不关心索引。我根据Hazelcast的官方推荐对其进行了优化。它改进了一些东西,但问题再次出现在大型数据集上。我会给你们写一封关于Px100的私信。我对Ignite有很大的计划。 - Alex Rogachevsky
@AlexRogachevsky Alex,听起来不错。期待听到您对Apache Ignite的计划。 - Dmitriy
德米特里,我按照你的计算吸气剂的建议,但你也应该确保你打电话给他们一次,然后传递该值。对于任何东西来说,这些吸气剂都很常见,但DTO风格的实体却很愚蠢。我的第二个问题是关于复合指数。我想在文档中的javadoc +详细示例中解释IgniteBiTuple。布尔意味着什么:可能是提升。像这样的东西并不总是“马会明白那种明显的东西”。最后,你有一个严重的性能错误 - 当指定log4j log log / log4j config时。 - Alex Rogachevsky
@AlexRogachevsky以下是Ignite SQL功能的文档,包括复合索引: apacheignite.gridgain.org/v1.3/docs/sql-queries - Dmitriy


答案:


我已经在GitHub上查看了提供的代码并且有很多评论:

索引和连接

  1. 可能最重要的一点是Apache Ignite索引要比Hazelcast复杂得多。与Hazelcast不同,Ignite支持ANSI 99 SQL,因此您可以随意编写查询。
  2. 最重要的是,与Hazelcast不同,Ignite支持跨不同缓存或数据类型的组索引和SQL JOIN。想象一下,您有人员和组织表,并且您需要选择为同一组织工作的所有人员。这在Hazelcast的一步中是不可能的(如果我错了,请纠正我),但在Ignite中它是一个简单的SQL JOIN查询。

鉴于上述情况,Ignite索引需要更长时间才能创建,尤其是在您的测试中,其中有7个。

TestEntity类中的修复

在您的代码中,您存储在缓存中的实体, TestEntity,重新计算值 IDSORTcreatedAtSort,和 modifiedAtSort 每次呼叫吸气器。当实体存储在索引树中时,Ignite会多次调用这些getter。对TestEntity类的简单修复提供了4倍的性能提升: https://gist.github.com/dsetrakyan/6bfe089d53f888448503

堆测量不准确

测量堆的方式不正确。你应该至少打电话 System.gc()的 在进行堆测量之前,即使这样也不准确。例如,在下面的结果中,我使用您的方法获得负堆大小。

暖身

每个基准都需要热身。例如,当我申请时 TestEntity 修复,如上所述,并进行2次缓存填充和查询,我得到更好的结果。

MySQL比较

我不认为将单节点数据网格测试与MySQL相比是公平的,无论是Ignite还是Hazelcast。数据库有自己的缓存,每当使用如此小的内存大小时,您通常会测试数据库内存缓存与数据网格内存缓存。

每当在分区缓存上进行分布式测试时,通常都会带来性能优势。这样,数据网格将并行执行每个群集节点上的查询,结果应该更快地返回。

结果

以下是我为Apache Ignite获得的结果。在我做了上述修复后,它们看起来好多了。

请注意,第二次执行缓存填充和缓存查询时,我们会得到更好的结果,因为HotSpot JVM已经预热。

值得一提的是 Ignite不会缓存查询结果。每次运行查询时,都是从头开始执行。

[00:45:15] Ignite node started OK (id=0960e091, grid=Benchmark)
[00:45:15] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=8.0GB]
Starting - used heap: 225847216 bytes
Inserting 100000 records: ....................................................................................................
Inserted all records - used heap: 1001824120 bytes
Cache: 100000 entries, heap size: 775976904 bytes, inserts took 14819 ms
------------------------------------
Starting - used heap: 1139467848 bytes
Inserting 100000 records: ....................................................................................................
Inserted all records - used heap: 978473664 bytes
Cache: 100000 entries, heap size: **-160994184** bytes, inserts took 11082 ms
------------------------------------
Query 1 count: 100, time: 110 ms, heap size: 1037116472 bytes
Query 2 count: 100, time: 285 ms, heap size: 1037116472 bytes
Query 3 count: 100, time: 19 ms, heap size: 1037116472 bytes
Query 4 count: 100, time: 123 ms, heap size: 1037116472 bytes
------------------------------------
Query 1 count: 100, time: 10 ms, heap size: 1037116472 bytes
Query 2 count: 100, time: 116 ms, heap size: 1056692952 bytes
Query 3 count: 100, time: 6 ms, heap size: 1056692952 bytes
Query 4 count: 100, time: 119 ms, heap size: 1056692952 bytes
------------------------------------
[00:45:52] Ignite node stopped OK [uptime=00:00:36:515]

我将使用更正后的代码创建另一个GitHub仓库,并在我醒来时将其发布在此处(咖啡不再帮助了)。


10
2017-08-11 08:36



谢谢,德米特里。只是我项目的简史。我会私下给你们写一封信。我对Ignite有很大的计划。 - Alex Rogachevsky
谢谢,德米特里。只是我项目的简史。我用Hazelcast启动了Px100(如果你想知道的话,它在之前的版本中使用了标准的SQL / JPA)。我并不天真地认为它只是“在内存中”胜过MySQL,但我预计至少会有类似的表现。像其他人一样,我使用Serializable而不关心索引。我根据Hazelcast的官方推荐对其进行了优化。它改进了一些东西,但问题再次出现在大型数据集上。我会给你们写一封关于Px100的私信。我对Ignite有很大的计划。 - Alex Rogachevsky
@AlexRogachevsky Alex,听起来不错。期待听到您对Apache Ignite的计划。 - Dmitriy
德米特里,我按照你的计算吸气剂的建议,但你也应该确保你打电话给他们一次,然后传递该值。对于任何东西来说,这些吸气剂都很常见,但DTO风格的实体却很愚蠢。我的第二个问题是关于复合指数。我想在文档中的javadoc +详细示例中解释IgniteBiTuple。布尔意味着什么:可能是提升。像这样的东西并不总是“马会明白那种明显的东西”。最后,你有一个严重的性能错误 - 当指定log4j log log / log4j config时。 - Alex Rogachevsky
@AlexRogachevsky以下是Ignite SQL功能的文档,包括复合索引: apacheignite.gridgain.org/v1.3/docs/sql-queries - Dmitriy


这是基准源代码: https://github.com/a-rog/px100data/tree/master/examples/HazelcastVsIgnite

它是我之前提到的JDBC-ish NoSQL框架的一部分: Px100数据

构建和运行它:

cd <project-dir>
mvn clean package
cd target
java -cp "grid-benchmark.jar:lib/*" -Xms512m -Xmx3000m -Xss4m com.px100systems.platform.benchmark.HazelcastTest 100000
java -cp "grid-benchmark.jar:lib/*" -Xms512m -Xmx3000m -Xss4m com.px100systems.platform.benchmark.IgniteTest 100000

如您所见,我将内存限制设置为高以避免垃圾回收。您也可以运行我自己的框架测试(参见Px100DataTest.java)并与上面的两个进行比较,但让我们专注于纯粹的性能。除了Hazelcast 3.5.1和Ignite 1.3.3之外,测试都没有使用Spring或其他任何东西 - 目前最新的。

基准事务在事务上插入指定数量的appr。批量(事务)为1000的1K大小的记录(其中100000个 - 你可以增加它,但要注意内存)然后它执行两个升序和降序的查询:总共四个。所有查询字段和ORDER BY都被编入索引。

我不会发布整个课程(从GitHub下载)。 Hazelcast查询如下所示:

PagingPredicate predicate = new PagingPredicate(
        new Predicates.AndPredicate(new Predicates.LikePredicate("textField", "%Jane%"),
            new Predicates.GreaterLessPredicate("id", first.getId(), false, false)),
        (o1, o2) -> ((TestEntity)o1.getValue()).getId().compareTo(((TestEntity)o2.getValue()).getId()),
        100);

匹配的Ignite查询:

SqlQuery<Object, TestEntity> query = new SqlQuery<>(TestEntity.class,
        "FROM TestEntity WHERE textField LIKE '%Jane%' AND id > '" + first.getId() + "' ORDER BY id LIMIT 100");
    query.setPageSize(100);

以下是我的2012年8核MBP和8G内存执行的结果:

Hazelcast

Starting - used heap: 49791048 bytes
Inserting 100000 records: ....................................................................................................
Inserted all records - used heap: 580885264 bytes
Map: 100000 entries, used heap: 531094216 bytes, inserts took 5458 ms
Query 1 count: 100, time: 344 ms, heap size: 298844824 bytes
Query 2 count: 100, time: 115 ms, heap size: 454902648 bytes
Query 3 count: 100, time: 165 ms, heap size: 657153784 bytes
Query 4 count: 100, time: 106 ms, heap size: 811155544 bytes

点燃

Starting - used heap: 100261632 bytes
Inserting 100000 records: ....................................................................................................
Inserted all records - used heap: 1241999968 bytes
Cache: 100000 entries, heap size: 1141738336 bytes, inserts took 14387 ms
Query 1 count: 100, time: 222 ms, heap size: 917907456 bytes
Query 2 count: 100, time: 128 ms, heap size: 926325264 bytes
Query 3 count: 100, time: 7 ms, heap size: 926325264 bytes
Query 4 count: 100, time: 103 ms, heap size: 934743064 bytes 

一个明显的区别是插入性能 - 在现实生活中很明显。但是很少有人插入1000条记录。通常它是一次插入或更新(保存输入的用户数据等),所以它不会打扰我。但是查询性能确实如此。大多数以数据为中心的商业软件都是大量读取的。

注意内存消耗。 Ignite比Hazelcast更需要RAM。这可以解释更好的查询性能。好吧,如果我决定使用内存网格,我应该担心内存吗?

你可以清楚地告诉数据网格何时命中索引,什么时候不跟踪它们,如何缓存已编译的查询(7ms一个)等等。我不想推测并让你玩它,如Hazelcast和Ignite开发人员提供了一些见解。

到目前为止,作为一般性能,它可以比较,如果不低于MySQL。 IMO内存技术应该做得更好。我相信两家公司都会做笔记。

上面的结果非常接近。但是当在Px100数据中使用时和更高级别的Px100(它严重依赖索引的“排序字段”进行分页)时,Ignite会提前进行并且更适合我的框架。我主要关心查询性能。


4
2017-08-11 04:08



“如果我决定使用内存网格,我应该担心内存吗?”好吧,是的,你应该,除非你想要一个查询来关闭你的集群与OOME。当然,性能确实非常重要,但这并不意味着您不必担心内存使用情况。 - nilskp