Я использую SQLite3 в одном из моих проектов, и мне нужно убедиться, что строки, которые вставляются в таблицу, являются уникальными в отношении комбинации некоторых их столбцов.В большинстве случаев вставленные строки будут отличаться в этом отношении, но в случае совпадения новая строка должна обновить / заменить существующую.
Очевидным решением было использование составного первичного ключа с предложением конфликтаобрабатывать столкновения.Выше этого:
CREATE TABLE Event (Id INTEGER, Fld0 TEXT, Fld1 INTEGER, Fld2 TEXT, Fld3 TEXT, Fld4 TEXT, Fld5 TEXT, Fld6 TEXT);
стало так:
CREATE TABLE Event (Id INTEGER, Fld0 TEXT, Fld1 INTEGER, Fld2 TEXT, Fld3 TEXT, Fld4 TEXT, Fld5 TEXT, Fld6 TEXT, PRIMARY KEY (Fld0, Fld2, Fld3) ON CONFLICT REPLACE);
Это действительно обеспечивает ограничение уникальности, как мне это нужно.К сожалению, это изменение также влечет за собой снижение производительности, намного превышающее то, что я ожидал.Я провел несколько тестов, используя утилиту командной строки sqlite3
, чтобы убедиться, что в остальной части моего кода нет ошибок.Тесты включают ввод 100 000 строк, либо в одной транзакции, либо в 100 транзакциях по 1000 строк в каждой.Я получил следующие результаты:
| 1 * 100,000 | 10 * 10,000 | 100 * 1,000 |
|---------------|---------------|---------------|
| Time | CPU | Time | CPU | Time | CPU |
| (sec) | (%) | (sec) | (%) | (sec) | (%) |
--------------------------------|-------|-------|-------|-------|-------|-------|
No primary key | 2.33 | 80 | 3.73 | 50 | 15.1 | 15 |
--------------------------------|-------|-------|-------|-------|-------|-------|
Primary key: Fld3 | 5.19 | 84 | 23.6 | 21 | 226.2 | 3 |
--------------------------------|-------|-------|-------|-------|-------|-------|
Primary key: Fld2, Fld3 | 5.11 | 88 | 24.6 | 22 | 258.8 | 3 |
--------------------------------|-------|-------|-------|-------|-------|-------|
Primary key: Fld0, Fld2, Fld3 | 5.38 | 87 | 23.8 | 23 | 232.3 | 3 |
В настоящее время мое приложение выполняет транзакции максимум в 1000 строк, и я был удивлен 15-кратным падением производительности.Я ожидал не более чем трехкратного снижения пропускной способности и увеличения загрузки ЦП, как это было в случае с транзакцией 100 тыс. Транзакций.Я предполагаю, что для индексации, связанной с поддержанием ограничений первичного ключа, требуется значительно большее количество синхронных операций с БД, поэтому в этом случае мои жесткие диски становятся узким местом.эффект - повышение производительности примерно на 15%.К сожалению, этого недостаточно.PRAGMA synchronous = NORMAL
, похоже, не имеет никакого эффекта.
I может быть в состоянии восстановить некоторую производительность за счет увеличения размера транзакции, но я бы предпочел не делатьэто связано с увеличением использования памяти и опасениями по поводу скорости отклика и надежности.
Текстовые поля в каждой строке имеют переменную длину в среднем около 250 байт.Производительность запросов не имеет большого значения, но производительность вставки очень важна.Код моего приложения написан на C и (должен быть) переносим по крайней мере для Linux и Windows.
Есть ли способ улучшить производительность вставки без увеличения размера транзакции?Либо какая-то настройка в SQLite (что угодно, но не принудительно заставляет БД работать в асинхронном режиме) или программно в коде моего приложения?Например, есть ли способ обеспечить уникальность строки без использования индекса?
BOUNTY:
Используя метод хеширования / индексации, описанный в моем собственном ответе, яудалось несколько смягчить падение производительности до точки, где это, вероятно, приемлемо для моего приложения.Однако, похоже, что с увеличением числа строк в таблице наличие индекса делает вставки все медленнее и медленнее.
Меня интересует любой метод или тонкая настройка, которая повысит производительность в этомособый вариант использования, если он не требует взлома кода SQLite3 и не приводит к тому, что проект становится неприемлемым.