Перенос данных в оптимизированные таблицы MySQL из «сырых» таблиц в cron с помощью php - PullRequest
2 голосов
/ 20 апреля 2011

Я работаю с MLS провайдером недвижимости (RETS).Каждые 48 часов мы будем извлекать данные с их сервера в задании cron в базу данных SQL.Мне поручено написать скрипт php, который будет запускаться после сброса данных с удаленного сервера в наши «сырые» таблицы.В этих необработанных таблицах все столбцы VARCHAR(255), и мы хотим переместить данные в оптимизированные таблицы.Прежде чем отправить свой сценарий парню, отвечающему за настройку работы cron, я подумал, есть ли более эффективный способ сделать это, чтобы я не выглядел глупо.

Вот что я делаю:

Всего 8 таблиц, 4 необработанных и 4 оптимизированных - все в одной базе данных.Имена необработанных столбцов таблицы не являются описательными, как, например, c1, c2, c2, c4 и т. Д. Это сделано намеренно, поскольку данные, поступающие в каждый столбец, могут измениться.Имена столбцов необработанной таблицы сопоставляются с правильными оптимизированными столбцами таблицы с помощью php, что-то вроде этого:

$tables['optimized_table_name1']['raw_table'] = 'raw_table_name1';
$tables['optimized_table_name1']['data_map'] = array(
    'c1'    =>  array( // <--- "c1" is the raw table column name
                    'column_name'   =>  'id',
                    // I use other values for table creation,
                    // but they don't matter to the question.
                    // Just explaining why the array looks like this
                    //'type'        =>  'VARCHAR',
                    //'max_length'  =>  45,
                    //'primary_key' =>  FALSE,
                    // etc.
                ),
    'c9'    =>  array('column_name' =>  'address'),
    'c25'   =>  array('column_name' =>  'baths'),
    'c2'    =>  array('column_name' =>  'bedrooms') //etc.
    );

Я делаю то же самое для каждой из 4 таблиц: SELECT * FROM необработанная таблица,прочитайте массив конфигурации и создайте огромный оператор вставки SQL, TRUNCATE оптимизированную таблицу, затем выполните запрос INSERT.

foreach ($tables as $table_name => $config):

$raw_table  =   $config['raw_table'];
$data_map   =   $config['data_map'];
$fields =   array();
$values =   array();
$count      =   0;

// Get the raw data and create an array mapped to the optimized table columns.
$query = mysql_query("SELECT * FROM dbname.{$raw_table}");

while ($row = mysql_fetch_assoc($query))
{
    // Reading column names from my config file on first pass
    // Setting up the array, will only run once per table
    if (empty($fields))
    {
        foreach ($row as $key => $val)
        {// Produces an array with the column names
            $fields[] = $data_map[$key]['column_name'];
        }
    }
    foreach ($row as $key => $val)
    {// Assigns data to an array to be imploded later
        $values[$count][] = $val;
    }
    $count++;
}

// Create the INSERT statement string
$insert = array();
$sql = "\nINSERT INTO `{$table_name}` (`".implode('`,`', $fields)."`) VALUES\n";
foreach ($values as $key => $vals)
{
    foreach ($vals as &$val)
    {
        // Escape the data
        $val = mysql_real_escape_string($val);
    }
    // Using implode for simplicity, could avoid the nested foreach if I wanted to
    $insert[] = "('".implode("','", $vals)."')";
}
$sql .= implode(",\n", $insert).";\n";

// TRUNCATE optimized table and run INSERT query here

endforeach;

, который производит что-то вроде этого (только больше - максимум около 15 000 записей вtable, и одно оператор вставки на таблицу):

INSERT INTO `optimized_table_name1` (`id`,`beds`,`baths`,`town`) VALUES
('50300584','2','1','Fairfield'),
('87560584','3','2','New Haven'),
('76545584','2','1','Bristol');

Теперь я должен признать, что я был под крылом ORM в течение долгого времени и не в курсе моего ванильного mysql / php.Это довольно простая задача, и я хочу сделать код простым.

Мои вопросы:

  1. Является ли метод TRUNCATE / INSERT хорошим способом сделатьэто?
  2. Есть ли что-то в моем коде, что вы видите в качестве проблемы?Я знаю, что вы видите вложенные циклы foreach и просто содрогаетесь, но я хочу, чтобы код был как можно более чистым и избегал большого количества беспорядочной конкатенации строк (чтобы создать запрос на вставку).Как я уже сказал, я также давно не использовал нативные функции php для SQL.
  3. Мне кажется, что действительно не имеет значения, не оптимизирован ли код, если он запускается в 3 часа ночи каждые 2 дня, Имеет ли значение ?Будет ли этот код преформироваться? ОК?
  4. Есть ли лучшая общая стратегия для выполнения этой задачи?
  5. Нужно ли использовать транзакции?
  6. Как узнатьошибок, которые могут возникнуть в скриптах cron?

Извиняюсь, если я не использую правильный жаргон cron, для меня это ново.

1 Ответ

1 голос
/ 20 апреля 2011

Будьте проще.ORM отлично подходит для этой задачи.

Ответы:

  1. Да.
  2. Ваш код доступен для чтения.По крайней мере, у меня не было проблем с его чтением.
  3. У нас был сценарий, который запускался рано утром.Он не был оптимизирован и потреблял много памяти.Спустя ЧЕТЫРЕ года он начал потреблять более 512 Мб.Я потратил 2 часа на его оптимизацию, поэтому теперь он потребляет 7 Мб (довольно хорошая оптимизация, да? :)).Я лично считаю, что это нормально, что ваш сценарий не оптимизирован.Если этот скрипт начнет сбой, вы поймете, в чем проблема.Может быть, это исчерпает память, возможно, ваши SQL-запросы вызовут взаимоблокировки ... возможно, вы позже оптимизируете его для чтения с подчиненных серверов ... Я не знаю, но теперь он работает нормально, ничего страшного.
  4. Я бы сделал что-то похожее на ваш код.Но я бы, вероятно, сначала сгенерировал файл и загрузил данные на сервер, выполнив команду оболочки mysql -u username --password=password < import_file.sql.Таким образом, мой файл хранится где-то на диске, поэтому я всегда могу посмотреть на него.И, возможно, даже редактировать для одноразовой коррекции нагрузки.Но вы все равно можете сделать это, записав свое заявление sql в файл.
  5. Нет.Это всего лишь один запрос.Если вы используете движок InnoDB, это уже транзакция.
  6. Сначала используйте error_reporting (E_ALL & ~ E_NOTICE).Во-вторых, используйте функцию mysql_error PHP, чтобы ваш запрос выполнялся правильно.В-третьих, в вашем выходе ошибок cronjob перетекут в какой-то файл, например так: 0 7 * * 0 /path/to/php -c /path/to/php.ini /path/to/script.php 2> /tmp/errors_file И, таким образом, вы можете создать SECOND script runnin после первого, чтобы уведомлять об ошибках в script.php по электронной почте или ... любым способом уведомления, который вы предпочитаете.Я бы предпочел register_shutdown_functions , который бы проверял на error_file, и если он не пуст, уведомил бы вас и удалил его потом.

Просто мое мнение, но я надеюсь, что моеОтвет помогает, хотя.

...