Установка приоритета ввода / вывода sqlite из модуля python (ускорение фиксации sqlite) - PullRequest
2 голосов
/ 09 декабря 2010

У меня есть программа на Python во встроенной системе, которая должна записывать в базу данных sqlite.Эта база данных является критически важной и поэтому должна быть полностью синхронной.Проблема в том, что фиксация базы данных занимает ДЛИННОЕ время (3-30 секунд) для одной вставки.Вставка обернута в транзакцию, но на самом деле нет способа разделить эти вставки на несколько транзакций.

Я искал какой-либо способ заставить фиксацию базы данных занять меньше времени, но явроде потерян.

Я пытался установить pragma journal_mode=persistance, и это, похоже, помогло, но только в небольшом количестве.Теперь я думаю, что это может быть из-за недостатка времени ввода-вывода в sqlite.

Есть ли способ повысить ПРОСТОЙ приоритет процесса sqlite3?Я не хочу повышать приоритет самого Python, потому что мы ведем логи, обновления конфигурации и другие операции ввода-вывода файлов, но я хочу, чтобы sqlite занимал как можно больше времени ввода-вывода.

Я также открыт для других предложений по ускорению фиксации времени.

Это то, что я делаю в своей вставке:

    cur = None
            try:
                logging.info('Inserting into : ' + table + ':' + str(m))
                sql = "INSERT INTO " + table + "("
                bind = " VALUES("
                list = [];
                for k, v in m.items():
                    if(v != None):
                        sql += k + ","
                        bind += "?,"
                        list.append(v)
                sql = str(sql).rstrip(',') + ")"
                bind = str(bind).rstrip(',') + ")"

                cur = conn.cursor()
                cur.execute("PRAGMA journal_mode=PERSIST")
                logging.info(sql + bind)
                cur.execute(sql + bind, list) # It's definitely this that takes the most time. Yes I've profiled.
                conn.commit()
                id = cur.lastrowid
                return id
            except Exception as e:
                raise e
            finally:
                if cur != None:
                    cur.close()

Ответы [ 4 ]

2 голосов
/ 09 декабря 2010

Пробовали ли вы 3.7 WAL?

Исторически сложилось так, что SQLite очень медленно обрабатывал безопасные записи (например, без изменения на synchronous=off).Он записал всю транзакцию в журнал, сбросил ее на диск, а затем скопировал все обратно в исходный файл, и между ними произошло много синхронизаций блокировки, сериализовавших все это.

SQLite 3.7 - записьзаблаговременное ведение журнала (WAL) должно в значительной степени решить эту проблему;это позволяет избежать избыточной записи, которая обходится дорого для больших транзакций, и значительно сокращает количество необходимых синхронизаций FS.

См .: http://www.sqlite.org/wal.html

2 голосов
/ 16 декабря 2010

Вы не говорите, какая у вас встроенная платформа.Если это Linux, то есть причина, по которой это происходит.

Чтобы выполнить фиксацию, SQLite должен подождать, пока связанные данные не будут абсолютно положительно окончательно сохранены на диске.Это часто приходится делать три раза для транзакции - для базы данных, журнала и самого каталога, содержащего оба файла.Даже для WAL требуется одна синхронизация.

Для этого используется системный вызов fsync, который блокирует, пока данные для соответствующего дескриптора файла / каталога не окажутся на диске.Однако общее семейство файловых систем Linux ext3 / 4 превращает это в синхронизирующий вызов.Синхронизируйте блоки, пока все ожидающие данные для всей файловой системы не окажутся на диске.(Такое же поведение реализации может присутствовать в других встроенных операционных системах.)

Вы можете использовать strace или аналогичный инструмент для отслеживания системных вызовов и их времени, что поможет вам определить или устранить это как причину.

Если это (весьма вероятно), то у вас есть два решения.Один из них многократно вызывает синхронизацию в фоновом режиме или настраивает синхронизацию ядра (bdflush / kflushd и т. Д.) С короткими временными интервалами, чтобы объем незафиксированных данных записи был низким.Значения по умолчанию в Linux составляют около 30 секунд, если вы не находитесь в режиме ноутбука, и в этом случае это может быть несколько минут.

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

В Linux системный вызов / инструмент ionice может использоваться для изменения приоритета ввода / вывода.(Для повышения приоритета требуется root.) Однако, если приведенное выше поведение синхронизации является причиной проблемы, которая на самом деле не поможет, поскольку все равно будет тот же объем незафиксированных данных, и изменение порядка записи не будетhelp.

Если ваша базовая файловая система использует какую-то дрянную флэш-память, вы также можете настроить размер страницы SQLite (по умолчанию 1 КБ) в соответствии с размером вашей файловой системы.Это, вероятно, поможет немного.

1 голос
/ 12 июля 2011

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

cursor.execute("PRAGMA cache_size=200000")

Я полагаю, что в большинстве случаев это даст вам размер кэша 200 МБ (но это зависит от размера вашей страницы), поэтому вы можете настроить его, если у вас больше или меньше доступной памяти.

0 голосов
/ 09 декабря 2010

Почему бы вам не попробовать записать вставку в отдельный (постоянный) файл и затем выполнить фиксацию базы данных? Это дает вам возможность восстановления, не беспокоясь о том, чтобы что-то связать с sqlite - вы можете сделать свои INSERT асинхронными, если они предварительно записаны в журнал.

...