Cassandra如何处理磁盘IO

3

我希望比较单节点上 PostgreSQLCassandra 的读取性能。

我有一张包含8列,150000行的表。为了将其转换为Cassandra中的列族,我将主键作为行键,并将其余列保留在PostgreSQL中。此外,我将数据批量加载到Cassandra SSTables中,因此两者的数据都存储在磁盘上。

从PostgreSQL中读取表格:

 select * from tableName;

大约需要200毫秒。

为了读取带有键缓存和行缓存的列族,我尝试了thrift API(get_range_slices方法)和CQL 2.0。前者平均需要约7000毫秒,而后者则不堪忍受地需要100000毫秒。

我知道如果从Cassandra内存表中读取,速度可能非常快。但是由于它们都从磁盘中读取,为什么Cassandra会慢得多呢?

哪些基本机制是至关重要的?

编辑:

客户列族

CREATE COLUMN FAMILY customer
WITH comparator = UTF8Type
AND key_validation_class = UTF8Type
AND caching = all
AND column_metadata = 
[
 {column_name: C_NAME, validation_class: UTF8Type},
 {column_name: C_ADDRESS, validation_class: UTF8Type},
 {column_name: C_NATIONKEY, validation_class: UTF8Type},
 {column_name: C_PHONE, validation_class: UTF8Type},
 {column_name: C_ACCTBAL, validation_class: UTF8Type},
 {column_name: C_MKTSEGMENT, validation_class: UTF8Type},
 {column_name: C_COMMENT, validation_class: UTF8Type}
];

这是我的Thrift查询。
   // customer is that column family of 150000 rows
   ColumnParent cf1 = new ColumnParent("customer");
   // all columns
   SlicePredicate predicate = new SlicePredicate();
   predicate.setSlice_range(new SliceRange(ByteBuffer.wrap(new byte[0]), ByteBuffer.wrap(new byte[0]), false, 100));
   // all keys
   KeyRange keyRange = new KeyRange(150000);
   keyRange.setStart_key(new byte[0]);
   keyRange.setEnd_key(new byte[0]);
   List<KeySlice> cf1_rows = client.get_range_slices(cf1, predicate, keyRange, ConsistencyLevel.ONE);   

同时,这是我的CQL2.0查询:
   select * from customer limit 150000;

编辑:

我自己对标题的误导感到抱歉,提供的数据可能会带来更多争议。我在这里不是挑选赢家。

它们都在进行磁盘I/O(这不是Cassandra的典型用例),并且它们在时间上有所不同,因此必须有原因。我很好奇它们处理这个问题的方式。如果你们能为我解释一下背后的机制,我将不胜感激。

这不是苹果对苹果的比较,但我关心的是口感。一个可能更酸,可能因为它含有更多的维生素C。这对我很重要。

谢谢。


你能发布一下你的Thrift查询吗?有时候查询可能会以非常低效的方式编写。 - Nikola Yovchev
@baba 我已经发布了查询。 - manuzhang
Cassandra 通常在变长键的键范围查询上非常缓慢。您能发布列族定义吗?您是否使用 BytesType 比较器?此外,您是否尝试过在结束键中使用 keyRange.setEnd_key(Bytes.fromLong(Long.MAX_VALUE).getBytes()) 替换 new byte[0]? - Nikola Yovchev
@baba 设置 setEnd_key 会返回错误的结果。 - manuzhang
为什么呢?我假设你的键是Long类型,所以如果你使用BytesType比较器,你的keyRange可以从0到long类型的最大值开始。 - Nikola Yovchev
@baba 我已经发布了我的columnfamily定义。它使用UTF8Type比较器。 - manuzhang
3个回答

1

这不是Cassandra的有效测试,因为Postgres和Cassandra并不是为解决同样的问题而设计的。全CF扫描不是真实世界的查询,如果您在生产系统中执行此操作,应使用Hadoop而不是通过Thrift执行。检索大量数据的更现实的Cassandra测试将是列切片,其中您正在检索给定一组键的A到n范围内的列。这是一种更高效的操作,也是Cassandra更合适的数据模型选择。此外,没有人在单个节点上运行Cassandra; 3个节点是最低配置。

如果您想测试全扫描功能,使用Thrift(通过CQL)不是正确的方法,因为所有结果都必须一次性适合内存并在网络上传输(即没有游标)。如果所有数据都可以适合内存,则Cassandra不是您的正确选择。使用Cassandra与Hadoop允许您并行扫描整个数据集,并在几秒钟内回答有关理论上无限数量的数据的问题 - 这是Postgres无法完成的。如果您想详细了解此操作方式,请查看Cassandra的Hadoop包中的RangeClient。值得注意的是,全扫描需要磁盘读取,而许多常见的读取模式利用缓存并且不会触及磁盘。
相比之下,Cassandra在列范围查询或按键获取方面非常快速。这是因为键被哈希到特定节点,然后按列名称排序进行写入。因此,如果您知道您的键和/或想要一系列连续的列(这是非常常见的Cassandra读取模式),则最坏情况下可以获得顺序I/O,最好情况下可以获得缓存数据 - 没有锁定或间接寻址(即索引)。

1
是的,那就是我想知道的。表扫描和CF扫描有何区别。 - manuzhang
但问题是为什么?你想要对两者进行基准测试,可能是因为你有一些使用案例需要解决,并想知道哪个更快/更好。我认为你应该制定一个具体的基准测试,并针对每种解决方案选择适当的数据/查询模型。 - rs_atl
是的,我想在Cassandra之上添加Join操作。据我所知,在NoSQL数据库中没有针对Join操作的基准测试。 - manuzhang
就像我之前所说的,这只是一个试验。我经常听到人们这样说,但我想亲自测试并找出原因。 - manuzhang
我已经设计和实现基于Cassandra的系统超过3年了,我可以肯定地告诉你,你在努力方面是错误的。根据我的经验,大多数认为他们需要连接的人只是选择了错误的数据模型 - 也就是说,他们将关系思维强加到非关系存储上。没有人试图在Memcached、ZooKeeper或ext3上实现连接?为什么不呢?因为这些存储系统不是关系数据库。Cassandra也不是。 - rs_atl
显示剩余3条评论

1
为了补充你的指标,我们在一个六节点集群上进行了性能测试,性能表现非常出色(即更多节点)。我们使用PlayOrm的可扩展SQL查询了所有符合我们条件的活动,并从包含1,000,000行数据的表中返回100行数据,仅用了60毫秒。
通常,人们会分页显示结果,因此查询前100行是更典型的网站用例。其他自动化程序“可能”获取所有行,但通常需要使用map/reduce来处理所有行。如果您在noSQL中查询所有行,则无法进行苹果与苹果之间的比较。
此外,更公平的比较是在六个或十个节点上运行Cassandra,而不是一个节点,因为这样可以加快速度,因为磁盘是并行的,这在Postgres中真的很难做到,或者至少会有分布式事务的问题。这可能更接近实际情况,因为您不会在生产中使用单个节点运行Cassandra。

0

Thrift和CQL-over-Thrift都是基于RPC而不是基于游标的。因此,Cassandra必须将整个结果集拉入内存,然后将其转换为Thrift格式并将其发送回来(仍在内存中)。

因此,我的猜测是,大部分差异来自于您对JVM的分配/GC子系统进行了大量修改。


如果我直接在像“StorageProxy”这样的接口上工作,是否会得到类似于Postgres的体验? - manuzhang

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