Провел небольшое исследование 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 Мб.