Как эффективно урезать базу данных SQlite до заданного размера файла? - PullRequest
6 голосов
/ 13 мая 2011

Я использую SQLite 3.7.2 в Windows. Моя база данных используется для хранения данных журнала, которые генерируются 24/7. Схема в основном:

CREATE TABLE log_message(id INTEGER PRIMARY KEY AUTOINCREMENT, process_id INTEGER, text TEXT);
CREATE TABLE process(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);

Поле log_message.process_id отображается на process.id, таким образом, каждое сообщение журнала связывается с процессом, из которого оно исходит.

Теперь, рано или поздно, база данных становится слишком большой, и я хотел бы удалить самые старые записи (те, которые имеют самые низкие значения log_message.id), пока база данных снова не упадет до заданного размера (скажем, 1 ГБ). Для этого я сейчас делаю

PRAGMA page_count;
PRAGMA page_size;

после каждых нескольких сообщений журнала, чтобы получить размер базы данных. Если он превышает мой лимит, я просто удаляю часть (сейчас: 100 сообщений) сообщений журнала, как это:

BEGIN TRANSACTION;
DELETE FROM log_message WHERE id IN (SELECT id FROM log_message LIMIT 100);
DELETE FROM process WHERE id IN (SELECT id FROM PROCESS EXCEPT SELECT process_id FROM log_message);
COMMIT;
VACUUM;

Последний оператор DELETE удаляет все записи без ссылок из таблицы process. Я повторяю этот процесс, пока размер файла снова не станет приемлемым.

Это связано как минимум с двумя проблемами:

  1. Подход к удалению 100 сообщений журнала является довольно случайным; Я сделал это число на основе нескольких экспериментов. Я хотел бы знать количество записей, которые я должен удалить заранее.
  2. Повторные вызовы VACUUM могут занимать довольно много времени (домашняя страница SQLite сообщает, что VACUUM может занимать до полсекунды на МБ в Linux, я думаю, в Windows это будет происходить не быстрее).

У кого-нибудь есть другие предложения, как это сделать?

Ответы [ 5 ]

2 голосов
/ 13 мая 2011
CREATE TABLE log_messages (
  integer id primary key, -- no autoincrement here
  datetime event_time,    -- for last id retrieval
  char(248) message       -- fixed field size
)

Предположим, что целочисленное поле имеет длину 4 байта, поле даты и времени также имеет длину 4 байта, а каждый символ имеет длину одного байта.Тогда каждая запись имеет длину 256 байт, а ограничение вашего пространства составляет 1 КБ.4 записи.

Инициализация таблицы с последовательными идентификаторами

1 | 2011-05-01 23:00:01 | null
2 | 2011-05-01 23:00:01 | null
3 | 2011-05-01 23:00:01 | null
4 | 2011-05-01 23:00:01 | null

Когда ваша программа запускается, вы запускаете запрос, подобный:

SELECT id FROM log_messages ORDER BY event_time DESC LIMIT 1

Результат этого запроса равен 4, теперь вы добавляете 1, поскольку максимальное количество записей также равно 4, 4 + 1 = 1, то есть идентификатор записи, которую необходимо обновить.

ОБНОВЛЕНИЕ log_message SET message = "новое сообщение", event_time = NOW () WHERE id = 1

Для следующей записи вы просто добавляете 1 к последнему идентификатору, который у вас в памяти.

Надеюсь, вы поняли идею.

2 голосов
/ 13 мая 2011

если у вас есть база данных «правильного размера», подсчитайте количество строк log_message.

SELECT COUNT(*) FROM LOG_MESSAGE

Сохраните это число.

Если вы хотите уменьшить файл, введите команду countснова.Вычислите разницу, удалите это количество строк из вашей базы данных, затем VACCUM.

Это может быть только приблизительным, но довольно быстро увеличит объем до 1 ГБ.Если вы все еще закончите, вы можете вернуться к 100 строкам за раз.

1 голос
/ 18 мая 2011

Разделите заданный максимальный размер файла на размер страницы (как указано PRAGMA page_size), чтобы получить максимальное количество страниц, которое может выделить база данных. Установите это значение, используя PRAGMA max_page_count.

Делая это, операторы INSERT будут выдавать ошибку SQLITE_FULL всякий раз, когда достигается максимальный размер. Всякий раз, когда это происходит, выполните процедуру DELETE, чтобы удалить самые старые записи. После этого вы можете снова ввести INSERT, пока база данных снова не заполнится. И так далее.

Это не урезает базу данных до заданного размера, но в любом случае это неэффективно. Вместо этого лучше установить максимальный размер, который нельзя превышать, а затем сохранить файл базы данных с таким размером, чтобы SQlite мог повторно использовать выделенное дисковое пространство вместо того, чтобы увеличивать или уменьшать файл.

1 голос
/ 13 мая 2011

Если вам требуются права на FS, я думаю, что лучшим способом было бы создать новый журнал базы данных и применить какой-либо поворот к файлам базы данных (удалив самые старые).

0 голосов
/ 14 августа 2015

Опоздание на четыре года и, вероятно, пенни, но вы когда-нибудь задумывались о том, чтобы установить «Id» в диапазоне, который вы сбрасываете до минимума, когда он достигает максимума, а затем вместо вставки и удаления записей выполняете «обновления» в базе данных?.
Я ценю, что вам придется хранить последний использованный номер "Id" в случае закрытия программы, чтобы вы могли начать с правильной точки при повторном запуске, но это кажется относительно тривиальным.
Настроив таким образом, вы будете предварительно определять размер вашей базы данных, если вы используете фиксированный размер записи, по количеству записей в диапазоне «Id».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...