【发布时间】:2017-04-09 00:18:20
【问题描述】:
我需要优化应用程序的一个读取路径。具体来说,此应用程序有时会执行突发读取,通常是 8k 个请求,每个请求 4KB。这种读取模式通常坚持每个分区读取一次,例如,8k 请求是 8k 行,每个分区一行。
这是我的表定义:
CREATE TABLE myobjects (
h blob,
...,
PRIMARY KEY (h)
);
CREATE TABLE mygroups (
id uuid,
sequence int,
h blob,
PRIMARY KEY (id, sequence)
);
基本上,我有一个对象集合,每个对象由h 字段中的哈希标识,以及一组对象列表。每个组由多个对象组成,数据库中的顺序由sequence 字段决定。 这是一种查找表方法,不多也不少。
在你翻脸之前,我知道我应该对等等等等进行非规范化处理,但是这种情况下的数据量非常高,我们估计查找方法可以降低存储成本, 相当。
不幸的是,从应用程序的角度来看,没有办法改变这种读取模式。
这不是一个好的读取模式,因为它直接转换为磁盘上的随机寻道。此外,该应用程序将仅在旋转磁盘上运行,目前我们没有使用 SSD 的计划。
现在,为了优化磁盘搜索,我想利用 SSTable 分区顺序。我的推理很简单:如果每个 SSTable 中的数据都是通过 token 函数排序的,也就是说每个 SSTable 可以包含多个由 TOKEN 函数排序的分区,如果我预先对需要排序的分区键进行排序使用相同的功能请求,我可以对每个 SSTable 进行一些顺序扫描,从而节省宝贵的磁盘寻道。
这是我已经尝试过的:
BoundStatement selectGroupByUUID = ...
ResultSet rs = ...
// Fetch all the rows
List<Row> rows = rs.all();
// Calculate the token for each row and sort by token order
TreeMap<Token, TreeSet<ByteBuffer>> tokenMap = new TreeMap<>();
for (Row row : rows) {
ByteBuffer hash = row.getBytes("h");
Token token = metadata.newToken(hash);
TreeSet<ByteBuffer> set = tokenMap.get(token);
if (set == null) {
set = new TreeSet<>();
tokenMap.put(token, set);
}
set.add(hash);
}
// Request each hash in "token" order
TreeMap<ByteBuffer, Future<ResultSet>> resultSetMap = new TreeMap<>();
for (Map.Entry<Token, TreeSet<ByteBuffer>> tokenMapEntry : tokenMap.entrySet()) {
TreeSet<ByteBuffer> set = tokenMapEntry.getValue();
for (ByteBuffer hash : set) {
Future<ResultSet> future = fetchObjectAsync(hash);
resultSetMap.put(hash, future);
}
}
这段代码的作用是:
- 获取属于组G的对象列表
- 使用 Java DataStax 驱动程序的
metadata.newToken()实用函数计算每个h的令牌 T。 - 将每个集合添加到由
Token排序的 TreeMap - 按令牌顺序迭代和请求,并按哈希顺序在每个令牌内部。
我似乎遗漏了一些东西,因为这不能正常工作,我认为这与直接按顺序请求哈希相比没有任何好处。
我也知道同时发出大量 IO 可以让 IO 调度程序优化磁盘查找,但我发现即使更改队列深度和 co 也没有任何改进......
您还有其他建议吗?
【问题讨论】:
-
与您的问题没有直接关系,但与磁盘有关,这是您使用旋转磁盘时的基本问题。您是否为提交日志使用不同的磁盘?我问的原因是,如果不是,那么您可能会通过将数据和提交日志拆分到单独的磁盘来获得一些收益。
-
@markc 测试机器仅在 RAID0 中的 2 个 HDD 上运行,但我正在执行读取操作,绝对没有写入操作...对于混合工作负载,预计性能会受到影响。然而,部署,应该在 2x2TB HDDs + 2x250gbSSDs 机器集群上。我们计划使用 HDDs 来存储数据,SSDs 来存储 commitlog 和经常查询/小型 CF。
-
读取修复可以创建写入。根据您最初的问题,文件系统磁盘缓存将在这里发挥作用。我认为您在这里所做的事情不会对您的阅读路径产生任何明显的影响。
-
你使用什么压缩策略?如果读取必须达到 20 个 sstables,这一切都没有实际意义。
-
@markc 这只是一个节点,我认为没有发生任何读取修复的事情。但是,我确信这一切都不会引起注意。
标签: java optimization cassandra datastax cql