考虑具有以下结构的表:
CREATE TABLE statistics (name text, when timestamp, value int,
PRIMARY KEY ((name, when)));
例如,按名称计算50%值百分位数的最佳方法是什么? 我想到了:
a)编写自定义聚合函数+查询,如:
SELECT PERCENTILE(value, 0.5) FROM statistics WHERE name = '...'
b)首先按名称计数元素
SELECT COUNT(value) FROM statistics WHERE name = '...'
然后按值升序对第(0.5 / count)行值进行分页。说,如果计数为100,它将排在第50行。
c)您的想法
我不确定案例A是否可以处理任务。当行数奇数时,情况B可能会很棘手。
答案 0 :(得分:4)
只要您始终提供name
-如果不指定分区并将所有内容都放在一个分区中,此请求可能会非常昂贵。我假设您的意思是表中的((name), when)
而不是((name, when))
,否则如果没有全表扫描(使用hadoop或spark),您的询问是不可能的。
UDA可以使用-但除非您愿意接受近似值,否则它可能会很昂贵。要使其完全准确,您需要进行2次传递(即做一次计数,而不是第二次传递才能将X放入集合中,但是由于没有隔离,因此也不是完美的)。因此,如果您需要非常准确的方法,那么最好的选择是在计算前仅在本地拉整个statistics[name]
分区,或者让UDA在地图中建立整个集合(或多数)(如果分区变大则不建议使用) 。即:
CREATE OR REPLACE FUNCTION all(state tuple<double, map<int, int>>, val int, percentile double)
CALLED ON NULL INPUT RETURNS tuple<double, map<int, int>> LANGUAGE java AS '
java.util.Map<Integer, Integer> m = state.getMap(1, Integer.class, Integer.class);
m.put(m.size(), val);
state.setMap(1, m);
state.setDouble(0, percentile);
return state;';
CREATE OR REPLACE FUNCTION calcAllPercentile (state tuple<double, map<int, int>>)
CALLED ON NULL INPUT RETURNS int LANGUAGE java AS
'java.util.Map<Integer, Integer> m = state.getMap(1, Integer.class, Integer.class);
int offset = (int) (m.size() * state.getDouble(0));
return m.get(offset);';
CREATE AGGREGATE IF NOT EXISTS percentile (int , double)
SFUNC all STYPE tuple<double, map<int, int>>
FINALFUNC calcAllPercentile
INITCOND (0.0, {});
如果愿意接受一个近似值,则可以使用一个采样池,比如说您存储了1024个元素,并且当UDA获取元素时,您以降低的统计机会替换其中的元素。 (vitter's algorithm R)这很容易实现,并且如果您的数据集预期具有正态分布,则将为您提供一个不错的近似值。如果您的数据集不是正态分布,则可能相差很远。对于正态分布,实际上实际上还有很多其他选择,但是我认为R在UDA中最容易实现。喜欢:
CREATE OR REPLACE FUNCTION reservoir (state tuple<int, double, map<int, int>>, val int, percentile double)
CALLED ON NULL INPUT RETURNS tuple<int, double, map<int, int>> LANGUAGE java AS '
java.util.Map<Integer, Integer> m = state.getMap(2, Integer.class, Integer.class);
int current = state.getInt(0) + 1;
if (current < 1024) {
// fill the reservoir
m.put(current, val);
} else {
// replace elements with gradually decreasing probability
int replace = (int) (java.lang.Math.random() * (current + 1));
if (replace <= 1024) {
m.put(replace, val);
}
}
state.setMap(2, m);
state.setDouble(1, percentile);
state.setInt(0, current);
return state;';
CREATE OR REPLACE FUNCTION calcApproxPercentile (state tuple<int, double, map<int, int>>)
CALLED ON NULL INPUT RETURNS int LANGUAGE java AS
'java.util.Map<Integer, Integer> m = state.getMap(2, Integer.class, Integer.class);
int offset = (int) (java.lang.Math.min(state.getInt(0), 1024) * state.getDouble(1));
if(m.get(offset) != null)
return m.get(offset);
else
return 0;';
CREATE AGGREGATE IF NOT EXISTS percentile_approx (int , double)
SFUNC reservoir STYPE tuple<int, double, map<int, int>>
FINALFUNC calcApproxPercentile
INITCOND (0, 0.0, {});
在上面,百分位数功能会更快变慢,使用采样器的大小可能会或多或少地给您带来准确性,但是太大了,您会开始影响性能。通常,超过10k值的UDA(甚至是简单的功能,例如count
)都会开始失败。同样重要的是要在这些情况下认识到,尽管单个查询返回单个值,但要花费大量的工作。因此,许多此类查询或大量并发性会给您的协调员带来很大压力。对于CASSANDRA-10783
注意:我不保证在示例UDA中没有遗漏过1的错误-我没有进行全面测试,但应该足够接近,您才能从那里开始工作