Заполнение базы данных MySQL из базы данных DB2

В настоящее время у меня есть сценарий для заполнения базы данных MySQL с сервера DB2. Это работает, но кажется, что строки вставляются в MySQL очень медленно. Процесс сервера выполняется на ~ 1% ЦП, пока работает скрипт, и мне интересно, как я могу ускорить вставки.

Из соображений безопасности администратор базы данных DB2 предоставил нам доступ только для чтения к необходимым таблицам базы данных.

Это мой сценарий:

<?php

$selectQuery = "SELECT 
                    PK AS COL1,
                    COL2,
                    COL3,
                    COL4,
                    CASE WHEN DATE > '" . date('Y-m-d') . "'
                      THEN 1
                      ELSE 0
                      END AS COL5
                FROM table1";

$insertQuery = "INSERT INTO `table1` (
                    `fk`,
                    `col2`,
                    `col3`,
                    `col4`,
                    `col5`,
                    `last_updated`
                )
                SELECT :col1, f.`fid`, :col3, :col4, :col5, NOW()
                    FROM f
                    WHERE f.`code` = :col2
                    LIMIT 1
                ON DUPLICATE KEY UPDATE
                    `col2` = VALUES(col2),
                    `col3` = VALUES(col3),
                    `col4` = VALUES(col4),
                    `col5` = VALUES(col5),
                    `last_updated` = NOW();";

$paramTypes = array(
    'col1' => PDO::PARAM_STR,
    'col2' => PDO::PARAM_STR,
    'col3' => PDO::PARAM_STR,
    'col4' => PDO::PARAM_STR,
    'col5' => PDO::PARAM_BOOL
);

$sync->populate($selectQuery, $insertQuery, $paramTypes);

В классе синхронизации (класс, экземпляром которого является $sync):

<?php

class SyncObject {
    private $db2;
    private $db2_user = '...';
    private $db2_pass = '...';
    private $db2_dbname = '...';
    private $db2_host = 'secure.example.net';
    private $db2_port = ...;

    private $mysql;

    public function __construct() {
        // Establish a DB2 connection
        $this->db2 = db2_pconnect("DATABASE={$this->db2_dbname};HOSTNAME={$this->db2_host};PORT={$this->db2_port};PROTOCOL=TCPIP;UID={$this->db2_user};PWD={$this->db2_pass};", '', '');

        // Establish a MySQL connection
        $this->mysql = new PDO('mysql:host=secure-mysql.example.net;port=...;dbname=...', '...', '...', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}

    public function populate($selectQuery, $insertQuery, $paramTypes = array()) {

        $insStmt = $this->mysql->prepare($insertQuery);

        foreach ($paramTypes as $parameterName => $parameterType) {

            $$parameterName = '';

            $insStmt->bindParam(":$parameterName", $$parameterName, $parameterType);
        }

        // Retrieve the data

        $stmt = db2_exec($this->db2, $selectQuery);

        while ($row = db2_fetch_assoc($stmt)) {
            foreach ($row as $fieldName => &$fieldValue) {

                $fieldName = strtolower($fieldName);

                $$fieldName = trim($fieldValue);

                $insStmt->execute();
            }
        }
    }
}

Кстати, этот метод populate вызывается шесть раз, по одному разу для каждой таблицы. Я показал здесь только одну таблицу. Размер таблиц варьируется от 20 строк до 21 миллиона строк.

Я думаю, что могу связать параметры в верхнем регистре в запросе, чтобы избежать функции strtolower в foreach, но помимо этого незначительного изменения есть ли какие-либо предложения по улучшению производительности скрипта?


person rink.attendant.6    schedule 22.05.2013    source источник


Ответы (3)


Вставка данных по строкам не будет работать хорошо, независимо от того, что вы делаете. На мой взгляд, лучшим подходом было бы использование команды DB2 EXPORT для извлечения данных таблицы DB2 в файлы CSV, а затем использование MySQL LOAD DATA для загрузки их в целевую базу данных. Я не очень хорошо знаком с PHP, но я думаю, что он должен позволить вам запускать внешние команды с помощью exec().

Вам потребуется как минимум установить клиент времени выполнения DB2 Data Server, чтобы иметь возможность запускать процессор командной строки DB2 для EXPORT.

person mustaccio    schedule 22.05.2013
comment
В настоящее время это не вариант для нас, поскольку у нас очень мало контроля над сервером, однако я учту это предложение, когда мы перейдем на более новый сервер. Спасибо! - person rink.attendant.6; 23.05.2013

попробуйте экспортировать все данные в формат файла csv, а затем используйте утилиту загрузки данных, чтобы загрузить их в базу данных MySQL. Утилиты загрузки и экспорта работают быстрее, чем выборка одной строки и ее вставка одна за другой.

person Aman Singh    schedule 22.05.2013

После краткого изучения того, как InnoDB выполняет операции, я сделал следующее, чтобы ускорить вставки:

  • Используйте транзакции (т. е. отключите автофиксацию): $this->mysql->beginTransaction(). Количество запросов на транзакцию было ограничено, хотя я почти уверен, что MySQL все равно зафиксирует, когда буфер InnoDB заполнится.
  • Отключить проверку внешнего ключа: SET foreign_key_checks = 0. База данных DB2 имела довольно высокую целостность, так что это была безопасная операция.
  • Отключить проверку уникальных ключей: SET unique_checks = 0. В базе данных DB2 уже применялись уникальные ключи, поэтому это было безопасно.
  • Включить незафиксированные операции чтения: SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Кроме того, следует учитывать системные переменные InnoDB, но их нельзя изменить с ограниченным доступом к серверу.

Эта страница также может помочь, хотя на ней перечислено большинство вещей, перечисленных здесь: http://dev.mysql.com/doc/refman/5.6/en/optimizing-innodb-bulk-data-loading.html.

person rink.attendant.6    schedule 23.05.2013