Здравствуйте, Anton Batenev, Вы писали:
AB> Пример вставки 100К записей за ~15 секунд (обработка ошибок заменена на паники для упрощения).
Чего так медленно?
Попробовал на FPC:
program project1;
uses
SysUtils, SQLite3Wrap;
var
db : TSQLite3Database;
st : TSQLite3Statement;
i : Integer;
begin
db := TSQLite3Database.Create;
try
db.Open('data.db');
db.Execute('CREATE TABLE IF NOT EXISTS test (' +
'key INTEGER NOT NULL PRIMARY KEY,' +
'value TEXT NOT NULL)');
st := db.Prepare('INSERT OR REPLACE INTO "test" ("key", "value") VALUES (?, ?)');
db.BeginTransaction;
try
for i := 0 to 100000 - 1 do
begin//db.Execute(Format('INSERT OR REPLACE INTO "test" ("key", "value") VALUES (%u, %u)', [i, i*2]));
st.BindInt(1, i);
st.BindInt(2, i * i);
st.StepAndReset;
end;
db.Commit;
except
db.Rollback;
raise;
end;
finally
db.Free;
end;
end.
Без стейтмента — около 1 сек. Со стейтментом — 350 мсек.
Поправил немного код (не сразу заметил, что второй параметр текстовый. время почти не изменилось):
program project1;
uses
SysUtils, SQLite3Wrap;
var
db : TSQLite3Database;
st : TSQLite3Statement;
i : Integer;
begin
db := TSQLite3Database.Create;
try
db.Open('data.db');
db.Execute('CREATE TABLE IF NOT EXISTS test (' +
'key INTEGER NOT NULL PRIMARY KEY,' +
'value TEXT NOT NULL)');
st := db.Prepare('INSERT OR REPLACE INTO "test" ("key", "value") VALUES (?, ?)');
try
db.BeginTransaction;
try
for i := 0 to 100000 - 1 do
begin//db.Execute(Format('INSERT OR REPLACE INTO "test" ("key", "value") VALUES (%u, %s)', [i, (i*2).ToString]));
st.BindInt(1, i);
st.BindText(2, (i * i).ToString);
st.StepAndReset;
end;
db.Commit;
except
db.Rollback;
raise;
end;
finally
st.Free;
end;
finally
db.Free;
end;
end.
На каком оборудовании это работает? Полсекунды это очень много, даже для крутящегося жёсткого диска.
Я на Go работал с sqlite, правда без прослоек, таких задержек не было.
Я бы предложил сократить задачу до конкретного примера в несколько десятков строк без лишних библиотек и на него уже смотреть. А так рассуждать абстрактно можно много.
Здравствуйте, alex_public, Вы писали:
_>>>P.S. Да, и надеюсь ты не забыл там, что sqlite строго однопоточная? M>>Разве? Я там мьютексы какие-то видел
_>Я не в том смысле, что нельзя обращаться из разных потоков, а в том что это будет только замедлять работу, т.к. каждый запрос блокирует всю базу. Это как раз отличие sqlite от "взрослых" СУБД, в которых одновременные запросы могут реально исполняться параллельно (если они конечно не на одну строчку указывают).
Здравствуйте, rudzuk, Вы писали:
r> AB> Пример вставки 100К записей за ~15 секунд (обработка ошибок заменена на паники для упрощения). r> Чего так медленно?
Подозреваю, что из за сишных биндингов (go не очень хорошо работает с такими) и из за моего старого ноута. Но т.к. смысл примера был лишь продемонстрировать идею, то я даже не стал копаться.
Здравствуйте, DiPaolo, Вы писали:
DP> Что не так с gorm?
Тут наверное в КСВ сразу можно будет переносить Любой ORM рано или поздно превращается в боль, страдания, костыли и/или абсолютно неоптимальные планы. За исключением случая когда ORM возводится в абсолют и схему базы костылят под возможности и логику ORM, а не наоборот.
DP> Что можешь порекомендовать взамен?
"Нативная" (с минимальной прослойкой) работа с БД (в данном случае через интерфейс database/sql, но это совершенно не обязательно)
Здравствуйте, Anton Batenev, Вы писали:
AB> r> Чего так медленно?
AB> Подозреваю, что из за сишных биндингов (go не очень хорошо работает с такими) и из за моего старого ноута. Но т.к. смысл примера был лишь продемонстрировать идею, то я даже не стал копаться.
У меня десктоп тоже древний, с HDD, на нем твой код вообще 48 сек. работает (диск постоянно трещит). Вряд ли это биндинги...
Здравствуйте, Anton Batenev, Вы писали:
AB> Подозреваю, что из за сишных биндингов (go не очень хорошо работает с такими) и из за моего старого ноута. Но т.к. смысл примера был лишь продемонстрировать идею, то я даже не стал копаться.
Стало интересно. Посмотрел в otop, твой пример пишет на диск около 3 GB Определенно не в биндингах дело...
и sqlite не будет дожидаться физической записи на диск при операциях вставки и обновления.
Работать будет ГОРАЗДО быстрее.
Опасность такая: если операционная система зависнет или питание выключат в неподходящий момент, то БД может повредиться.
При этом если только твое приложение вылетит, то с данными всё будет в порядке в любом случае.
OFF (0)
With synchronous OFF (0), SQLite continues without syncing as soon as it has handed data off to the operating system. If the application running SQLite crashes, the data will be safe, but the database might become corrupted if the operating system crashes or the computer loses power before that data has been written to the disk surface. On the other hand, commits can be orders of magnitude faster with synchronous OFF.
Здравствуйте, Pzz, Вы писали:
Pzz>Если несколько изменений объединены в одну транзакцию, то вся эта долгая возня с обеспечением синхронизации с диском делится на них на всех. Ускорение может быть на несколько порядков (грубо говоря, одно изменение и 10000 изменений, объединенных в одну транзакцию, занимают довольно сравнимое время).
Кстати, чуть-чуть оффтоп, но на ту же тему. Если в PostgreSQL заменить поштучное чтение 1 млн. записей из БД на bulk-чтение порциями сразу по 1 тыс. записей за раз, даже скорость чтения возрастает на пару порядков. Но тут капитан Очевидность уже давно майор или даже подполковник в этом вопросе. В Оракле, помнится, практически по барабану было. Там сервер с клиентом сразу фишечку просекали, что булками читаться будет и результат кешировался на стороне клиентской библиотеки, даже если курсор создавался для поштучного чтения. ЕМНИП. Читало небольшую булку, потом уже на стороне клиента поштучно расковыривало.
Барретт, конечно, весьма неоднозначный человек, в том плане, что любит восторженно абсолютизировать спорные подходы, которые применимы далеко не всегда, не везде и не для всех, однако sqlite он раскочегаривал так, что мое почтение. Хоть и не на go, но его наработки, возможно, будут полезны для ознакомления.