Производительность CouchDB

Nov 10, 2009 01:35

Провел небольшое исследование CouchDB на скорость вставки документов. Результаты оказались не очень приятными, хотя и лучше, чем ожидались. По-крайней мере, намного лучше, чем прошлогодние тесты, найденные гуглом.


***************************************************
16 thread

******
Ordinary

Time to insert 100013 items: 109.623253 sec
Average items per second: 912.333809324195

******
Batch

Time to insert 100013 items: 47.989694 sec
Average items per second: 2084.051629918707

***************************************************
4 thread

******
Ordinary

Time to insert 100013 items: 119.520088 sec
Average items per second: 836.7882058453638

******
Batch

Time to insert 100013 items: 55.791765 sec
Average items per second: 1792.6122251196032

***************************************************
1 thread

******
Ordinary

Time to insert 100013 items: 144.625547 sec
Average items per second: 691.5306602090154

******
Batch

Time to insert 100013 items: 74.48103 sec
Average items per second: 1342.7982937400302

Проверял на:
Erlang R13B01 (erts-5.7.2) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
Apache CouchDB 0.10.0
CouchDB прямо из репозитария, собирать самостоятельно пока не пробовал.

-module(couch_bench).

-compile(export_all).

-define(Root, "http://localhost:5984/bench").
-define(Count, 100013).

%%------------------------------------------------------------------------------

do() ->
inets:start(),

Data = data(?Count),
Count = ?Count,

do(Data, Count, 16),
do(Data, Count, 4),
do(Data, Count, 1),
ok.

%%------------------------------------------------------------------------------

do(Data, Count, N) ->
io:format("~n***************************************************~n~w thread~n", [N]),
io:format("~n******~nOrdinary~n~n"),
reset(),
show_result(Count, do_queries(N, Count, Data, "")),

io:format("~n******~nBatch~n~n"),
reset(),
show_result(Count, do_queries(N, Count, Data, "?batch=ok")),

ok.

%%------------------------------------------------------------------------------

reset() ->
do_req(delete, ""),
timer:sleep(5000),
is_ok(do_req(put, "", "", default)).

%%------------------------------------------------------------------------------

show_result(Count, Time) ->
io:format("Time to insert ~p items: ~p sec~n", [Count, Time]),
io:format("Average items per second: ~p~n", [Count / Time]).

%%------------------------------------------------------------------------------

do_queries(N, Count, Data, Query) ->
C = Count div N,
Self = self(),

{NNext, NData} = lists:foldl(
fun(_, {Next, Acc}) ->
{NData, NNext} = lists:split(C, Next),
{NNext, [NData | Acc]}
end,
{Data, []}, lists:seq(2, N)),

{Time, ok} = timer:tc(?MODULE, do_timeit, [[NNext | NData], Query, Self]),
Time / 1000000.

%%------------------------------------------------------------------------------

do_timeit(Data, Query, Self) ->
{_, Pids} = lists:foldl(
fun(TData, {I, Pids}) ->
Pid = spawn_link(?MODULE, do_query, [TData, Query, Self, I]),
{I + 1, [Pid | Pids]}
end,
{1, []},
Data),

lists:foreach(
fun(Pid) ->
receive
{Pid, Ok} ->
ok = Ok
end
end,
Pids),
ok.

%%------------------------------------------------------------------------------

do_query(Data, Query, Parent, I) ->
Profile = list_to_atom("prof" ++ integer_to_list(I)),
inets:start(httpc, [{profile, Profile}]),

Parent ! {self(), send(Data, Query, Profile)}.

%%------------------------------------------------------------------------------

send(Data, Query, Profile) ->
lists:foreach(fun({Id, Json}) ->
is_ok(do_req(put, Id ++ Query, Json, Profile))
end, Data).

%%------------------------------------------------------------------------------

data(Count) ->
[{"/" ++ integer_to_list(I),
"{\"num\":" ++ integer_to_list(random:uniform(100)) ++ "}"
} || I <- lists:seq(1, Count)].

%%------------------------------------------------------------------------------

do_req(Method, Id) ->
do_req_fin(http:request(Method, {?Root ++ Id, []}, [], [])).

%%------------------------------------------------------------------------------

do_req(Method, Id, Body, Profile) ->
do_req_fin(http:request(Method, {?Root ++ Id, [], "application/json", Body}, [], [], Profile)).

%%------------------------------------------------------------------------------

do_req_fin({ok, {_Status, _Headers, RBody}}) ->
{ok, Result} = json:decode_string(RBody),
Result.

%%------------------------------------------------------------------------------

is_ok({struct, Proplist}) ->
case proplists:get_bool(ok, Proplist) of
true -> ok;
_ -> io:format("Error: ~p~n", [Proplist])
end.

%%------------------------------------------------------------------------------

Вывод: 16 потоков и batch режим позволяют довольно быстро (2000 док/сек) заполнить базу. Кроме того, отправка документов в таком режиме позволяет их эффективно записывать на диск, так что делать базе compact не требуется.

Кстати, база в 100 000 документов, заполненная в обычном режиме занимает 0.5 Гб. А после compact 18.6 Мб.
Previous post Next post
Up