Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Web программирование > SQL, базы данных
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 09.06.2015, 10:58   #1
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,426
По умолчанию Оптимизации SQLite

доброго времени суток!

Собственно разрабатываю БД под свой проект.
В XML стало напряжно хранить большой массив данных, да и управлять этим зверем тоже стало сложно, БД справится лучше.

Исходник БД во втором посте.

Таблица Groups и Childrens представляют собой древовидную структуру, хранимую в таблице tree, так же есть view отображающая последнее видимое состояние в UI (Здесь tree,view не описаны, не нужно).
Таблицы table_blobs1(1:1), table_blobs2(1:n) хранят некоторый массив данных, принадлежащих конкретной записи childrens.ID, в blob1 принадлежит 1 запись, в blob2 - бесконечное множество.

Так же я переложил всю работу на БД в части формирования статистики, с помощью триггеров, после INSERT, DELETE для групп, элементов, блобов, в таблице Stats обновляются соотв. поля.

Проблемы:
1. В блобах планируется хранить файлы. На сколько это разумно?
2. Можно ли блобы отделить от отновной БД, затем подключать их и использовать (JOIN к примеру)?
3. Каскадное удаление данных, если удаляется запись из Groups, все childrens.id, group.id (для них так же срабатывает триггер) ссылающиеся на удаляемую запись - так же стираются, при удалении записи из childrens, чистятся таблицы с BLOB'ми.
Я делаю это с помощью триггеров.
Тут же обновляется статистика, и tree, view.

итого такой подход очень дорого стоит в не большой БД на 5-10 тыс. записей, даже если это делать внутри транзакции.
Есть ли иной, более правильный подход?

4. Как правильно делать статистику? Сейчас я это делаю триггерами после вставки, удаления, для каждой записи. Но работает медленно на 20-30 тыс записей, на 50-60 тыс. - страх и ужос.

Как лечить?

Цель: на 100 тыс.записей тормозов быть не должно Рабочая нагрузка будет примерно 5-50 тыс. записей в каждой таблице, для blobs2 - 150 тыс. записей при достижении 50 тыс.записей в childrens.

Последний раз редактировалось Человек_Борща; 09.06.2015 в 11:03.
Человек_Борща вне форума Ответить с цитированием
Старый 09.06.2015, 10:58   #2
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,426
По умолчанию

И так DDL моей БД:
Код:
CREATE TABLE "childrens" (
  "Id" INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, 
  "kind" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0, 
  "GroupID" INTEGER, 
  "Name" TEXT NOT NULL ON CONFLICT FAIL, 
  "Description" TEXT NOT NULL ON CONFLICT REPLACE, 
  "DateCreate" DATETIME DEFAULT (now()), 
  "blob1id" INTEGER, 
  "Deleted" BOOL NOT NULL ON CONFLICT REPLACE DEFAULT False, 
  "DateDeleted" BOOL);

CREATE TRIGGER "childrens_DelStats"
AFTER DELETE
ON "childrens"
FOR EACH ROW
BEGIN
     UPDATE stats SET ChildCount = ChildCount - 1;
END;

CREATE TRIGGER "childrens_addStats"
AFTER INSERT
ON "childrens"
FOR EACH ROW
BEGIN
     UPDATE stats SET ChildCount = ChildCount + 1;
END;

CREATE TRIGGER "childrens_CleanupBlobs2"
AFTER DELETE
ON "childrens"
FOR EACH ROW
BEGIN
  DELETE FROM table_blobs2 WHERE ParentID = OLD.id;
END;

CREATE TRIGGER "childrens_CleanupBlob1"
AFTER DELETE
ON "childrens"
FOR EACH ROW
BEGIN
  DELETE FROM table_blobs1 WHERE ParentID = OLD.id;
END;


CREATE TABLE "groups" (
  "Id" INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, 
  "kind" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 1, 
  "GroupID" INTEGER, 
  "Name" TEXT NOT NULL ON CONFLICT FAIL, 
  "Description" TEXT NOT NULL ON CONFLICT REPLACE, 
  "GroupKind" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0, 
  "DateCreate" DATETIME DEFAULT (now()), 
  "Deleted" BOOL NOT NULL ON CONFLICT REPLACE DEFAULT False, 
  "DateDeleted" BOOL);

CREATE TRIGGER "Groups_UpdateTree"
AFTER DELETE
ON "groups"
FOR EACH ROW
BEGIN
  DELETE FROM tree WHERE (kind = 1) and (ParentID = OLD.id);
END;

CREATE TRIGGER "Groups_CleanupLinks"
AFTER DELETE
ON "groups"
FOR EACH ROW
BEGIN
  DELETE FROM links WHERE GroupID = OLD.id;
END;

CREATE TRIGGER "Groups_CleanupGroups"
AFTER DELETE
ON "groups"
FOR EACH ROW
BEGIN
  DELETE FROM Groups WHERE GroupID = OLD.id;
END;

CREATE TRIGGER "Groups_addStats"
AFTER INSERT
ON "groups"
FOR EACH ROW
BEGIN
     UPDATE stats SET GroupsCount = GroupsCount + 1;
END;

CREATE TRIGGER "Groups_DelStats"
AFTER DELETE
ON "groups"
FOR EACH ROW
BEGIN
     UPDATE stats SET GroupsCount = GroupsCount - 1;
END;


CREATE TABLE "stats" (
  "ChildCount" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0, 
  "GroupsCount" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0, 
  "Blobs1Count" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0, 
  "Blobs2Count" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0);


CREATE TABLE "table_blobs1" (
  "Id" INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, 
  "ParentID" INTEGER, 
  "Path" TEXT NOT NULL ON CONFLICT REPLACE, 
  "CRC32File" TEXT, 
  "data" BLOB, 
  "Size" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0, 
  "CRC32" TEXT);

CREATE TRIGGER "BLOB1_DelStats"
AFTER DELETE
ON "table_blobs1"
FOR EACH ROW
BEGIN
     UPDATE stats SET Blobs1Count = Blobs1Count - 1;
END;

CREATE TRIGGER "BLOB1_addStats"
AFTER INSERT
ON "table_blobs1"
FOR EACH ROW
BEGIN
     UPDATE stats SET Blobs1Count = Blobs1Count + 1;
END;


CREATE TABLE "table_blobs2" (
  "id" INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, 
  "ParentID" INTEGER, 
  "Path" TEXT NOT NULL ON CONFLICT REPLACE, 
  "CRC32File" TEXT, 
  "Data" BLOB, 
  "CRC32" TEXT, 
  "Size" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT (-1), 
  "Width" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT (-1), 
  "Height" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT (-1));

CREATE TRIGGER "BLOB2_addStats"
AFTER INSERT
ON "table_blobs2"
FOR EACH ROW
BEGIN
     UPDATE stats SET Blobs2Count = Blob2Count + 1;
END;

CREATE TRIGGER "BLOB2_DelStats"
AFTER DELETE
ON "table_blobs2"
FOR EACH ROW
BEGIN
     UPDATE stats SET Blobs2Count = Blobs2Count - 1;
END;
Человек_Борща вне форума Ответить с цитированием
Старый 09.06.2015, 11:27   #3
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

1. Файлы в блобах бы не держал
2. stats бы не создавал. Вся статистика запросом элементарно получается
3. Не вижу ни одного индекса. Естественно при удалении будут долго записи искаться. Но и обилие индексов убыстрив поиск замедлит удаление. Но они нужны в разумных пределах
4. Зачем несколько триггеров на удаление, в одном нельзя все сделать? Может SQLite не позволяет, не в курсе. Если позволяет - срочно объединить
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию

Последний раз редактировалось Аватар; 09.06.2015 в 11:31.
Аватар вне форума Ответить с цитированием
Старый 09.06.2015, 14:17   #4
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,426
По умолчанию

Цитата:
1. Файлы в блобах бы не держал
тоже так думаю.

Цитата:
2. stats бы не создавал. Вся статистика запросом элементарно получается
Скорее всего переведу это в разряд view. и сниму триггеры.

Цитата:
3. Не вижу ни одного индекса. Естественно при удалении будут долго записи искаться. Но и обилие индексов убыстрив поиск замедлит удаление. Но они нужны в разумных пределах
Как грамотно создавать индексы и к чему их привязывать?

Цитата:
4. Зачем несколько триггеров на удаление, в одном нельзя все сделать? Может SQLite не позволяет, не в курсе. Если позволяет - срочно объединить
позволяет скорее всего.
Триггер можно сделать для каждой записи и для STATEMENT(как понимать?) так с понимаем самого слова проблема, у него масса значений, в контексте БД что это? 1 запрос?
Человек_Борща вне форума Ответить с цитированием
Старый 09.06.2015, 14:42   #5
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Индексы для полей из WHERE. Составные если несколько полей

Судя по этому https://www.sqlite.org/lang_createtrigger.html есть только триггера FOR EACH ROW. Т.е. триггер срабатывает для каждой обрабатываемой строки таблицы, а не для всей команды редактирования в целом. Вообще-то не в курсе, не работаю с этой СУБД. Для каждой строки значит, что триггер отработает три раза при удалении одной командой трех строк
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию

Последний раз редактировалось Аватар; 09.06.2015 в 14:47.
Аватар вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
SQLite, egresr БД в Delphi 5 04.11.2014 11:51
SQLite PinkPink Qt и кроссплатформенное программирование С/С++ 3 10.04.2012 00:19
SQLite Dr.Badnezz БД в Delphi 2 24.03.2010 09:46
SQLite 3 XeN0N PHP 4 19.01.2009 19:06
SQLite 3 XeN0N PHP 1 18.01.2009 20:30