Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757
Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737 Форумы портала PHP.SU :: Оптимизация запроса UPDATE в MySQL
Покинул форум
Сообщений всего: 305
Дата рег-ции: Июнь 2009
Помог: 0 раз(а)
Сразу приношу извинения за большой текст. Задавал этот вопрос кратко на другом форуме, но людям не хватило информации, поэтому пришлось выложить более подробно:
Так устроен типичный запрос на поиск\проверку билета:
Таблица summ. В ней хранятся описания призов и денежные суммы призов. Записей в среднем 3-5 тысяч.
Связана с таблицей wins по полям LID, DrawNum, Module, Tour. Связь один(summ) ко многим(wins). Связь логическая, физически (средствами СУБД) не реализована.
`LID` smallint(6) unsigned NOT NULL, // Код лотереи
`DrawNum` smallint(6) unsigned NOT NULL, // Номер тиража
`Module` smallint(6) unsigned NOT NULL, // Код типа розыгрыша
`Tour` smallint(6) unsigned NOT NULL, // Тур
`Money` bigint(20) unsigned NOT NULL, // Сумма
`Descr1` char(64) DEFAULT NULL, // Текстовое описание приза
`Descr2` char(64) DEFAULT NULL, // Текстовое описание приза №2
PRIMARY KEY(`LID`,`DrawNum`,`Module`,`Tour`)
) ENGINE=MyISAM;
Таблица mods. В ней хранятся типы розыгрыша(особенности правил игры в определенном тираже)
Записей в среднем до 1000.
Связана с таблицей wins по полям LID,DrawNum,Module
Так вот. Первый запрос выполняется приемлимо быстро. В среднем 1-50 мили сек. Особенности работы оператора состоят в том что если билет выигрышный(т.е. первый запрос вернулся не пустым), то ему надо зафиксировать оплату билета. Т.е. проапдейтить поля PayDT, WorkST, CurTax в таблице wins. Для этого выполняется следующий запрос:
UPDATE wins SET PayDT=40044.4448122338,WorkST=1,CurTax=0
WHERE LID=1 AND DrawNum=532 AND TckNum=358015
Так вот. ПРоблема в том что этот запрос уже выполняется по времени порядка 1 секунды. Что абсолютно неприемлимо ибо скорость оператора по вводу билетов может достигать 3-4 билетов в секунду. Отсюда следует собственно вопрос, как существенно ускорить запрос на изменение. Уверен что это возможно т.к. базы аналогичной структуры в СУБД Access и MSSQL выполняют такой запрос практически на порядок быстрее.
Теперь какие у меня есть мысли по всему этому.
1) Попробовать BLOB поле заменить на текстовое ибо тип хранимых данных это позволяет сделать.
2) С учетом пункта 1) (или без учета) вынести Blob(текстовое) поле в другую таблицу. Связь будет по LID,DrawNum,TckNum
3) Создать временную таблицу Tmp со структурой аналогичной таблице wins и все фиксации оплаты заносить туда с Помощью INSERT. (Протестил добавление - оно работает приемлемо быстро порядка 500 записей в секунду). И далее(например перед закрытием программы) либо писать хитрый запрос на вроде(проапдейтить в wins те записи которые присутствуют в Tmp). Такой запрос с помощью SQL вроде можно написать. Либо запускать параллельный поток, который будет брать запись из Tmp апдейтить соответствующую в wins и удалять из Tmp.
4) Мне кажется, что есть среди настроек MySQL те, которые отвечают за работу с кешем. Что бы апдейты записывались не напрямую в базу(А судя по времени, у меня возникает ощущение что СУБД пишет напрямую), а через кеш. Очень надеюсь что знающие люди подскажут эти настройки.
Champion
Отправлено: 19 Августа, 2009 - 14:39:55
Активный участник
Покинул форум
Сообщений всего: 4350
Дата рег-ции: Авг. 2008 Откуда: Москва
Помог: 57 раз(а)
Самым быстрым образом апдейт выполнится, когда есть индексы по тому, что участвует в WHERE и нет индексов по всему остальному. Первое гораздо значительнее. Выводы делайте сами. (Добавление)
Phantik пишет:
1) Попробовать BLOB поле заменить на текстовое ибо тип хранимых данных это позволяет сделать
Обязательно. (Добавление)
А вообще, написано очень много.
Stierus
Отправлено: 19 Августа, 2009 - 14:57:13
Рекордсмен по количеству сообщений за 7 дней
Покинул форум
Сообщений всего: 2132
Дата рег-ции: Дек. 2008 Откуда: Москваль
Помог: 52 раз(а)
а экслэйн этого запроса что выдает?
Phantik
Отправлено: 19 Августа, 2009 - 15:30:39
Посетитель
Покинул форум
Сообщений всего: 305
Дата рег-ции: Июнь 2009
Помог: 0 раз(а)
Champion пишет:
Самым быстрым образом апдейт выполнится, когда есть индексы по тому, что участвует в WHERE и нет индексов по всему остальному. Первое гораздо значительнее. Выводы делайте сами. (Добавление)
Phantik пишет:
1) Попробовать BLOB поле заменить на текстовое ибо тип хранимых данных это позволяет сделать
Обязательно. (Добавление)
А вообще, написано очень много.
BLOB на char(n) заменил. Прирост получился несущественный.
Все поля которые в WHERE являются составным Primary Key т.е. я так понимаю индексированы автоматически. Поля которые обновляются - без индексов.
Есть аналогичная структура таблиц\запросов под MSSQL и MSAccess - запрос Update работает на порядок быстрее. Пытаюсь того же добиться на MySQL. (Добавление)
Stierus пишет:
а экслэйн этого запроса что выдает?
id 1
select_type SIMPLE
table wins
type ref
possible_keys PRIMARY
key PRIMARY
key_len 4
ref const,const
rows 103610
Extra Using Where
Я правда не знаю какой из этого следует вывод
Champion
Отправлено: 19 Августа, 2009 - 15:57:33
Активный участник
Покинул форум
Сообщений всего: 4350
Дата рег-ции: Авг. 2008 Откуда: Москва
Помог: 57 раз(а)
Phantik пишет:
Все поля которые в WHERE являются составным Primary Key
А, значит обновляется всего одна запись. А селект с тем же условием быстро выполняется.
И еще. Этот апдейт не вызывает каскадных апдейтов, триггеров не запускает? (Добавление)
А может индекс пересоздать?
Phantik
Отправлено: 19 Августа, 2009 - 17:48:54
Посетитель
Покинул форум
Сообщений всего: 305
Дата рег-ции: Июнь 2009
Помог: 0 раз(а)
Прошу всех прощения за то что из мухи сделал слона. Вообщем разобрался окончательно. Правильный ответ был:
alter table wins add index xxx (LID,DrawNum,TckNum);
И все работает мгновенно.
А до этого меня приглючило что будет достаточно только PRIMARY KEY(`LID`,`DrawNum`,`Module`,`Tour`,`TckNum`)
Champion
Отправлено: 19 Августа, 2009 - 18:03:26
Активный участник
Покинул форум
Сообщений всего: 4350
Дата рег-ции: Авг. 2008 Откуда: Москва
Помог: 57 раз(а)
Ну не знаю, как в MySQL, но обычно, если в запросе сравниваются на = часть полей составного ключа, он используется на ура. (Добавление)
Обычно - это я про MSSQL, Access, Firebird, Interbase, Yaffil
EuGen
Отправлено: 19 Августа, 2009 - 18:06:23
Профессионал
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Вот Вам вывод получается - индекс был неверен. Собственно, на них нужно было смотреть в первую очередь.
Про EXPLAIN рекомендую прочитать, полезная вещь. Так же как и про список процессов.
----- Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
Phantik
Отправлено: 19 Августа, 2009 - 18:39:25
Посетитель
Покинул форум
Сообщений всего: 305
Дата рег-ции: Июнь 2009
Помог: 0 раз(а)
Champion пишет:
Ну не знаю, как в MySQL, но обычно, если в запросе сравниваются на = часть полей составного ключа, он используется на ура. (Добавление)
Обычно - это я про MSSQL, Access, Firebird, Interbase, Yaffil
Я тоже с этим разобрался только сегодня. До этого такие же таблицы с такими же ключами в асексе и МС Скуэле работали мгновенно.
Все гости форума могут просматривать этот раздел. Только зарегистрированные пользователи могут создавать новые темы в этом разделе. Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.