Вопрос к знатокам PHP/MySQL |
Здравствуйте, гость ( Вход | Регистрация )
Вопрос к знатокам PHP/MySQL |
jtj |
07.08.2006, 10:50
Сообщение
#1
|
Группа: Старые пользователи Сообщений: 22 Регистрация: 20.01.2006 Пользователь №: 2,054 Репутация: 211 |
Всем привет.
У меня такая задачка (IMG:style_emoticons/default/smile.gif) Есть MySQL-таблица, в которой 1000 записей (строк), а возможно и более. В каждой строке, есть несколько полей и одно из этих полей мне нужно изменить в каждой из 1000 строк. Новое значение каждого поля уникально и зависит от внешних факторов. Изменения делаются из php-скрипта. Вопрос - как из php-скрипты изменить все 1000 строк, таким образом что бы не превысить каких-либо лимитов (например, на количество запросов к БД) и не положить сервер перегрузкой? Пример выполнения такой задачи - когда в биллинге снимается абонентская плата (изменяется балланс большого кол-ва клиентов, причем у каждого клиента свой балланс и изменяется в соответствиии с тарифом который использует конкретно данный клиент и дополнительными услугами типа выделенных IP). Так понимаю, не делать же 1000 раз update? Посоветуйте, пожалуйста, как быть, а то всю ночь бьюсь... Заранее огромное спасибо всем откликнувшимся. |
edogs |
07.08.2006, 16:49
Сообщение
#2
|
php(zce)/mysql Группа: Старые пользователи Сообщений: 3,576 Регистрация: 15.08.2003 Из: Санкт-Петербург Пользователь №: 249 Репутация: 246 |
Раз каждое изменение индивидуально, то значит Вам всё равно придется делать 1 апдейт на 1 строку.
Значит задача сводится к тому, что бы сделать это в несколько приемов. Лично мы делали бы примерно так, если надо за раз не запускать 1000 запросов. Что бы не апдейтить одно и то же по нескольку раз 1) В каждое поле добавить дату последнего изменения. Её апдейтить при изменении поля. Скрипт выбирает допустим 10 полей которые не обновлялись последними. Апдейтит их нужным образом. Или 2) Завести еще одну таблицу (или файл) где хранить уникальный ID последней апдейтеной строки. И выбирать 10 следующих после нее. Записывать ID конечно после апдейта. Запускать скрипт можно а) По крону (например простым GET/wget). б) Выводить после завершения простую хтмл страницу с meta refresh=Х секунд на самого себя. Неплохой идеей может являться лок таблицы на время апдейта, если апдейт конечно не 30 минут занимает. Будет работать быстрее и безопаснее. А вообще, Вы уверены что не хотите переписать эту задачу на файлы? Файл считали, что надо проапдейтили, и назад записали. Хотя работать с результатом конечно труднее будет, особенно на сложных операциях. но Вы же не уточнили что делаете с данными кроме апдейта(IMG:style_emoticons/default/smile.gif) Всем привет. У меня такая задачка (IMG:style_emoticons/default/smile.gif) Есть MySQL-таблица, в которой 1000 записей (строк), а возможно и более. В каждой строке, есть несколько полей и одно из этих полей мне нужно изменить в каждой из 1000 строк. Новое значение каждого поля уникально и зависит от внешних факторов. Изменения делаются из php-скрипта. Вопрос - как из php-скрипты изменить все 1000 строк, таким образом что бы не превысить каких-либо лимитов (например, на количество запросов к БД) и не положить сервер перегрузкой? Пример выполнения такой задачи - когда в биллинге снимается абонентская плата (изменяется балланс большого кол-ва клиентов, причем у каждого клиента свой балланс и изменяется в соответствиии с тарифом который использует конкретно данный клиент и дополнительными услугами типа выделенных IP). Так понимаю, не делать же 1000 раз update? Посоветуйте, пожалуйста, как быть, а то всю ночь бьюсь... Заранее огромное спасибо всем откликнувшимся. |
eSupport.org.ua |
07.08.2006, 17:42
Сообщение
#3
|
Одесский сисадмин Группа: Старые пользователи Сообщений: 5,200 Регистрация: 18.11.2004 Из: Одесса Пользователь №: 823 Репутация: 263 |
В биллингах идет так:
На каждый учетный период в отведенной под это дело таблице накапливается история операций. Затем когда происходит переход - данные из таблицы консолидируются select'ом и переходят в итоговую таблицу. А то что написано выше - это не биллинг а непонятно что... |
edogs |
07.08.2006, 18:05
Сообщение
#4
|
php(zce)/mysql Группа: Старые пользователи Сообщений: 3,576 Регистрация: 15.08.2003 Из: Санкт-Петербург Пользователь №: 249 Репутация: 246 |
В биллингах идет так: На каждый учетный период в отведенной под это дело таблице накапливается история операций. Затем когда происходит переход - данные из таблицы консолидируются select'ом и переходят в итоговую таблицу. А то что написано выше - это не биллинг а непонятно что... Да кстати, прочитали сначала что надо сделать, а вот для чего - не обратили внимания. Вот это в частности Цитата Пример выполнения такой задачи - когда в биллинге снимается абонентская плата (изменяется балланс большого кол-ва клиентов, причем у каждого клиента свой балланс и изменяется в соответствиии с тарифом который использует конкретно данный клиент и дополнительными услугами типа выделенных IP). Можно сделать проще, посмотрите математические функции для операций с БД. Можно апдейтнуть всё за 1 запрос, вычисляя сумму вычета на базе данных о тарифе и используемых услугах. P.S.: А историю операций вести надо по любому, esupport полностью прав. |
kuvalda |
07.08.2006, 18:46
Сообщение
#5
|
Группа: Старые пользователи Сообщений: 22 Регистрация: 14.07.2006 Пользователь №: 3,192 Репутация: 208 |
Всем привет. У меня такая задачка (IMG:style_emoticons/default/smile.gif) Есть MySQL-таблица, в которой 1000 записей (строк), а возможно и более. В каждой строке, есть несколько полей и одно из этих полей мне нужно изменить в каждой из 1000 строк. Код MYSQL_QUERY("Update BaseName set StolbetsKotoriiMeniaem=NovoeZnachenie"); Сообщение отредактировал kuvalda - 07.08.2006, 18:48 |
jtj |
07.08.2006, 19:50
Сообщение
#6
|
Группа: Старые пользователи Сообщений: 22 Регистрация: 20.01.2006 Пользователь №: 2,054 Репутация: 211 |
Большое спасибо ответившим (IMG:style_emoticons/default/smile.gif)
Раз каждое изменение индивидуально, то значит Вам всё равно придется делать 1 апдейт на 1 строку. Вот это я и хотел узнать, что не существует варианта с единственным запросом, если каждое изменнение уникально и зависит от внешних факторов (IMG:style_emoticons/default/unsure.gif) Значит задача сводится к тому, что бы сделать это в несколько приемов. Лично мы делали бы примерно так, если надо за раз не запускать 1000 запросов. Что бы не апдейтить одно и то же по нескольку раз 1) В каждое поле добавить дату последнего изменения. Её апдейтить при изменении поля. Скрипт выбирает допустим 10 полей которые не обновлялись последними. Апдейтит их нужным образом. Или 2) Завести еще одну таблицу (или файл) где хранить уникальный ID последней апдейтеной строки. И выбирать 10 следующих после нее. Записывать ID конечно после апдейта. Запускать скрипт можно а) По крону (например простым GET/wget). б) Выводить после завершения простую хтмл страницу с meta refresh=Х секунд на самого себя. Неплохой идеей может являться лок таблицы на время апдейта, если апдейт конечно не 30 минут занимает. Будет работать быстрее и безопаснее. Спасибо за советы, второй обязательно применю, если не перепишу базу таким образом, что бы не было внешних факторов. А вообще, Вы уверены что не хотите переписать эту задачу на файлы? Файл считали, что надо проапдейтили, и назад записали. Хотя работать с результатом конечно труднее будет, особенно на сложных операциях. но Вы же не уточнили что делаете с данными кроме апдейта(IMG:style_emoticons/default/smile.gif) Очень хотелось вынести это поле из базы в тестовый файл (IMG:style_emoticons/default/smile.gif) Но помню очень бурный спор (не с Вами, но здесь, на форуме), где обсуждалось быстродействие MySQL против txt при одновременной работе с большим кол-вом данных... В биллингах идет так: На каждый учетный период в отведенной под это дело таблице накапливается история операций. Затем когда происходит переход - данные из таблицы консолидируются select'ом и переходят в итоговую таблицу. Сложно к пониманию, но информация к размышлению, спасибо (IMG:style_emoticons/default/smile.gif) А то что написано выше - это не биллинг а непонятно что... То что написано выше - абстрактное рассуждение по поводу абстрактной проблемы (IMG:style_emoticons/default/smile.gif) Можно сделать проще, посмотрите математические функции для операций с БД. Можно апдейтнуть всё за 1 запрос, вычисляя сумму вычета на базе данных о тарифе и используемых услугах. P.S.: А историю операций вести надо по любому, esupport полностью прав. Это было бы идеально, если бы не: Таблица clients содержит поле ballans а так же поле package_ID являющееся внешним ключем, а так же поле personal_discount (где в процентах указана персональная скидка). Таблица packages содержит поле cost (стоимость, например суточная) (естесственно обе эти таблицы содержат не только эти поля) Пока еще можно проапдейтить одним запросом, типа UPDATE clients SET clients.ballans = clients.ballans-packages.cost WHERE clients.package_ID = packages.id Ну образно конечно написал, но что-то в этом образе посторить можно (IMG:style_emoticons/default/smile.gif) Мешает то, что есть еще дополнительные факторы, влияющие на сумму снимаемую с ballans каждой конкретной строки - это как personal_discount (персональная скидка), так и addons (например "дополнительные услуги") определяющие итоговую сумму снимаемую с ballans В общем, пока писал-писал здесь пост, половину стрер, потому что вдруг понял, что модифицировать все записи одним запросом не нужно (не безопасно) - пришло понимание что и как сделать (IMG:style_emoticons/default/smile.gif) edogs, спасибо за подсказку Цитата 2) Завести еще одну таблицу (или файл) где хранить уникальный ID последней апдейтеной строки. И выбирать 10 следующих после нее. Записывать ID конечно после апдейта. Это очень дельная мысль(IMG:style_emoticons/default/smile.gif) Всем спасибо (IMG:style_emoticons/default/smile.gif) Сообщение отредактировал jtj - 07.08.2006, 19:57 |
edogs |
07.08.2006, 20:58
Сообщение
#7
|
php(zce)/mysql Группа: Старые пользователи Сообщений: 3,576 Регистрация: 15.08.2003 Из: Санкт-Петербург Пользователь №: 249 Репутация: 246 |
Очень хотелось вынести это поле из базы в тестовый файл (IMG:style_emoticons/default/smile.gif) Холиварная тема, но смысл в целом сводится к тому, что если идут операции выборки, фильровки, сортировки, то лучше не мучаться и сделать на базе. Если же идут операции - взял всю информацию - обработал - положил обратно, то файл в целом рулит. Таблица в БД это на самом деле, такой же физический файл со всеми вытекающими.Но помню очень бурный спор (не с Вами, но здесь, на форуме), где обсуждалось быстродействие MySQL против txt при одновременной работе с большим кол-вом данных... Мешает то, что есть еще дополнительные факторы, влияющие на сумму снимаемую с ballans каждой конкретной строки - это как personal_discount (персональная скидка), так и addons (например "дополнительные услуги") определяющие итоговую сумму снимаемую с ballans |
jtj |
08.08.2006, 08:23
Сообщение
#8
|
Группа: Старые пользователи Сообщений: 22 Регистрация: 20.01.2006 Пользователь №: 2,054 Репутация: 211 |
Холиварная тема, но смысл в целом сводится к тому, что если идут операции выборки, фильровки, сортировки, то лучше не мучаться и сделать на базе. Если же идут операции - взял всю информацию - обработал - положил обратно, то файл в целом рулит. Таблица в БД это на самом деле, такой же физический файл со всеми вытекающими. Хехе, года не прошло с того момента, как я спорил на форуме, доказывая что файловую БД местами использовать не менее удобно, чем MySQL и быстродействие не ниже. Причем тогда почти дословно повторил фразу Цитата Таблица в БД это на самом деле, такой же физический файл (IMG:style_emoticons/default/smile.gif) Меня тогда запинали, доаказывая что не стоит изобретать велосипед (IMG:style_emoticons/default/smile.gif) Но в целом велосипед действительно изобретать не стоит, лучше совершенствовать изобретенное. У меня вопрос к edogs как к разработчикам и к eSupport.org.ua (и другим хостерам) как к тем, кто понимает что такое лимиты, потоки и т.п. Исходя из задачи (обновить 1000 записей), что вы думаете о таком приеме: Код $s=0; for($i=0; $i<1000; $i++){//просто цикл который выполнится 1000 раз //mysql_query("UPDATE таблица SET и так далее");//обновляем одно поле таблицы за один проход цикла if($s==10){//на десятом проходе цикла set_time_limit(3);//добавляем скрипту три секунды на выполнение sleep(1);//и засыпаем на секунду $s=0; } $s++; } В этом коде происходит буквально следующее - скрипт делает 10 запросов к БД и "засыпает" на 1 секунду. При этом процесс скрипта продолжает существовать и через секунду скрипт делает еще 10 запросов и снова засыпает. И так пока не выполнит все запросы. На 1000 запросов потребуется меньше двух минут (IMG:style_emoticons/default/smile.gif) Получается не более 10 зпросов к БД в секунду. Сразу скажу, что лично мне в этом способе не нравится потеря совместимости с php safe_mode (функция set_time_limit), но... Хочется поинтересоваться у eSupport.org.ua и вообще у хостеров - какова вероятность, что такой процесс будет остановлен из-за каких-либо лимитов (например на кол-во запросов к БД от одного процесса), ну и повторяюсь, хочется поинтересоваться у edogs как у разработчиков - что Вы думаете об этом способе, имеет ли он смысл? (IMG:style_emoticons/default/smile.gif) |
edogs |
08.08.2006, 10:39
Сообщение
#9
|
php(zce)/mysql Группа: Старые пользователи Сообщений: 3,576 Регистрация: 15.08.2003 Из: Санкт-Петербург Пользователь №: 249 Репутация: 246 |
1) set_time_limit устанавливает общее время скрипта на выполнение, не надо его в цикл. Пишите 1 раз в начале скрипта - столько времени - сколько надо. 0 - если хотите снять лимит.
2) sleep в таком смысле это имхо ужасно. Кстати, что интересно, проспавшая 1 секунда не влияет на п.1. Но слип оставляет висящим процесс (утрируем), и зачем это делать? 3) Вместо $s можно использовать $i%10 4) Если скрипт остановится неожиданно (ну сервер рестартовал), все заново? У Вас нет отметки о текущем статусе. 5) Способ такой нам очень не нравится по куче причин. 1000 запросов из скрипта. Слип. Неизвестное количество времени работы. 6) С нашей точки зрения А и Б варианты для повтора (крон или мета рефреш) являются намного более оптимальными. Получается просто аккуратный запуск скрипта. В варианте Б, для того, что бы не считывать каждый раз отметку последней записи, можно кидать рефреш с параметром (хотя запоминать все равно надо). P.S.: Сорри за критику(IMG:style_emoticons/default/smile.gif) |
jtj |
08.08.2006, 13:25
Сообщение
#10
|
Группа: Старые пользователи Сообщений: 22 Регистрация: 20.01.2006 Пользователь №: 2,054 Репутация: 211 |
Возможно стоило сразу уточнить несколько моментов.
1. Скрипт изначально запускается по крону (поэтому в принципе, скрипт может висеть хоть час, если сам по себе процесс не будет создавать нагрузки (в чем сильно сомневаюсь, ведь это всего-лишь навсего единственный процесс использующий несколько килобайт памяти) 2. Скрипт (запускаемый ежесуточно) обновляет те записи, где "дата последнего обновления" ниже текущей (по формуле: текущее значение - (вычитаемая сумма * (текущая дата - дата последнего обновления = дней)) т.е. если вдруг скрипт остановится, то в следующий раз он вычтет корректное значение). 3. Цитата 1) set_time_limit устанавливает общее время скрипта на выполнение, не надо его в цикл. Пишите 1 раз в начале скрипта - столько времени - сколько надо. 0 - если хотите снять лимит. set_time_limit в цикле, потому что Цитата 5) Способ такой нам очень не нравится по куче причин. 1000 запросов из скрипта. Слип. Неизвестное количество времени работы. 4. Цитата 2) sleep в таком смысле это имхо ужасно. Кстати, что интересно, проспавшая 1 секунда не влияет на п.1. Но слип оставляет висящим процесс (утрируем), и зачем это делать? Что бы было не более десяти запросов к БД в секунду (IMG:style_emoticons/default/smile.gif) Цитата P.S.: Сорри за критику Спасибо за критику (IMG:style_emoticons/default/smile.gif) P.S. Внесенные мной дополнения никак не влияют на Ваше мнение относительно такого способа? (IMG:style_emoticons/default/smile.gif) P.P.S. Хотя все равно я уже почти решил использовать для этого поля файл, потому что ту же совместимость с safe_mode терять не хочется (IMG:style_emoticons/default/smile.gif) |
edogs |
08.08.2006, 18:25
Сообщение
#11
|
php(zce)/mysql Группа: Старые пользователи Сообщений: 3,576 Регистрация: 15.08.2003 Из: Санкт-Петербург Пользователь №: 249 Репутация: 246 |
1. Скрипт изначально запускается по крону (поэтому в принципе, скрипт может висеть хоть час, если сам по себе процесс не будет создавать нагрузки (в чем сильно сомневаюсь, ведь это всего-лишь навсего единственный процесс использующий несколько килобайт памяти) Теоретически может, а на практике, Вы же не знаете условий и лимитов сервера на котором он запускается, мы так поняли. На одном из хостеров, нам было интересно узнать, что процессы выполняющиеся дольше 10 минут прибиваются безоговорочно, из-за чего имели проблему с попыткой сдампить большую базу.И по поводу нескольких килобайт памяти - не факт. Померяйте для интереса. memory_get_usage set_time_limit в цикле, потому что (IMG:style_emoticons/default/smile.gif) А зачем? Поставьте в начале на 0 и всё. Скрипт будет выполняться до завершения.Что бы было не более десяти запросов к БД в секунду (IMG:style_emoticons/default/smile.gif) А лимиты могут быть разные(IMG:style_emoticons/default/smile.gif)P.S. Внесенные мной дополнения никак не влияют на Ваше мнение относительно такого способа? |
jtj |
09.08.2006, 01:09
Сообщение
#12
|
Группа: Старые пользователи Сообщений: 22 Регистрация: 20.01.2006 Пользователь №: 2,054 Репутация: 211 |
А зачем? Поставьте в начале на 0 и всё. Скрипт будет выполняться до завершения. Век живи - век учисль, спасибо (IMG:style_emoticons/default/smile.gif) |
Текстовая версия | Сейчас: 06.06.2024, 23:03 |