2016-06-25 2 views
0

Я работаю в проекте, где требуется загрузить большой файл с удаленного сервера, который содержит данные, разделенные на трубы, около 5 миллионов записей.Нужно обходное решение против PHP 7.0.3 undefined function mysqli :: set_local_infile_handler()

После завершения загрузки необходимо загрузить данные в базу данных. В настоящее время я работаю с базой данных MySQL, поэтому я не рассматривал другие варианты.

Я использую объект ориентированный MySQLi и я вызываю запрос LOAD DATA LOCAL INFILE. Это займет от полутора часов до двух часов, и мне нужен способ показать прогресс, и единственным вариантом, который я нашел, был метод set_local_infile_handler. Кажется, этот метод позволяет программисту изменить форматирование данных, прежде чем подавать его на запрос, но, будучи единственным вариантом, который я нашел, я хочу использовать его для достижения цели.

Однако все это я получаю:

PHP Fatal error: Uncaught Error: Call to undefined method mysqli::set_local_infile_handler() in C:\Repositories\project\tools\loaddata.php:65 

Я прошу:

  • починки, чтобы позволить mysqli::set_local_infile_handler в моем коде
  • Альтернативный с MySQLi показать прогресс, а данные загружается
  • Другие альтернативы с использованием PHP

Я попробовал несколько идей (только с 100 тысяч записей):

  • Измененный от new mysqli() к mysqli_init() и mysqli::real_connect(), но единственная причина в том, что все примеры с mysqli::set_local_infile_handler использовать последнюю форму.
  • Я раскопал mysqli.allow_local_infile = On в файле php.ini, однако до этого я не имел проблем с запуском кода с локальным запросом infile, но я ожидал, что это может сделать видимый метод беспокойством. Я остановился и начал сервер, кстати.
  • Я назвал второй запрос

    SHOW STATUS WHERE Variable_name in ('bytes_received','bytes_sent','innodb_buffer_pool_pages_data','innodb_buffer_pool_bytes_data','innodb_buffer_pool_pages_flushed','innodb_buffer_pool_read_requests','innodb_buffer_pool_reads','innodb_buffer_pool_write_requests','innodb_data_read','innodb_data_reads','innodb_data_writes','innodb_data_written','innodb_rows_inserted').

    Я сохранил все эти переменные, потому что я не заботился о каждом конкретном значении, я заметил, что только эти переменные менялись значимо, но, похоже, последнего может быть достаточно. Однако я делаю это с отдельными файлами, но пока не знаю, могу ли я иметь два соединения, работающие в одном и том же коде, и два потока, чтобы иметь самую медленную работу в фоновом режиме. На самом деле я просматривал php mysqli callback function, когда нашел mysqli::set_local_infile_handler ссылку.

Я был с этим весь день, так что я должен был попробовал некоторые другие вещи, но как я не помню их, или они не имеют никакого отношения на данный момент. Спасибо заранее.

Я не вижу ошибок в коде, он работает без локальных методов входной_файл, но здесь это:

require_once("connectvars.php"); 
$filepath = $_SERVER["argv"][1]; 
$bloqIdx=0; 

$conn = mysqli_init(); 
$conn->real_connect($mysvr,$myusr,$mypwd,$mydb); 
// $conn = new mysqli($mysvr,$myusr,$mypwd,$mydb); 
if ($conn->connect_error) { 
    trigger_error("SQL".$conn->connect_error,E_USER_ERROR); 
    die("Connect Error"); 
} 

function countData($stream, &$buffer, $buflen, &$errmsg) { 
    global $bloqIdx; 
    $len = strlen($buffer); 
    if ($bloqIdx%1000==0) echo "."; 
    return $len; 
} 
function getRowsInserted() { 
    global $conn; 
    $result = $conn->query("show status where Variable_name='innodb_rows_inserted')"; 
    $rowsInserted=0; 
    if ($result && $conn->affected_rows>0 && $row = $result->fetch_assoc()) $rowsInserted=$row["Value"]; 
    $result->close(); 
    return $rowsInserted; 
} 

$conn->query("truncate mytable"); 

$riStart = getRowsInserted(); 
$start = time(); 
$query = "LOAD DATA LOCAL INFILE '$filepath' INTO TABLE mytable FIELDS TERMINATED by '|' LINES TERMINATED BY '\n'"; 
$conn->set_local_infile_handler("countData"); 
$conn->query($query); 
$conn->set_local_infile_default(); 
$minutes = abs(time() - $start)/60; 
$riEnd = getRowsInserted()-$riStart; 
echo "Finished Loading $riEnd rows from $filepath for ".round($minutes,2)." minutes\n"; 

$conn->close(); 

По закомментировав:

$conn->set_local_infile_handler("countData"); 

и

$conn->set_local_infile_default(); 

-код работает, но информация о прогрессе отсутствует.

В http://php.net/manual/en/mysqli.set-local-infile-handler.php допустимых версиях по-видимому, (PHP 5, PHP 7)

Если вам действительно нужно проверить код текстового файла в качестве аргумента может быть что-то вроде:

1|one|alpha|C|2012-10-21 17:44:18 
2|two|beta|C|2013-02-05 12:23:57 
3|three|gamma|C|2012-12-10 07:18:09 
4|four|delta|X|2012-11-27 11:51:32 
5|five|phi|C|2013-01-07 14:03:29 

и таблицы сценария:

create table `mytable` (
    `id` INT NOT NULL, 
    `num` CHAR(10) NULL, 
    `code` CHAR(13) NULL, 
    `status` CHAR(1) NULL, 
    `registered` DATETIME NULL, 
    INDEX `mycode` (`code` ASC, `registered` ASC), 
    PRIMARY KEY (`id`)) 
ENGINE = InnoDB 
DEFAULT CHARACTER SET = utf8 
COLLATE = utf8_bin; 
+0

Опубликовать код, вызывающий ошибку. Возможно, вы называете это неправильным. – ShiraNai7

+0

Вы можете разветвить дочерний процесс, чтобы выполнить загрузку и импорт файла, затем введите бесконечный цикл (каждый со сном() и некоторым условием для выхода с ошибкой или завершением) ... затем проверьте innodb_rows_inserted (as вы упомянули об изменениях). Викинг: http://php.net/manual/en/function.pcntl-fork.php –

+0

Хорошо, добавлен код. Я изменил некоторые вещи, связанные с частными данными. –

ответ

0

Вы могли просто разделить их в 2-х различных процессов.

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

Второй процесс - это gui. Он предоставляет пользователю кнопку, которая создает триггер (файл tmp), а затем периодически проверяет наличие файла загрузки. После того как файл загрузки будет переименован в заданный формат первым процессом, он может проанализировать имя файла для общего количества строк, а затем периодически запрашивать базу данных, чтобы определить существующее количество строк и представить ее пользователю в виде процента.

+0

Если вы хотите что-то более отзывчивое, чем задание cron, вы можете использовать inotify-tools (linux) для просмотра событий файловой системы. Таким образом, вы можете активно следить за созданием файла триггера вместо ожидания следующей итерации cron. –

+0

Я думал, что-то подобное, за исключением идеи триггера, кажется грязным, но функциональным. Кстати, я работаю над Windows, к сожалению. Вместо использования WAMP я устанавливал php, apache и mysql отдельно, у меня нет опыта работы с IIS и мало и ржавым опытом работы с sqlserver. Итак, на данный момент планировщик заданий - это мой эквивалент cron, который я использую для запуска пакетного файла, а между тем я использую второй CMD-терминал для проверки прогресса. Я попробую это решение и проверю, работает ли оно на моем сервисе. –

+0

Решение, связанное с требованием, которое я упомянул в тот момент, заключалось в использовании двух разных процессов, поэтому я отметил это как принятый ответ. На данный момент это требование устарело, поставщик большого файла также позволяет общаться через веб-сервис, и это стало актуальным решением. –