Hazelcast与Ignite基准测试比较

17

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

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

2个回答

16

我已经在GitHub中审查了提供的代码,并有许多评论:

索引和连接

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

鉴于上述事实,Ignite索引创建会花费更长时间,特别是在您的测试中,您有7个这样的索引。

TestEntity类的修复

在您的代码中,您存储在缓存中的实体每次调用getter时都会重新计算idSortcreatedAtSortmodifiedAtSort的值。当实体存储在索引树中时,Ignite会多次调用这些getter。通过对TestEntity类进行简单修复,可以提高4倍的性能:https://gist.github.com/dsetrakyan/6bfe089d53f888448503

堆测量不准确

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

热身

每个基准测试都需要热身。例如,当我应用上面建议的修复时,并且对缓存进行两次填充和查询时,我获得更好的结果。

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 存储库,并在此处发布更正后的代码,当我更清醒时(喝咖啡已经没有帮助了)。


@AlexRogachevsky 这里是Ignite SQL功能的文档,包括复合索引:http://apacheignite.gridgain.org/v1.3/docs/sql-queries - Dmitriy
@AlexRogachevsky 关于 Getter,我不确定是否同意,但我们会考虑。你所做的是每次调用 Getter 时都重新计算相同的值,这并不常见。 - Dmitriy
@AlexRogachevsky 感谢您注意到null-appender bug的log4j问题(出于某些原因,它会打开debug模式)-已在主线程中修复,并将成为下一个版本的一部分。 - Dmitriy
Dmitry,我通过编程配置索引。顺便说一句,它似乎不起作用。我不能在通用框架/API中使用特定于供应商的注释。请修订CacheTypeMetadata类的javadoc,特别是setGroups()方法。它目前的描述是“设置分组索引字段。”而复杂的参数:一个IgniteBiTuple的映射的映射告诉我很少。您也可以简化该签名。您可能没有考虑到有人会使用它而不是注释。至于getter,我重构了您看到的那些,但我仍然有很多计算从更改的字段中获取值的getter。 - Alex Rogachevsky
文档有解释吗?这也不会影响性能。CacheTypeMetadata type = new CacheTypeMetadata(); LinkedHashMap<String, IgniteBiTuple<Class<?>,Boolean>> compoundIndex = new LinkedHashMap<>(); compoundIndex.put("textField", new IgniteBiTuple<>(String.class, true)); compoundIndex.put("id", new IgniteBiTuple<>(Long.class, false)); type.getGroups().put("compIdx1", compoundIndex); - Alex Rogachevsky
显示剩余5条评论

8
这里是基准源代码:https://github.com/a-rog/px100data/tree/master/examples/HazelcastVsIgnite。它是我之前提到的JDBC式NoSQL框架Px100 Data的一部分:Px100 Data
构建和运行方式如下:
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 - 目前最新的版本。
基准测试会在事务中插入指定数量的约为1K大小的记录(一共10万条 - 您可以增加,但要注意内存),每次批量插入1000条。然后执行两个查询,一个升序,一个降序:总共四个查询。所有查询字段和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);

以下是在我拥有8核心和8G内存的2012年MBP上执行的结果:

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

Ignite

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相当,甚至略低。在我看来,内存技术应该做得更好。我相信两家公司都会做笔记。
上面的结果非常接近。然而,在Px100 Data和更高级别的Px100(它严重依赖于带排序字段的索引进行分页)中使用时,Ignite领先,更适合我的框架。我主要关心查询性能。

3
如果我决定使用内存网格,那么我应该担心内存问题吗?是的,除非你想让一个查询通过 OOME 来使整个集群崩溃。当然,性能确实非常重要,但这并不意味着你不应该关注内存使用。 - nilskp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接