Хостинг - Обзор: эпицентр русскоязычного хостинга

Здравствуйте, гость ( Вход | Регистрация )

> Вопрос к знатокам 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?
Посоветуйте, пожалуйста, как быть, а то всю ночь бьюсь...

Заранее огромное спасибо всем откликнувшимся.




User is offlineProfile CardPM
Go to the top of the page
+Quote Post
 
Reply to this topicStart new topic
Ответов(1 - 11)
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)
Цитата(jtj @ 07.08.2006, 10:50) *
Всем привет.
У меня такая задачка (IMG:style_emoticons/default/smile.gif)
Есть MySQL-таблица, в которой 1000 записей (строк), а возможно и более.
В каждой строке, есть несколько полей и одно из этих полей мне нужно изменить в каждой из 1000 строк.
Новое значение каждого поля уникально и зависит от внешних факторов.
Изменения делаются из php-скрипта.

Вопрос - как из php-скрипты изменить все 1000 строк, таким образом что бы не превысить каких-либо лимитов (например, на количество запросов к БД) и не положить сервер перегрузкой?

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

Так понимаю, не делать же 1000 раз update?
Посоветуйте, пожалуйста, как быть, а то всю ночь бьюсь...

Заранее огромное спасибо всем откликнувшимся.


User is offlineProfile CardPM
Go to the top of the page
+Quote Post
eSupport.org.ua
сообщение 07.08.2006, 17:42
Сообщение #3


Одесский сисадмин


Группа: Старые пользователи
Сообщений: 5,200
Регистрация: 18.11.2004
Из: Одесса
Пользователь №: 823


Репутация: 263


В биллингах идет так:
На каждый учетный период в отведенной под это дело таблице накапливается история операций.
Затем когда происходит переход - данные из таблицы консолидируются select'ом и переходят в итоговую таблицу.

А то что написано выше - это не биллинг а непонятно что...
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
edogs
сообщение 07.08.2006, 18:05
Сообщение #4


php(zce)/mysql


Группа: Старые пользователи
Сообщений: 3,576
Регистрация: 15.08.2003
Из: Санкт-Петербург
Пользователь №: 249


Репутация: 246


Цитата(eSupport.org.ua @ 07.08.2006, 17:42) *
В биллингах идет так:
На каждый учетный период в отведенной под это дело таблице накапливается история операций.
Затем когда происходит переход - данные из таблицы консолидируются select'ом и переходят в итоговую таблицу.

А то что написано выше - это не биллинг а непонятно что...


Да кстати, прочитали сначала что надо сделать, а вот для чего - не обратили внимания.

Вот это в частности

Цитата


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


Можно сделать проще, посмотрите математические функции для операций с БД. Можно апдейтнуть всё за 1 запрос, вычисляя сумму вычета на базе данных о тарифе и используемых услугах.
P.S.: А историю операций вести надо по любому, esupport полностью прав.

User is offlineProfile CardPM
Go to the top of the page
+Quote Post
kuvalda
сообщение 07.08.2006, 18:46
Сообщение #5





Группа: Старые пользователи
Сообщений: 22
Регистрация: 14.07.2006
Пользователь №: 3,192


Репутация: 208


Цитата(jtj @ 07.08.2006, 10:50) *

Всем привет.
У меня такая задачка (IMG:style_emoticons/default/smile.gif)
Есть MySQL-таблица, в которой 1000 записей (строк), а возможно и более.
В каждой строке, есть несколько полей и одно из этих полей мне нужно изменить в каждой из 1000 строк.


Код

MYSQL_QUERY("Update BaseName set StolbetsKotoriiMeniaem=NovoeZnachenie");


Сообщение отредактировал kuvalda - 07.08.2006, 18:48
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
jtj
сообщение 07.08.2006, 19:50
Сообщение #6





Группа: Старые пользователи
Сообщений: 22
Регистрация: 20.01.2006
Пользователь №: 2,054


Репутация: 211


Большое спасибо ответившим (IMG:style_emoticons/default/smile.gif)

Цитата(edogs @ 07.08.2006, 17:49) *

Раз каждое изменение индивидуально, то значит Вам всё равно придется делать 1 апдейт на 1 строку.


Вот это я и хотел узнать, что не существует варианта с единственным запросом, если каждое изменнение уникально и зависит от внешних факторов (IMG:style_emoticons/default/unsure.gif)

Цитата(edogs @ 07.08.2006, 17:49) *

Значит задача сводится к тому, что бы сделать это в несколько приемов.

Лично мы делали бы примерно так, если надо за раз не запускать 1000 запросов.
Что бы не апдейтить одно и то же по нескольку раз
1) В каждое поле добавить дату последнего изменения. Её апдейтить при изменении поля. Скрипт выбирает допустим 10 полей которые не обновлялись последними. Апдейтит их нужным образом.
Или
2) Завести еще одну таблицу (или файл) где хранить уникальный ID последней апдейтеной строки. И выбирать 10 следующих после нее. Записывать ID конечно после апдейта.
Запускать скрипт можно
а) По крону (например простым GET/wget).
б) Выводить после завершения простую хтмл страницу с meta refresh=Х секунд на самого себя.
Неплохой идеей может являться лок таблицы на время апдейта, если апдейт конечно не 30 минут занимает. Будет работать быстрее и безопаснее.

Спасибо за советы, второй обязательно применю, если не перепишу базу таким образом, что бы не было внешних факторов.
Цитата(edogs @ 07.08.2006, 17:49) *

А вообще, Вы уверены что не хотите переписать эту задачу на файлы? Файл считали, что надо проапдейтили, и назад записали. Хотя работать с результатом конечно труднее будет, особенно на сложных операциях. но Вы же не уточнили что делаете с данными кроме апдейта(IMG:style_emoticons/default/smile.gif)

Очень хотелось вынести это поле из базы в тестовый файл (IMG:style_emoticons/default/smile.gif)
Но помню очень бурный спор (не с Вами, но здесь, на форуме), где обсуждалось быстродействие MySQL против txt при одновременной работе с большим кол-вом данных...
Цитата(eSupport.org.ua @ 07.08.2006, 18:42) *

В биллингах идет так:
На каждый учетный период в отведенной под это дело таблице накапливается история операций.
Затем когда происходит переход - данные из таблицы консолидируются select'ом и переходят в итоговую таблицу.

Сложно к пониманию, но информация к размышлению, спасибо (IMG:style_emoticons/default/smile.gif)
Цитата(eSupport.org.ua @ 07.08.2006, 18:42) *

А то что написано выше - это не биллинг а непонятно что...

То что написано выше - абстрактное рассуждение по поводу абстрактной проблемы (IMG:style_emoticons/default/smile.gif)

Цитата(edogs @ 07.08.2006, 19:05) *

Можно сделать проще, посмотрите математические функции для операций с БД. Можно апдейтнуть всё за 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
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
edogs
сообщение 07.08.2006, 20:58
Сообщение #7


php(zce)/mysql


Группа: Старые пользователи
Сообщений: 3,576
Регистрация: 15.08.2003
Из: Санкт-Петербург
Пользователь №: 249


Репутация: 246


Цитата(jtj @ 07.08.2006, 19:50) *
Очень хотелось вынести это поле из базы в тестовый файл (IMG:style_emoticons/default/smile.gif)
Но помню очень бурный спор (не с Вами, но здесь, на форуме), где обсуждалось быстродействие MySQL против txt при одновременной работе с большим кол-вом данных...
Холиварная тема, но смысл в целом сводится к тому, что если идут операции выборки, фильровки, сортировки, то лучше не мучаться и сделать на базе. Если же идут операции - взял всю информацию - обработал - положил обратно, то файл в целом рулит. Таблица в БД это на самом деле, такой же физический файл со всеми вытекающими.
Цитата(jtj @ 07.08.2006, 19:50) *

Мешает то, что есть еще дополнительные факторы, влияющие на сумму снимаемую с ballans каждой конкретной строки - это как personal_discount (персональная скидка), так и addons (например "дополнительные услуги") определяющие итоговую сумму снимаемую с ballans
Можно I) использовать вложенные запросы II) временные таблицы.

User is offlineProfile CardPM
Go to the top of the page
+Quote Post
jtj
сообщение 08.08.2006, 08:23
Сообщение #8





Группа: Старые пользователи
Сообщений: 22
Регистрация: 20.01.2006
Пользователь №: 2,054


Репутация: 211


Цитата(edogs @ 07.08.2006, 21:58) *

Холиварная тема, но смысл в целом сводится к тому, что если идут операции выборки, фильровки, сортировки, то лучше не мучаться и сделать на базе. Если же идут операции - взял всю информацию - обработал - положил обратно, то файл в целом рулит. Таблица в БД это на самом деле, такой же физический файл со всеми вытекающими.


Хехе, года не прошло с того момента, как я спорил на форуме, доказывая что файловую БД местами использовать не менее удобно, чем 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)
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
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)
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
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)
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
edogs
сообщение 08.08.2006, 18:25
Сообщение #11


php(zce)/mysql


Группа: Старые пользователи
Сообщений: 3,576
Регистрация: 15.08.2003
Из: Санкт-Петербург
Пользователь №: 249


Репутация: 246


Цитата(jtj @ 08.08.2006, 13:25) *
1. Скрипт изначально запускается по крону (поэтому в принципе, скрипт может висеть хоть час, если сам по себе процесс не будет создавать нагрузки (в чем сильно сомневаюсь, ведь это всего-лишь навсего единственный процесс использующий несколько килобайт памяти)
Теоретически может, а на практике, Вы же не знаете условий и лимитов сервера на котором он запускается, мы так поняли. На одном из хостеров, нам было интересно узнать, что процессы выполняющиеся дольше 10 минут прибиваются безоговорочно, из-за чего имели проблему с попыткой сдампить большую базу.
И по поводу нескольких килобайт памяти - не факт. Померяйте для интереса. memory_get_usage
Цитата(jtj @ 08.08.2006, 13:25) *
set_time_limit в цикле, потому что
(IMG:style_emoticons/default/smile.gif) А зачем? Поставьте в начале на 0 и всё. Скрипт будет выполняться до завершения.
Цитата(jtj @ 08.08.2006, 13:25) *
Что бы было не более десяти запросов к БД в секунду (IMG:style_emoticons/default/smile.gif)
А лимиты могут быть разные(IMG:style_emoticons/default/smile.gif)
Цитата(jtj @ 08.08.2006, 13:25) *

P.S. Внесенные мной дополнения никак не влияют на Ваше мнение относительно такого способа?
По сути нет. Ключевой момент тут в том, что мы не видим смысла находить способы искуственно затягивать выполнение скрипта и размазывать запросы в базу по времени, если есть вариант просто вызывать скрипт выполняющий малую работу почаще. Если бы была необходимость выполнить эту процедуру за 1 запуск скрипта, мы бы подумали над Вашим решением, но раз такой необходимости нет, то имхо надо упрощать.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
jtj
сообщение 09.08.2006, 01:09
Сообщение #12





Группа: Старые пользователи
Сообщений: 22
Регистрация: 20.01.2006
Пользователь №: 2,054


Репутация: 211


Цитата(edogs @ 08.08.2006, 19:25) *

А зачем? Поставьте в начале на 0 и всё. Скрипт будет выполняться до завершения.

Век живи - век учисль, спасибо (IMG:style_emoticons/default/smile.gif)
User is offlineProfile CardPM
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 



- Текстовая версия Сейчас: 06.06.2024, 23:03
Яндекс.Метрика