2017-02-03 3 views
5

Мой вопрос приходит непосредственно от this один, хотя меня интересует только ОБНОВЛЕНИЕ и только это.Повысить производительность SQLite в UPDATE-секунду?

У меня есть приложение, написанное на C/C++, что делает интенсивное использование SQLite, в основном SELECT/UPDATE, на очень частый интервал (около 20 запросов каждый от 0,5 до 1 секунду)

Моя база данных не является большой, о записей в моменты, здесь структура таблицы:

CREATE TABLE player (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    name VARCHAR(64) UNIQUE, 
    stats VARBINARY, 
    rules VARBINARY 
); 

до этого момента я не использовал transactions, потому что я улучшал код и хотел удар ловкость.

Тогда я измерил производительность базы данных, просто выполняя 10 update запросы, следующие (в цикле различных значений):

// 10 times execution of this 
UPDATE player SET stats = ? WHERE (name = ?) 

где stats является JSON ровно 150 символов и name от 5-10 персонажи.

Без операций, результат неприемлем: - около 1 полной второго (0,096 каждого)

С сделками, время падает раз x7.5: - около 0,11 - 0,16 секунды (0,013 каждого)

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

Затем я попытался играть с PRAGMA вариантов:

PRAGMA synchronous = NORMAL 
PRAGMA journal_mode = MEMORY 

дал мне меньшие времена, но не всегда, больше как около 0,08 - 0,14 секунды

PRAGMA synchronous = OFF 
PRAGMA journal_mode = MEMORY 

Наконец дали мне чрезвычайно малые времена около 0,002 - 0,003 секунды, но я не хочу использовать его, так как мое приложение сохраняет базу данных каждую секунду, и есть высокая вероятность повреждения базы данных o n ОС/сбой питания.

Мой C SQLite код для запросов является: (комментарии/обработка ошибок/несвязанные части опущенные)

// start transaction 
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL); 

// query 
sqlite3_stmt *statement = NULL; 
int out = sqlite3_prepare_v2(query.c_str(), -1, &statement, NULL); 
// bindings 
for(size_t x = 0, sz = bindings.size(); x < sz; x++) { 
    out = sqlite3_bind_text(statement, x+1, bindings[x].text_value.c_str(), bindings[x].text_value.size(), SQLITE_TRANSIENT); 
    ... 
} 

// execute 
out = sqlite3_step(statement); 

if (out != SQLITE_OK) { 
    // should finalize the query no mind the error 
    if (statement != NULL) { 
     sqlite3_finalize(statement); 
    } 
} 

// end the transaction 
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL); 

Как вы видите, это довольно типичный TABLE, записывает число невелико, и я делаю простой простой UPDATE ровно 10 раз. Есть ли что-нибудь еще, что я мог бы сделать, чтобы уменьшить мои UPDATE раз? Я использую последние SQLite 3.16.2.

Примечание: Таймингов выше, поступающие непосредственно из одного END TRANSACTION запроса.Запросы выполняются в простой транзакции, а я - с использованием подготовленного оператора.

UPDATE:

Я выполнил некоторые тесты с транзакцией включенными и выключенными и различными обновлениями сосчитать. Я провел тесты со следующими параметрами:

VACUUM; 
PRAGMA synchronous = NORMAL; -- def: FULL 
PRAGMA journal_mode = WAL; -- def: DELETE 
PRAGMA page_size = 4096;  -- def: 1024 

Результаты не вытекает:

Сделок (10 обновлений)

  • 0,30800 сек (0.0308 на обновление)
  • 0,30200 ИКС
  • 0,36200 secs
  • 0.28600 s ЭКС

Сделок (100 обновлений)

  • 2.64400 сек (0.02644 каждое обновление)
  • 2.61200 ИКС
  • 2.76400 секунд
  • 2.68700 секунд

нет транзакций Дополнения (1000 обновлений)

  • 28.02800 сек (0.028 каждое обновление)
  • 27.73700 сек
  • ..

с транзакциями (10 обновлений)

  • 0,12800 secs (0,0128 каждое обновление)
  • 0,08100 сек
  • 0.16400 ИКС
  • 0,10400 сек

с транзакциями (100 обновлений)

  • 0.088 секунд (0.00088 каждое обновление)
  • 0,091 сек
  • 0.052 Секунд
  • 0.101 СЕК

с транзакциями (1000 обновлений)

  • 0.08900 сек (0.000089 каждое обновление)
  • 0.15000 ИКС
  • 0.11000 секунд
  • 0.09100 секунд

Мои выводы заключаются в том, что с transactions нет смысла в time cost per query. Возможно, времена становятся больше с колоссальным количеством обновлений, но меня не интересуют эти цифры. Там буквально нет разницы во времени между 10 и 1000 обновлений по одной транзакции. Однако мне интересно, является ли это аппаратным ограничением на моей машине и не может многое сделать. Кажется, я не могу пойти ниже ~100 miliseconds, используя единую транзакцию и варьируя 10-1000 обновлений, даже используя WAL.

Без транзакций фиксированная стоимость около 0.025 секунд.

+0

Если вы используете languiage C/C++, используйте правильный тег. В противном случае это похоже на C++, а не на ** другой ** язык C! И это не сайт для проверки кода. – Olaf

+0

@Olaf, единственный материал '' C++ '' 'std :: string'; остальное - 'C'. Я специально подчеркиваю это выше. Во-вторых, я не хочу, чтобы кто-то просматривал мой код, мне нужен лучший подход SQLite для решения моей проблемы. – user6096479

+4

Он делает ** не ** компилируется как C, поэтому это не C. Просто потому, что у вас такой же синтаксис/грамматика не означает, что у вас есть одна и та же семантика! Тот, кто говорит вам, что C++ - это «C с классами», является неправильным и не знает, по крайней мере, одного из них достаточно хорошо, чтобы написать нетривиальный код. – Olaf

ответ

3

С такими небольшими количествами данных, время для самой операции базы данных незначительно; то, что вы измеряете, является служебной задачей транзакции (время, необходимое для принудительной записи на диск), которая зависит от ОС, файловой системы и аппаратного обеспечения.

Если вы можете жить с его ограничениями (в основном, без сети), вы можете использовать асинхронную запись, включив WAL mode.

+0

Я попробовал 'WAL', установив' PRAGMA synchronous = NORMAL' и 'PRAGMA journal_mode = WAL', но я не получил никакого улучшения, я имею в виду вообще. Мне вообще не нужна сеть, и я бы хотел получить пользу от WAL, я просто не получаю никакой выгоды (возможно, нужны некоторые дополнительные опции?!?). С другой стороны, мне нужно «обновить» максимум 20 запросов на максимум 1 секунду. Они (запросы) могут быть меньше, но не могут быть больше. Мне кажется, что использование 'synchronous = NORMAL или FULL' i не может нарушить барьер' 10 мс/запрос на обновление', если только я не решил дать команду ОС, уменьшив безопасность. – user6096479

+0

Чтобы проверить, включен ли WAL, выполните 'PRAGMA journal_mode;'. –

+0

Я не знаю, почему, но мой 'PRAGMA journal_mode = WAL' не был запрошен на мой вызов C, поэтому режим остался' DELETE'. Когда я использовал запрос на 'PHPLiteAdmin', выполнялся нормально. Однако мои времена немного уменьшились примерно до «0.07 - 0.11», что кажется ОК. – user6096479

3

Вы по-прежнему можете ограничить время, необходимое для совершения транзакции. В вашем первом примере каждая транзакция заняла около 0,10 для завершения, что довольно близко к времени транзакции для вставки 10 записей. Какие результаты вы получаете, если вы выставляете 100 или 1000 обновлений в одной транзакции?

Кроме того, SQLite ожидает около 60 транзакций в секунду на среднем жестком диске, в то время как вы получаете около 10. Может ли проблема вашего диска быть проблемой здесь?

https://sqlite.org/faq.html#q19

+0

Я ограничен из-за скорости жесткого диска и синхронного режима ожидания SQLite для проверки записанных данных? Поэтому, если я выбираю безопасность по производительности, я ограничусь ~ ~ 10 мс на запрос обновления с типичным диском 7200? Я не тестировал 100 или 1000 обновлений, потому что мое приложение обрабатывает максимум 15-20 запросов на транзакцию (с некоторыми SELECT между ними), поэтому я как бы эмулировал сценарий по этому вопросу. Я дефрагментировал свой диск неделю назад, я сделаю снова и отвечу :) – user6096479

+0

nope, время по-прежнему варьируется от «0.10 до 0.14' секунд» транзакцией из 10 запросов. – user6096479

+0

см. Мой вопрос update – user6096479

0

Попробуйте добавить УКАЗАТЕЛИ к базе данных:

CREATE INDEX IDXname ON player (name) 

 Смежные вопросы

  • Нет связанных вопросов^_^