Python, sqlalchemy: как повысить производительность зашифрованной базы данных sqlite? - PullRequest
0 голосов
/ 24 октября 2018

У меня есть простое сервисное приложение: python, веб-сервер tornado, база данных sqlite.База данных зашифрована.

Проблема в том, что обработка даже очень простого http-запроса занимает около 300 мсек.

Из журналов видно, что большую часть этого времени занимает обработка самого первого запроса sql,как бы ни был прост этот первый запрос.Последующие запросы SQL обрабатываются гораздо быстрее.Но затем сервер начинает обработку следующего http-запроса, и снова первый SQL-запрос очень медленный.

Если я отключу шифрование базы данных, проблема исчезнет: время обработки SQL-запросов не зависит от того, является ли запроссначала или нет, и время отклика моего сервера уменьшается в 10-15 раз.

Я не совсем понимаю, что происходит.Похоже, sqlalchemy читает и расшифровывает файл базы данных каждый раз, когда начинается новый сеанс.Есть ли способ обойти эту проблему?

Ответы [ 2 ]

0 голосов
/ 06 ноября 2018

Для шифрования базы данных sqlcipher создает ключ из предоставленной мной парольной фразы.Эта операция требует значительных ресурсов.

Но возможно использовать не ключевую фразу, а 256-битный необработанный ключ.В этом случае sqlcipher не должен был бы генерировать ключ шифрования.

Первоначально мой код был:

session.execute('PRAGMA KEY = "MY_PASSPHRASE";')

Чтобы использовать необработанный ключ, я изменил эту строку на:

session.execute('''PRAGMA KEY = "x'<the key>'";''')

, где <the key> - это строка шестнадцатеричных символов длиной 64 символа.

Результат в 20 и более раз ускоряется при небольших запросах.

Только для справки: для преобразования базы данных в новый ключ шифрования используетсядолжны быть выполнены следующие команды:

PRAGMA KEY = ""MY_PASSPHRASE";
PRAGMA REKEY = "x'<the key>'";

Смежный вопрос: python, sqlite, sqlcipher: очень низкая производительность при обработке первого запроса

Некоторая информация о командах sqlcipher и разнице междуключи и необработанные ключи: https://www.zetetic.net/sqlcipher/sqlcipher-api/

0 голосов
/ 25 октября 2018

Из-за того, как pysqlite или модуль sqlite3 работает, SQLAlchemy по умолчанию использует NullPool с файловыми базами данных .Это объясняет, почему ваша база данных расшифровывается для каждого запроса: NullPool отбрасывает соединения, когда они закрыты.Причина, по которой это делается, заключается в том, что стандартное поведение pysqlite запрещает использование соединения в более чем одном потоке, и без шифрования создание новых соединений происходит очень быстро.

Pysqlite имеет недокументированный флаг check_same_thread, который можетможно использовать для отключения проверки, но совместное использование соединений между потоками следует обрабатывать с осторожностью, а в документации SQLAlchemy упоминается, что NullPool хорошо работает с блокировкой файлов SQLite.

В зависимости от вашего веб-сервера вы можетеиспользуйте SingletonThreadPool, что означает, что все соединения в потоке - это одно и то же соединение:

engine = create_engine('sqlite:///my.db',
                       poolclass=SingletonThreadPool)

Если вы чувствуете себя авантюрным и ваш веб-сервер не разделяет соединения / сеансы между потоками во время использования (например,используя сессию с определенными областями), то вы можете попробовать использовать другую стратегию объединения в пару с check_same_thread=False:

engine = create_engine('sqlite:///my.db',
                       poolclass=QueuePool,
                       connect_args={'check_same_thread':False})
...