Минимизация / кэширование SQL-запросов в приложении C++
Я пишу проект на C++/Qt, и он может подключаться к любому типу базы данных SQL, поддерживаемой QtSQL ( http://doc.qt.nokia.com/latest/qtsql.html). Это включает в себя локальные и внешние серверы.
Однако, когда рассматриваемая база данных является внешней, скорость запросов начинает становиться проблемой (медленный пользовательский интерфейс, ...). Причина: каждый объект, который хранится в базе данных, загружается с отложенной загрузкой, и поэтому он будет выдавать запрос каждый раз, когда требуется атрибут. В среднем около 20 из этих объектов должны отображаться на экране, каждый из которых показывает около 5 атрибутов. Это означает, что для каждого экрана, который я показываю, выполняется около 100 запросов. Запросы выполняются довольно быстро на самом сервере базы данных, но накладные расходы на фактический запрос, выполняемый по сети, значительны (измеряются в секундах для всего экрана).
Я думал о нескольких способах решения проблемы, наиболее важными из которых являются (по-моему):
- Делать меньше запросов
- Делайте запросы быстрее
Борьба (1)
- Я мог бы найти какой-то способ отложить фактическую выборку атрибута (запустить транзакцию), а затем, когда программист пишет endTransaction(), база данных пытается извлечь все за один раз (с помощью SQL UNION или цикла...), Это, вероятно, потребует внесения некоторых изменений в способ работы ленивых объектов, но если люди прокомментируют, что это достойное решение, я думаю, что оно может быть разработано элегантно. Если это решение ускоряет все достаточно, то сложная схема кэширования может даже не понадобиться, что избавляет от множества головных болей.
- Я мог бы попытаться предварительно загрузить данные атрибутов, извлекая их все в одном запросе для всех запрашиваемых объектов, фактически делая их ленивыми. Конечно, в этом случае мне придется беспокоиться об устаревших данных. Как бы я обнаружил устаревшие данные, не отправив хотя бы один запрос на внешнюю базу данных? (Примечание: отправка запроса для проверки устаревших данных для каждой проверки атрибута обеспечит увеличение производительности в лучшем случае в 0 раз и снижение производительности в худшем случае в 2 раза, когда данные фактически окажутся устаревшими)
Борьба (2)
Например, запросы можно было бы выполнять быстрее, если бы работала локальная синхронизированная копия базы данных. Однако на клиентских машинах у меня не так много возможностей для запуска, например, точно такого же типа базы данных, как на сервере. Таким образом, локальная копия будет, например, базой данных SQLite. Это также будет означать, что я не смогу использовать решение, специфичное для db-vendor. Какие у меня варианты здесь? Что хорошо сработало для людей в подобных ситуациях?
Беспокойство
Мои основные заботы:
- Устаревшие данные: существует множество возможных запросов, которые изменяют базу данных таким образом, что это запрещает действие, которое может показаться возможным для пользователя с устаревшими данными.
- Ремонтопригодность: насколько свободно я могу соединиться в этом новом слое? Очевидно, было бы предпочтительнее, если бы ему не нужно было знать все о моей внутренней ленивой объектной системе и о каждом объекте и возможном запросе.
Последний вопрос
Что было бы хорошим способом минимизировать стоимость запроса? Хороший смысл, что-то вроде комбинации: поддерживаемый, простой в реализации, не слишком специфичный для приложения. Если дело доходит до выбора любых 2, то пусть будет так. Я хотел бы услышать, как люди рассказывают о своем опыте и о том, что они сделали для его решения.
Как видите, я подумал о некоторых проблемах и способах их решения, но я не знаю, что могло бы быть разумным. Поскольку это, вероятно, потребует много работы и интенсивных изменений во многих слоях программы (надеюсь, как можно меньше), я подумал о том, чтобы спросить всех экспертов здесь, прежде чем принимать окончательное решение по этому вопросу. Также возможно, что я просто упускаю из виду очень простое решение, и в этом случае будет очень полезен указатель на него!
Предполагая, что была выполнена вся соответствующая настройка на стороне сервера (например: кэш MySQL, наилучшие возможные индексы,...)
* Примечание: я проверил вопросы пользователей с похожими проблемами, которые не полностью удовлетворили мой вопрос: предложение по схеме репликации для моего варианта использования? и лучшая практика для локальной базы данных кеша? например)
Если какая-либо дополнительная информация необходима для предоставления ответа, пожалуйста, дайте мне знать, и я должным образом обновлю свой вопрос. Извиняюсь за любые орфографические / грамматические ошибки, английский не мой родной язык.
Заметка о "ленивых"
Небольшой пример того, как выглядит мой код (конечно, упрощенно):
QList<MyObject> myObjects = database->getObjects(20, 40); // fetch and construct object 20 to 40 from the db
// ...some time later
// screen filling time!
foreach (const MyObject& o, myObjects) {
o->getInt("status", 0); // == db request
o->getString("comment", "no comment!"); // == db request
// about 3 more of these
}
1 ответ
На первый взгляд кажется, что у вас две противоречивые цели: скорость запроса, но всегда использование актуальных данных. Таким образом, вы, вероятно, должны вернуться к своим потребностям, чтобы помочь решить здесь.
1) Ваша база данных почти статична по сравнению с использованием приложения. В этом случае используйте ваш вариант 1b
и предварительно загрузить все данные. Если есть небольшая вероятность того, что данные могут измениться под ним, просто дайте пользователю возможность обновить кэш (полностью или для определенного подмножества данных). Таким образом, медленный доступ находится в руках пользователя.
2) База данных меняется довольно часто. В этом случае "возможно" база данных SQL не подходит для ваших нужд. Возможно, вам потребуется динамическая база данных с более высокой производительностью, которая загружает обновления, а не требует извлечения. Таким образом, ваше приложение получит уведомление при изменении базовых данных, и вы сможете быстро ответить. Однако, если это не сработает, вы можете придумать свой запрос, чтобы минимизировать количество вызовов библиотеки БД и ввода / вывода. Например, если вы выполните последовательность select
Заявления ваших результатов должны иметь все соответствующие данные в том порядке, в котором вы их запрашивали. Вы просто должны отслеживать, какие были соответствующие операторы select. В качестве альтернативы, если вы можете использовать более слабые критерии запроса, чтобы он возвращал более одной строки для вашего простого запроса, что также должно повысить производительность.