如何进行更快的Riak MapReduce查询?

如何进行更快的Riak MapReduce查询?,mapreduce,riak,riak-search,Mapreduce,Riak,Riak Search,如何使MapReduce查询更快? 我们使用五节点Riak DB集群构建了一个应用程序。 我们的数据模型由三个桶组成:比赛、联盟和团队 比赛包含联赛和球队的链接: 型号 var match = { id: matchId, leagueId: meta.leagueId, homeTeamId: meta.homeTeamId, awayTeamId: meta.awayTeamId, startTime: m.ma

如何使MapReduce查询更快?

我们使用五节点Riak DB集群构建了一个应用程序。 我们的数据模型由三个桶组成:比赛、联盟和团队

比赛包含联赛和球队的链接:

型号

var match = {
        id: matchId,
        leagueId: meta.leagueId,
        homeTeamId: meta.homeTeamId,
        awayTeamId: meta.awayTeamId,
        startTime: m.match.startTime,
        firstHalfStartTime: m.match.firstHalfStartTime,
        secondHalfStartTime: m.match.secondHalfStartTime,
        score: {
            goals: {
                a: 1*safeGet(m.match, 'score.goals.a'),
                b: 1*safeGet(m.match, 'score.goals.b')
            },
            corners: {
                a: 1*safeGet(m.match, 'score.corners.a'),
                b: 1*safeGet(m.match, 'score.corners.b')
            }
        }
    };

var options = {
        index: {
            leagueId: match.leagueId,
            teamId: [match.homeTeamId, match.awayTeamId],
            startTime: match.startTime || match.firstHalfStartTime || match.secondHalfStartTime
        },
        links: [
            { bucket: 'leagues', key: match.leagueId, tag: 'league' },
            { bucket: 'teams', key: match.homeTeamId, tag: 'home' },
            { bucket: 'teams', key: match.awayTeamId, tag: 'away' }
        ]
    };
    match.model = 'match';
    modelCache.save('matches', match.id, match, options, callback);
查询

我们编写一个查询,返回来自多个bucket的结果,一种方法是分别查询每个bucket。另一种方法是使用链接组合单个查询的结果

我们尝试的两个查询版本都占用了一秒钟的时间,不管我们的存储桶大小有多小。 第一个版本使用了两个映射阶段,我们按照本文()建模

我们如何使这些查询更快

其他信息:


我们使用RIAKJS和node.js来运行查询。

一种使查询至少更快的方法是将JavaScript mapreduce函数部署到服务器,而不是将它们作为作业的一部分传递给服务器。(参见js_source_dir参数说明)。如果您有重复运行的JavaScript函数,通常建议您这样做

与在Erlang中实现的本机函数相比,运行JavaScript mapreduce函数会带来一些开销,因此在可能的情况下使用非JavaScript函数也会有所帮助

第一个查询中的两个映射阶段函数似乎是为了绕过正常链接阶段(我认为更有效)不能传递正在处理的记录(匹配记录)的限制而设计的。第一个函数包括所有链接,并以JSON形式传递匹配数据作为附加数据,而第二个函数以JSON形式传递匹配数据以及链接记录

我编写了一个简单的Erlang函数,它包括所有链接以及传入的记录的ID。这可以与本机Erlang函数riak_kv_mapreduce:map_object_value一起使用,以替换第一个示例中的两个map phase函数,从而删除一些JavaScript用法。在现有的解决方案中,我希望您能收到大量的副本,因为几场比赛可能会链接到同一个联赛/球队

-module(riak_mapreduce_example).

-export([map_link/3]).

%% @spec map_link(riak_object:riak_object(), term(), term()) ->
%%                   [{{Bucket :: binary(), Key :: binary()}, Props :: term()}]
%% @doc map phase function for adding linked records to result set
map_link({error, notfound}, _, _) ->
    [];
map_link(RiakObject, Props, _) ->
    Bucket = riak_object:bucket(RiakObject),
    Key = riak_object:key(RiakObject),
    Meta = riak_object:get_metadata(RiakObject),
    Current = [{{Bucket, Key}, Props}],
    Links = case dict:find(<<"Links">>, Meta) of
        {ok, List} ->
            [{{B, K}, Props} || {{B, K}, _Tag} <- List];
        error ->
            []
    end,
    lists:append([Current, Links]).
-模块(riak\u mapreduce\u示例)。
-导出([map_link/3])。
%%@spec map_链接(riak_对象:riak_对象(),term(),term())->
%%[{{Bucket::binary(),Key::binary()},Props::term()}]
%%@doc-map阶段函数,用于将链接记录添加到结果集
映射链接({error,notfound},{,})->
[];
地图链接(RiakObject,道具)->
Bucket=riak_对象:Bucket(RiakObject),
Key=riak_对象:Key(RiakObject),
Meta=riak_对象:获取_元数据(RiakObject),
当前=[{Bucket,Key},Props}],
Links=案例dict:查找的(,Meta)
{好的,列表}->
[{B,K},Props}|{{B,K},_Tag}
[]
完,,
列表:追加([当前,链接])。
这些结果可以发送回客户端进行聚合,也可以传递到reduce phase函数中,如您提供的示例所示

示例函数需要编译并安装在所有节点上,并且可能需要重新启动

另一种提高性能的方法(这可能不是您的选择)可能是更改数据模型,以避免将mapreduce查询用于性能关键型查询

async.series([
        //First get all matches
        function(callback) {
            db.mapreduce
                .add(inputs)
                .map(function (val, key, arg) {
                    var data = Riak.mapValuesJson(val)[0];
                    if(arg.leagueId && arg.leagueId != data.leagueId) {
                        return [];
                    }
                    var d = new Date();
                    var date = data.startTime || data.firstHalfStartTime || data.secondHalfStartTime;
                    d.setFullYear(date.substring(0, 4));
                    d.setMonth(date.substring(5, 7) - 1);
                    d.setDate(date.substring(8, 10));
                    d.setHours(date.substring(11, 13));
                    d.setMinutes(date.substring(14, 16));
                    d.setSeconds(date.substring(17, 19));
                    d.setMilliseconds(0);
                    startTimestamp = d.getTime();
                    var short = {
                        id: data.id,
                        l: data.leagueId,
                        h: data.homeTeamId,
                        a: data.awayTeamId,
                        t: startTimestamp,
                        s: data.score,
                        c: startTimestamp
                    };
                    return [short];
                }, {leagueId: query.leagueId, page: query.page}).reduce(function (val, key) {
                    return val;
                }).run(function (err, matches) {
                    matches.forEach(function(match) {
                        result.match[match.id] = match; //Should maybe filter this
                        leagueIds.push(match.l);
                        teamIds.push(match.h);
                        teamIds.push(match.a);
                    });
                    callback();
                });
        },
        //Then get all leagues, teams and lines in parallel
        function(callback) {
            async.parallel([
                //Leagues
                function(callback) {
                    db.getMany('leagues', leagueIds, function(err, leagues) {
                        if (err) { callback(err); return; }
                        leagues.forEach(function(league) {
                            visibleLeagueIds[league.id] = true;
                            result.league[league.id] = {
                                r: league.regionId,
                                n: league.name,
                                s: league.name
                            };
                        });
                        callback();
                    });
                },
                //Teams
                function(callback) {
                    db.getMany('teams', teamIds, function(err, teams) {
                        if (err) { callback(err); return; }
                        teams.forEach(function(team) {
                            result.team[team.id] = {
                                n: team.name,
                                h: team.name,
                                s: team.stats
                            };
                        });
                        callback();
                    });
                }
            ], callback);
        }
    ], function(err) {
        if (err) { callback(err); return; }
        _.each(regionModel.getAll(), function(region) {
           result.region[region.id] = {
               id: region.id,
               c: 'https://d1goqbu19rcwi8.cloudfront.net/icons/silk-flags/' + region.icon + '.png',
               n: region.name
           };
        });
        var response = {
            success: true,
            result: {
                modelRecords: result,
                paging: {
                    page: query.page,
                    pageSize: 50,
                    total: result.match.length
                },
                time: moment().diff(a)/1000.00,
                visibleLeagueIds: visibleLeagueIds
            }
        };
        callback(null, JSON.stringify(response, null, '\t'));
    });
-module(riak_mapreduce_example).

-export([map_link/3]).

%% @spec map_link(riak_object:riak_object(), term(), term()) ->
%%                   [{{Bucket :: binary(), Key :: binary()}, Props :: term()}]
%% @doc map phase function for adding linked records to result set
map_link({error, notfound}, _, _) ->
    [];
map_link(RiakObject, Props, _) ->
    Bucket = riak_object:bucket(RiakObject),
    Key = riak_object:key(RiakObject),
    Meta = riak_object:get_metadata(RiakObject),
    Current = [{{Bucket, Key}, Props}],
    Links = case dict:find(<<"Links">>, Meta) of
        {ok, List} ->
            [{{B, K}, Props} || {{B, K}, _Tag} <- List];
        error ->
            []
    end,
    lists:append([Current, Links]).